Skip to content

Commit

Permalink
feat: allow overriding the mobile "⊕" button #143
Browse files Browse the repository at this point in the history
  • Loading branch information
darlal committed May 4, 2024
1 parent 82fd6e5 commit a1990c2
Show file tree
Hide file tree
Showing 13 changed files with 564 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ In the demo below, `Quick Switcher++: Open Symbols for the active editor` global
| Setting | Description | Default |
|---------------------------------|-------------|---------|
| Show ribbon icons | List of modes that should be accessible from the ribbon menu. | `HeadingsList`<br />`SymbolList` |
| Override default Switcher launch button (the "⊕" button) on mobile platforms | Override the "⊕" button (in the Navigation Bar) on mobile platforms to launch Switcher++ instead of the default system switcher. Select the Mode to launch Switcher++ in, or select "Do not override" to disable the feature. | `Do not override` |
| Preferred suggestion title source | The preferred source to use for the primary suggestion text that will be searched and displayed for file based suggestions. | `First H₁ heading` |
| Preferred file path display format | The preferred way to display file paths in suggestions. Note: by default, path information will not be displayed for files at the root of the vault. | `Parent folder & filename` |
| Hide path for root items | **Enabled**: path information will be hidden for items at the root of the vault. | enabled |
Expand Down
28 changes: 27 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Plugin } from 'obsidian';
import { SwitcherPlusSettings, SwitcherPlusSettingTab } from 'src/settings';
import { createSwitcherPlus } from 'src/switcherPlus';
import { createSwitcherPlus, MobileLauncher } from 'src/switcherPlus';
import { Mode, SessionOpts } from 'src/types';

type CommandDefinitionOpts = Pick<SessionOpts, 'useActiveEditorAsSource'>;
Expand Down Expand Up @@ -107,12 +107,17 @@ export default class SwitcherPlusPlugin extends Plugin {

this.addSettingTab(new SwitcherPlusSettingTab(this.app, this, options));
this.registerRibbonCommandIcons();
this.updateMobileLauncherButtonOverride(options.mobileLauncher.isEnabled);

COMMAND_DATA.forEach(({ id, name, mode, iconId, sessionOpts }) => {
this.registerCommand(id, name, mode, iconId, sessionOpts);
});
}

onunload(): void {
this.updateMobileLauncherButtonOverride(false);
}

registerCommand(
id: string,
name: string,
Expand Down Expand Up @@ -175,4 +180,25 @@ export default class SwitcherPlusPlugin extends Plugin {

return true;
}

updateMobileLauncherButtonOverride(isEnabled: boolean): void {
if (isEnabled) {
const onclickListener = () => {
const modeString = this.options.mobileLauncher.modeString as keyof typeof Mode;
const openMode = Mode[modeString];

if (openMode) {
this.createModalAndOpen(openMode, false);
}
};

MobileLauncher.installMobileLauncherOverride(
this.app,
this.options.mobileLauncher,
onclickListener,
);
} else {
MobileLauncher.removeMobileLauncherOverride();
}
}
}
86 changes: 69 additions & 17 deletions src/settings/__tests__/generalSettingsTabSection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,6 @@ describe('generalSettingsTabSection', () => {
);
});

it('should show path settings', () => {
const showPathDisplayFormatSpy = jest
.spyOn(sut, 'showPathDisplayFormat')
.mockReturnValueOnce();

sut.display(mockContainerEl);

expect(showPathDisplayFormatSpy).toHaveBeenCalled();

showPathDisplayFormatSpy.mockRestore();
});

it('should show the hidePathIfRoot setting', () => {
sut.display(mockContainerEl);

Expand Down Expand Up @@ -154,26 +142,30 @@ describe('generalSettingsTabSection', () => {
expect(addDropdownSettingSpy).toHaveBeenCalled();

showPathDisplayFormatSpy.mockRestore();
showPathDisplayFormatSpy.mockRestore();
addDropdownSettingSpy.mockRestore();
});

it('should save modified setting', () => {
config.pathDisplayFormat = PathDisplayFormat.None;
const finalValue = PathDisplayFormat.Full;

let onChangeFn: (v: string, c: SwitcherPlusSettings) => void;
type addDropdownSettingArgs = Parameters<SettingsTabSection['addDropdownSetting']>;
let dropdownSettingOnChangeFn: addDropdownSettingArgs[6];

// let onChangeFn: (v: string, c: SwitcherPlusSettings) => void;
const addDropdownSettingSpy = jest
.spyOn(SettingsTabSection.prototype, 'addDropdownSetting')
.mockImplementationOnce((_c, _n, _d, _i, _o, _k, onChange) => {
onChangeFn = onChange;
.mockImplementationOnce((...args) => {
dropdownSettingOnChangeFn = args[6];
return mock<Setting>();
});

const configSaveSpy = jest.spyOn(config, 'save');

sut.showPathDisplayFormat(mockContainerEl, config);

// trigger the save
onChangeFn(finalValue.toString(), config);
dropdownSettingOnChangeFn(finalValue.toString(), config);

expect(config.pathDisplayFormat).toBe(finalValue);
expect(configSaveSpy).toHaveBeenCalled();
Expand Down Expand Up @@ -273,6 +265,66 @@ describe('generalSettingsTabSection', () => {
});
});

describe('showOverrideMobileLauncher', () => {
it('should show setting to override the mobile launcher (plus) button', () => {
const initialValue = Mode[Mode.CommandList];
config.mobileLauncher.modeString = initialValue;
config.mobileLauncher.isEnabled = true;

const addDropdownSettingSpy = jest.spyOn(
SettingsTabSection.prototype,
'addDropdownSetting',
);

const showOverrideMobileLauncherSpy = jest.spyOn(sut, 'showOverrideMobileLauncher');

sut.display(mockContainerEl);

expect(showOverrideMobileLauncherSpy).toHaveBeenCalledWith(mockContainerEl, config);
expect(addDropdownSettingSpy).toHaveBeenCalledWith(
mockContainerEl,
expect.any(String),
expect.any(String),
initialValue,
expect.anything(),
null,
expect.any(Function),
);

showOverrideMobileLauncherSpy.mockRestore();
addDropdownSettingSpy.mockRestore();
});

it('should save modified setting', () => {
const finalValue = Mode[Mode.Standard];
config.mobileLauncher.modeString = Mode[Mode.CommandList];
config.mobileLauncher.isEnabled = true;

type addDropdownSettingArgs = Parameters<SettingsTabSection['addDropdownSetting']>;
let dropdownSettingOnChangeFn: addDropdownSettingArgs[6];

const addDropdownSettingSpy = jest
.spyOn(SettingsTabSection.prototype, 'addDropdownSetting')
.mockImplementationOnce((...args) => {
dropdownSettingOnChangeFn = args[6];
return mock<Setting>();
});

const configSaveSpy = jest.spyOn(config, 'save');

sut.showOverrideMobileLauncher(mockContainerEl, config);

// trigger the save
dropdownSettingOnChangeFn(finalValue, config);

expect(config.mobileLauncher.modeString).toBe(finalValue);
expect(configSaveSpy).toHaveBeenCalled();

addDropdownSettingSpy.mockRestore();
configSaveSpy.mockRestore();
});
});

describe('showMatchPriorityAdjustments', () => {
type addToggleSettingArgs = Parameters<SettingsTabSection['addToggleSetting']>;
let toggleSettingOnChangeFn: addToggleSettingArgs[5];
Expand Down
15 changes: 15 additions & 0 deletions src/settings/__tests__/switcherPlusSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ function getDefaultSettingsData(): SettingsData {
preferredSourceForTitle: 'H1',
closeWhenEmptyKeys: [{ modifiers: null, key: 'Backspace' }],
escapeCmdChar: '!',
mobileLauncher: {
isEnabled: false,
modeString: Mode[Mode.HeadingsList],
iconName: '',
coreLauncherButtonIconSelector: 'span.clickable-icon',
coreLauncherButtonSelector:
'.mobile-navbar-action.mod-tappable:has(span.clickable-icon svg.svg-icon.lucide-plus-circle)',
},
};

return data;
Expand Down Expand Up @@ -249,6 +257,13 @@ function getTransientSettingsData(): SettingsData {
},
],
escapeCmdChar: chance.letter(),
mobileLauncher: {
isEnabled: false,
modeString: Mode[Mode.CommandList],
iconName: '',
coreLauncherButtonIconSelector: '',
coreLauncherButtonSelector: '',
},
};

return data;
Expand Down
48 changes: 48 additions & 0 deletions src/settings/generalSettingsTabSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { Modal } from 'obsidian';
import { SwitcherPlusSettings } from 'src/settings';
import { Mode, PathDisplayFormat, TitleSource } from 'src/types';
import { SettingsTabSection } from './settingsTabSection';
import { getModeNames } from 'src/utils';

export class GeneralSettingsTabSection extends SettingsTabSection {
display(containerEl: HTMLElement): void {
const { config } = this;

this.addSectionTitle(containerEl, 'General Settings');
this.showEnabledRibbonCommands(containerEl, config);
this.showOverrideMobileLauncher(containerEl, config);
this.showPreferredSourceForTitle(containerEl, config);

this.showPathDisplayFormat(containerEl, config);
Expand Down Expand Up @@ -169,6 +171,52 @@ export class GeneralSettingsTabSection extends SettingsTabSection {
popup.open();
}

showOverrideMobileLauncher(
containerEl: HTMLElement,
config: SwitcherPlusSettings,
): void {
const { mobileLauncher } = config;
const desc =
'Override the "⊕" button (in the Navigation Bar) on mobile platforms to launch Switcher++ instead of the default system switcher. Select the Mode to launch Switcher++ in, or select "Do not override" to disable the feature.';

const disableOptionKey = 'disabled'; // Option to disable the feature
const options: Record<string, string> = { [disableOptionKey]: 'Do not override' };

// Add each mode to the list of options
const modeNames = getModeNames();
modeNames.forEach((name) => {
options[name] = name;
});

let initialValue = disableOptionKey;
if (
mobileLauncher.isEnabled &&
modeNames.includes(mobileLauncher.modeString as keyof typeof Mode)
) {
initialValue = mobileLauncher.modeString;
}

this.addDropdownSetting(
containerEl,
'Override default Switcher launch button (the "⊕" button) on mobile platforms',
desc,
initialValue,
options,
null,
(rawValue, config) => {
const isEnabled = rawValue !== disableOptionKey;

config.mobileLauncher.isEnabled = isEnabled;
if (isEnabled) {
config.mobileLauncher.modeString = rawValue;
}

config.save();
this.mainSettingsTab.plugin.updateMobileLauncherButtonOverride(isEnabled);
},
);
}

showMatchPriorityAdjustments(
containerEl: HTMLElement,
config: SwitcherPlusSettings,
Expand Down
17 changes: 17 additions & 0 deletions src/settings/switcherPlusSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
FacetSettingsData,
InsertLinkConfig,
MatchPriorityData,
MobileLauncherConfig,
Mode,
NavigationKeysConfig,
PathDisplayFormat,
Expand Down Expand Up @@ -125,6 +126,14 @@ export class SwitcherPlusSettings {
preferredSourceForTitle: 'H1',
closeWhenEmptyKeys: [{ modifiers: null, key: 'Backspace' }],
escapeCmdChar: '!',
mobileLauncher: {
isEnabled: false,
modeString: Mode[Mode.HeadingsList],
iconName: '',
coreLauncherButtonIconSelector: 'span.clickable-icon',
coreLauncherButtonSelector:
'.mobile-navbar-action.mod-tappable:has(span.clickable-icon svg.svg-icon.lucide-plus-circle)',
},
};
}

Expand Down Expand Up @@ -607,6 +616,14 @@ export class SwitcherPlusSettings {
this.data.escapeCmdChar = value;
}

get mobileLauncher(): MobileLauncherConfig {
return this.data.mobileLauncher;
}

set mobileLauncher(value: MobileLauncherConfig) {
this.data.mobileLauncher = value;
}

constructor(private plugin: SwitcherPlusPlugin) {
this.data = SwitcherPlusSettings.defaults;
}
Expand Down
Loading

0 comments on commit a1990c2

Please sign in to comment.