diff options
author | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-07 21:16:47 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-07 21:16:47 +0900 |
commit | 05ef6a8ca35aaa801c11eb6b4896caa3690058af (patch) | |
tree | 2c7708ca91ac2b462cc86aa28612e3d3943496f3 /src/background | |
parent | 457d954e08923b4accd28a919c72d0b61db1bb98 (diff) | |
parent | 27d0a7f37d24a0ad68a8ccb7dee18fc1d00eea58 (diff) |
Merge pull request #578 from ueokande/move-to-typescript
Move to TypeScript
Diffstat (limited to 'src/background')
-rw-r--r-- | src/background/controllers/AddonEnabledController.ts (renamed from src/background/controllers/AddonEnabledController.js) | 4 | ||||
-rw-r--r-- | src/background/controllers/CommandController.ts (renamed from src/background/controllers/CommandController.js) | 14 | ||||
-rw-r--r-- | src/background/controllers/FindController.ts (renamed from src/background/controllers/FindController.js) | 6 | ||||
-rw-r--r-- | src/background/controllers/LinkController.js | 15 | ||||
-rw-r--r-- | src/background/controllers/LinkController.ts | 19 | ||||
-rw-r--r-- | src/background/controllers/MarkController.js | 15 | ||||
-rw-r--r-- | src/background/controllers/MarkController.ts | 17 | ||||
-rw-r--r-- | src/background/controllers/OperationController.ts (renamed from src/background/controllers/OperationController.js) | 15 | ||||
-rw-r--r-- | src/background/controllers/SettingController.ts (renamed from src/background/controllers/SettingController.js) | 9 | ||||
-rw-r--r-- | src/background/controllers/VersionController.ts (renamed from src/background/controllers/VersionController.js) | 6 | ||||
-rw-r--r-- | src/background/controllers/version.js | 13 | ||||
-rw-r--r-- | src/background/domains/CommandDocs.ts (renamed from src/background/domains/CommandDocs.js) | 3 | ||||
-rw-r--r-- | src/background/domains/CompletionGroup.js | 14 | ||||
-rw-r--r-- | src/background/domains/CompletionGroup.ts | 7 | ||||
-rw-r--r-- | src/background/domains/CompletionItem.js | 24 | ||||
-rw-r--r-- | src/background/domains/CompletionItem.ts | 7 | ||||
-rw-r--r-- | src/background/domains/Completions.js | 27 | ||||
-rw-r--r-- | src/background/domains/GlobalMark.js | 24 | ||||
-rw-r--r-- | src/background/domains/GlobalMark.ts | 7 | ||||
-rw-r--r-- | src/background/domains/Setting.js | 51 | ||||
-rw-r--r-- | src/background/index.ts (renamed from src/background/index.js) | 0 | ||||
-rw-r--r-- | src/background/infrastructures/ConsoleClient.ts (renamed from src/background/infrastructures/ConsoleClient.js) | 12 | ||||
-rw-r--r-- | src/background/infrastructures/ContentMessageClient.ts (renamed from src/background/infrastructures/ContentMessageClient.js) | 14 | ||||
-rw-r--r-- | src/background/infrastructures/ContentMessageListener.ts (renamed from src/background/infrastructures/ContentMessageListener.js) | 82 | ||||
-rw-r--r-- | src/background/infrastructures/MemoryStorage.ts (renamed from src/background/infrastructures/MemoryStorage.js) | 6 | ||||
-rw-r--r-- | src/background/presenters/IndicatorPresenter.ts (renamed from src/background/presenters/IndicatorPresenter.js) | 4 | ||||
-rw-r--r-- | src/background/presenters/NotifyPresenter.ts (renamed from src/background/presenters/NotifyPresenter.js) | 10 | ||||
-rw-r--r-- | src/background/presenters/TabPresenter.ts (renamed from src/background/presenters/TabPresenter.js) | 44 | ||||
-rw-r--r-- | src/background/presenters/WindowPresenter.ts (renamed from src/background/presenters/WindowPresenter.js) | 2 | ||||
-rw-r--r-- | src/background/repositories/BookmarkRepository.ts (renamed from src/background/repositories/BookmarkRepository.js) | 4 | ||||
-rw-r--r-- | src/background/repositories/BrowserSettingRepository.js | 8 | ||||
-rw-r--r-- | src/background/repositories/BrowserSettingRepository.ts | 24 | ||||
-rw-r--r-- | src/background/repositories/CompletionsRepository.ts (renamed from src/background/repositories/CompletionsRepository.js) | 14 | ||||
-rw-r--r-- | src/background/repositories/FindRepository.ts (renamed from src/background/repositories/FindRepository.js) | 6 | ||||
-rw-r--r-- | src/background/repositories/MarkRepository.ts (renamed from src/background/repositories/MarkRepository.js) | 8 | ||||
-rw-r--r-- | src/background/repositories/PersistentSettingRepository.ts (renamed from src/background/repositories/PersistentSettingRepository.js) | 6 | ||||
-rw-r--r-- | src/background/repositories/SettingRepository.js | 23 | ||||
-rw-r--r-- | src/background/repositories/SettingRepository.ts | 51 | ||||
-rw-r--r-- | src/background/repositories/VersionRepository.js | 10 | ||||
-rw-r--r-- | src/background/usecases/AddonEnabledUseCase.ts (renamed from src/background/usecases/AddonEnabledUseCase.js) | 18 | ||||
-rw-r--r-- | src/background/usecases/CommandUseCase.ts (renamed from src/background/usecases/CommandUseCase.js) | 57 | ||||
-rw-r--r-- | src/background/usecases/CompletionsUseCase.ts (renamed from src/background/usecases/CompletionsUseCase.js) | 135 | ||||
-rw-r--r-- | src/background/usecases/ConsoleUseCase.js | 61 | ||||
-rw-r--r-- | src/background/usecases/ConsoleUseCase.ts | 65 | ||||
-rw-r--r-- | src/background/usecases/FindUseCase.ts (renamed from src/background/usecases/FindUseCase.js) | 14 | ||||
-rw-r--r-- | src/background/usecases/LinkUseCase.ts (renamed from src/background/usecases/LinkUseCase.js) | 8 | ||||
-rw-r--r-- | src/background/usecases/MarkUseCase.ts (renamed from src/background/usecases/MarkUseCase.js) | 33 | ||||
-rw-r--r-- | src/background/usecases/SettingUseCase.ts (renamed from src/background/usecases/SettingUseCase.js) | 21 | ||||
-rw-r--r-- | src/background/usecases/TabSelectUseCase.ts (renamed from src/background/usecases/TabSelectUseCase.js) | 24 | ||||
-rw-r--r-- | src/background/usecases/TabUseCase.ts (renamed from src/background/usecases/TabUseCase.js) | 36 | ||||
-rw-r--r-- | src/background/usecases/VersionUseCase.ts (renamed from src/background/usecases/VersionUseCase.js) | 12 | ||||
-rw-r--r-- | src/background/usecases/ZoomUseCase.js | 35 | ||||
-rw-r--r-- | src/background/usecases/ZoomUseCase.ts | 39 | ||||
-rw-r--r-- | src/background/usecases/filters.js | 72 | ||||
-rw-r--r-- | src/background/usecases/filters.ts | 76 | ||||
-rw-r--r-- | src/background/usecases/parsers.js | 31 | ||||
-rw-r--r-- | src/background/usecases/parsers.ts | 36 |
57 files changed, 733 insertions, 665 deletions
diff --git a/src/background/controllers/AddonEnabledController.js b/src/background/controllers/AddonEnabledController.ts index 9a3a521..251af25 100644 --- a/src/background/controllers/AddonEnabledController.js +++ b/src/background/controllers/AddonEnabledController.ts @@ -1,11 +1,13 @@ import AddonEnabledUseCase from '../usecases/AddonEnabledUseCase'; export default class AddonEnabledController { + private addonEnabledUseCase: AddonEnabledUseCase; + constructor() { this.addonEnabledUseCase = new AddonEnabledUseCase(); } - indicate(enabled) { + indicate(enabled: boolean): Promise<any> { return this.addonEnabledUseCase.indicate(enabled); } } diff --git a/src/background/controllers/CommandController.js b/src/background/controllers/CommandController.ts index b113709..f3a6b7f 100644 --- a/src/background/controllers/CommandController.js +++ b/src/background/controllers/CommandController.ts @@ -1,19 +1,23 @@ import CompletionsUseCase from '../usecases/CompletionsUseCase'; import CommandUseCase from '../usecases/CommandUseCase'; -import Completions from '../domains/Completions'; +import CompletionGroup from '../domains/CompletionGroup'; -const trimStart = (str) => { +const trimStart = (str: string): string => { // NOTE String.trimStart is available on Firefox 61 return str.replace(/^\s+/, ''); }; export default class CommandController { + private completionsUseCase: CompletionsUseCase; + + private commandIndicator: CommandUseCase; + constructor() { this.completionsUseCase = new CompletionsUseCase(); this.commandIndicator = new CommandUseCase(); } - getCompletions(line) { + getCompletions(line: string): Promise<CompletionGroup[]> { let trimmed = trimStart(line); let words = trimmed.split(/ +/); let name = words[0]; @@ -45,11 +49,11 @@ export default class CommandController { case 'set': return this.completionsUseCase.querySet(name, keywords); } - return Promise.resolve(Completions.empty()); + return Promise.resolve([]); } // eslint-disable-next-line complexity - exec(line) { + exec(line: string): Promise<any> { let trimmed = trimStart(line); let words = trimmed.split(/ +/); let name = words[0]; diff --git a/src/background/controllers/FindController.js b/src/background/controllers/FindController.ts index caeff98..28959e2 100644 --- a/src/background/controllers/FindController.js +++ b/src/background/controllers/FindController.ts @@ -1,15 +1,17 @@ import FindUseCase from '../usecases/FindUseCase'; export default class FindController { + private findUseCase: FindUseCase; + constructor() { this.findUseCase = new FindUseCase(); } - getKeyword() { + getKeyword(): Promise<string> { return this.findUseCase.getKeyword(); } - setKeyword(keyword) { + setKeyword(keyword: string): Promise<any> { return this.findUseCase.setKeyword(keyword); } } diff --git a/src/background/controllers/LinkController.js b/src/background/controllers/LinkController.js deleted file mode 100644 index 7e395b1..0000000 --- a/src/background/controllers/LinkController.js +++ /dev/null @@ -1,15 +0,0 @@ -import LinkUseCase from '../usecases/LinkUseCase'; - -export default class LinkController { - constructor() { - this.linkUseCase = new LinkUseCase(); - } - - openToTab(url, tabId) { - this.linkUseCase.openToTab(url, tabId); - } - - openNewTab(url, openerId, background) { - this.linkUseCase.openNewTab(url, openerId, background); - } -} diff --git a/src/background/controllers/LinkController.ts b/src/background/controllers/LinkController.ts new file mode 100644 index 0000000..707b28a --- /dev/null +++ b/src/background/controllers/LinkController.ts @@ -0,0 +1,19 @@ +import LinkUseCase from '../usecases/LinkUseCase'; + +export default class LinkController { + private linkUseCase: LinkUseCase; + + constructor() { + this.linkUseCase = new LinkUseCase(); + } + + openToTab(url: string, tabId: number): Promise<void> { + return this.linkUseCase.openToTab(url, tabId); + } + + openNewTab( + url: string, openerId: number, background: boolean, + ): Promise<void> { + return this.linkUseCase.openNewTab(url, openerId, background); + } +} diff --git a/src/background/controllers/MarkController.js b/src/background/controllers/MarkController.js deleted file mode 100644 index 0478369..0000000 --- a/src/background/controllers/MarkController.js +++ /dev/null @@ -1,15 +0,0 @@ -import MarkUseCase from '../usecases/MarkUseCase'; - -export default class MarkController { - constructor() { - this.markUseCase = new MarkUseCase(); - } - - setGlobal(key, x, y) { - this.markUseCase.setGlobal(key, x, y); - } - - jumpGlobal(key) { - this.markUseCase.jumpGlobal(key); - } -} diff --git a/src/background/controllers/MarkController.ts b/src/background/controllers/MarkController.ts new file mode 100644 index 0000000..419a08b --- /dev/null +++ b/src/background/controllers/MarkController.ts @@ -0,0 +1,17 @@ +import MarkUseCase from '../usecases/MarkUseCase'; + +export default class MarkController { + private markUseCase: MarkUseCase; + + constructor() { + this.markUseCase = new MarkUseCase(); + } + + setGlobal(key: string, x: number, y: number): Promise<any> { + return this.markUseCase.setGlobal(key, x, y); + } + + jumpGlobal(key: string): Promise<any> { + return this.markUseCase.jumpGlobal(key); + } +} diff --git a/src/background/controllers/OperationController.js b/src/background/controllers/OperationController.ts index 416aa9c..fa09512 100644 --- a/src/background/controllers/OperationController.js +++ b/src/background/controllers/OperationController.ts @@ -1,4 +1,4 @@ -import operations from '../../shared/operations'; +import * as operations from '../../shared/operations'; import FindUseCase from '../usecases/FindUseCase'; import ConsoleUseCase from '../usecases/ConsoleUseCase'; import TabUseCase from '../usecases/TabUseCase'; @@ -6,6 +6,16 @@ import TabSelectUseCase from '../usecases/TabSelectUseCase'; import ZoomUseCase from '../usecases/ZoomUseCase'; export default class OperationController { + private findUseCase: FindUseCase; + + private consoleUseCase: ConsoleUseCase; + + private tabUseCase: TabUseCase; + + private tabSelectUseCase: TabSelectUseCase; + + private zoomUseCase: ZoomUseCase; + constructor() { this.findUseCase = new FindUseCase(); this.consoleUseCase = new ConsoleUseCase(); @@ -15,7 +25,7 @@ export default class OperationController { } // eslint-disable-next-line complexity, max-lines-per-function - exec(operation) { + exec(operation: operations.Operation): Promise<any> { switch (operation.type) { case operations.TAB_CLOSE: return this.tabUseCase.close(false); @@ -72,6 +82,7 @@ export default class OperationController { case operations.CANCEL: return this.consoleUseCase.hideConsole(); } + throw new Error('unknown operation: ' + operation.type); } } diff --git a/src/background/controllers/SettingController.js b/src/background/controllers/SettingController.ts index e895d72..dfd2817 100644 --- a/src/background/controllers/SettingController.js +++ b/src/background/controllers/SettingController.ts @@ -1,17 +1,22 @@ import SettingUseCase from '../usecases/SettingUseCase'; import ContentMessageClient from '../infrastructures/ContentMessageClient'; +import Settings from '../../shared/Settings'; export default class SettingController { + private settingUseCase: SettingUseCase; + + private contentMessageClient: ContentMessageClient; + constructor() { this.settingUseCase = new SettingUseCase(); this.contentMessageClient = new ContentMessageClient(); } - getSetting() { + getSetting(): Promise<Settings> { return this.settingUseCase.get(); } - async reload() { + async reload(): Promise<any> { await this.settingUseCase.reload(); this.contentMessageClient.broadcastSettingsChanged(); } diff --git a/src/background/controllers/VersionController.js b/src/background/controllers/VersionController.ts index c596f9b..2e2a197 100644 --- a/src/background/controllers/VersionController.js +++ b/src/background/controllers/VersionController.ts @@ -1,11 +1,13 @@ import VersionUseCase from '../usecases/VersionUseCase'; export default class VersionController { + private versionUseCase: VersionUseCase; + constructor() { this.versionUseCase = new VersionUseCase(); } - notify() { - this.versionUseCase.notify(); + notify(): Promise<void> { + return this.versionUseCase.notify(); } } diff --git a/src/background/controllers/version.js b/src/background/controllers/version.js deleted file mode 100644 index ec0f634..0000000 --- a/src/background/controllers/version.js +++ /dev/null @@ -1,13 +0,0 @@ -import VersionInteractor from '../usecases/version'; - -export default class VersionController { - constructor() { - this.versionInteractor = new VersionInteractor(); - } - - notifyIfUpdated() { - browser.runtime.onInstalled.addListener(() => { - return this.versionInteractor.notify(); - }); - } -} diff --git a/src/background/domains/CommandDocs.js b/src/background/domains/CommandDocs.ts index 734c68e..25ea62a 100644 --- a/src/background/domains/CommandDocs.js +++ b/src/background/domains/CommandDocs.ts @@ -8,5 +8,4 @@ export default { bdeletes: 'Close all tabs matched by keywords', quit: 'Close the current tab', quitall: 'Close all tabs', -}; - +} as {[key: string]: string}; diff --git a/src/background/domains/CompletionGroup.js b/src/background/domains/CompletionGroup.js deleted file mode 100644 index 1749d72..0000000 --- a/src/background/domains/CompletionGroup.js +++ /dev/null @@ -1,14 +0,0 @@ -export default class CompletionGroup { - constructor(name, items) { - this.name0 = name; - this.items0 = items; - } - - get name() { - return this.name0; - } - - get items() { - return this.items0; - } -} diff --git a/src/background/domains/CompletionGroup.ts b/src/background/domains/CompletionGroup.ts new file mode 100644 index 0000000..1eea7d8 --- /dev/null +++ b/src/background/domains/CompletionGroup.ts @@ -0,0 +1,7 @@ +import CompletionItem from './CompletionItem'; + +export default interface CompletionGroup { + name: string; + items: CompletionItem[]; + // eslint-disable-next-line semi +} diff --git a/src/background/domains/CompletionItem.js b/src/background/domains/CompletionItem.js deleted file mode 100644 index c7ad8a1..0000000 --- a/src/background/domains/CompletionItem.js +++ /dev/null @@ -1,24 +0,0 @@ -export default class CompletionItem { - constructor({ caption, content, url, icon }) { - this.caption0 = caption; - this.content0 = content; - this.url0 = url; - this.icon0 = icon; - } - - get caption() { - return this.caption0; - } - - get content() { - return this.content0; - } - - get url() { - return this.url0; - } - - get icon() { - return this.icon0; - } -} diff --git a/src/background/domains/CompletionItem.ts b/src/background/domains/CompletionItem.ts new file mode 100644 index 0000000..657efaa --- /dev/null +++ b/src/background/domains/CompletionItem.ts @@ -0,0 +1,7 @@ +export default interface CompletionItem { + readonly caption?: string; + readonly content?: string; + readonly url?: string; + readonly icon?: string; + // eslint-disable-next-line semi +} diff --git a/src/background/domains/Completions.js b/src/background/domains/Completions.js deleted file mode 100644 index f399743..0000000 --- a/src/background/domains/Completions.js +++ /dev/null @@ -1,27 +0,0 @@ -export default class Completions { - constructor(groups) { - this.g = groups; - } - - get groups() { - return this.g; - } - - serialize() { - return this.groups.map(group => ({ - name: group.name, - items: group.items.map(item => ({ - caption: item.caption, - content: item.content, - url: item.url, - icon: item.icon, - })), - })); - } - - static empty() { - return EMPTY_COMPLETIONS; - } -} - -let EMPTY_COMPLETIONS = new Completions([]); diff --git a/src/background/domains/GlobalMark.js b/src/background/domains/GlobalMark.js deleted file mode 100644 index f0586f1..0000000 --- a/src/background/domains/GlobalMark.js +++ /dev/null @@ -1,24 +0,0 @@ -export default class GlobalMark { - constructor(tabId, url, x, y) { - this.tabId0 = tabId; - this.url0 = url; - this.x0 = x; - this.y0 = y; - } - - get tabId() { - return this.tabId0; - } - - get url() { - return this.url0; - } - - get x() { - return this.x0; - } - - get y() { - return this.y0; - } -} diff --git a/src/background/domains/GlobalMark.ts b/src/background/domains/GlobalMark.ts new file mode 100644 index 0000000..1ae912e --- /dev/null +++ b/src/background/domains/GlobalMark.ts @@ -0,0 +1,7 @@ +export default interface GlobalMark { + readonly tabId: number; + readonly url: string; + readonly x: number; + readonly y: number; + // eslint-disable-next-line semi +} diff --git a/src/background/domains/Setting.js b/src/background/domains/Setting.js deleted file mode 100644 index 106ec0f..0000000 --- a/src/background/domains/Setting.js +++ /dev/null @@ -1,51 +0,0 @@ -import DefaultSettings from '../../shared/settings/default'; -import * as settingsValues from '../../shared/settings/values'; - -export default class Setting { - constructor({ source, json, form }) { - this.obj = { - source, json, form - }; - } - - get source() { - return this.obj.source; - } - - get json() { - return this.obj.json; - } - - get form() { - return this.obj.form; - } - - value() { - let value = JSON.parse(DefaultSettings.json); - if (this.obj.source === 'json') { - value = settingsValues.valueFromJson(this.obj.json); - } else if (this.obj.source === 'form') { - value = settingsValues.valueFromForm(this.obj.form); - } - if (!value.properties) { - value.properties = {}; - } - return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value }; - } - - serialize() { - return this.obj; - } - - static deserialize(obj) { - return new Setting({ source: obj.source, json: obj.json, form: obj.form }); - } - - static defaultSettings() { - return new Setting({ - source: DefaultSettings.source, - json: DefaultSettings.json, - form: {}, - }); - } -} diff --git a/src/background/index.js b/src/background/index.ts index f9efd4d..f9efd4d 100644 --- a/src/background/index.js +++ b/src/background/index.ts diff --git a/src/background/infrastructures/ConsoleClient.js b/src/background/infrastructures/ConsoleClient.ts index f691515..c162634 100644 --- a/src/background/infrastructures/ConsoleClient.js +++ b/src/background/infrastructures/ConsoleClient.ts @@ -1,34 +1,34 @@ -import messages from '../../shared/messages'; +import * as messages from '../../shared/messages'; export default class ConsoleClient { - showCommand(tabId, command) { + showCommand(tabId: number, command: string): Promise<any> { return browser.tabs.sendMessage(tabId, { type: messages.CONSOLE_SHOW_COMMAND, command, }); } - showFind(tabId) { + showFind(tabId: number): Promise<any> { return browser.tabs.sendMessage(tabId, { type: messages.CONSOLE_SHOW_FIND }); } - showInfo(tabId, message) { + showInfo(tabId: number, message: string): Promise<any> { return browser.tabs.sendMessage(tabId, { type: messages.CONSOLE_SHOW_INFO, text: message, }); } - showError(tabId, message) { + showError(tabId: number, message: string): Promise<any> { return browser.tabs.sendMessage(tabId, { type: messages.CONSOLE_SHOW_ERROR, text: message, }); } - hide(tabId) { + hide(tabId: number): Promise<any> { return browser.tabs.sendMessage(tabId, { type: messages.CONSOLE_HIDE, }); diff --git a/src/background/infrastructures/ContentMessageClient.js b/src/background/infrastructures/ContentMessageClient.ts index 0fab5a3..d4bc476 100644 --- a/src/background/infrastructures/ContentMessageClient.js +++ b/src/background/infrastructures/ContentMessageClient.ts @@ -1,10 +1,10 @@ -import messages from '../../shared/messages'; +import * as messages from '../../shared/messages'; export default class ContentMessageClient { - async broadcastSettingsChanged() { + async broadcastSettingsChanged(): Promise<void> { let tabs = await browser.tabs.query({}); for (let tab of tabs) { - if (tab.url.startsWith('about:')) { + if (!tab.id || tab.url && tab.url.startsWith('about:')) { continue; } browser.tabs.sendMessage(tab.id, { @@ -13,20 +13,20 @@ export default class ContentMessageClient { } } - async getAddonEnabled(tabId) { + async getAddonEnabled(tabId: number): Promise<boolean> { let { enabled } = await browser.tabs.sendMessage(tabId, { type: messages.ADDON_ENABLED_QUERY, - }); + }) as { enabled: boolean }; return enabled; } - toggleAddonEnabled(tabId) { + toggleAddonEnabled(tabId: number): Promise<void> { return browser.tabs.sendMessage(tabId, { type: messages.ADDON_TOGGLE_ENABLED, }); } - scrollTo(tabId, x, y) { + scrollTo(tabId: number, x: number, y: number): Promise<void> { return browser.tabs.sendMessage(tabId, { type: messages.TAB_SCROLL_TO, x, diff --git a/src/background/infrastructures/ContentMessageListener.js b/src/background/infrastructures/ContentMessageListener.ts index 5b0f62e..1cc2696 100644 --- a/src/background/infrastructures/ContentMessageListener.js +++ b/src/background/infrastructures/ContentMessageListener.ts @@ -1,4 +1,5 @@ -import messages from '../../shared/messages'; +import * as messages from '../../shared/messages'; +import CompletionGroup from '../domains/CompletionGroup'; import CommandController from '../controllers/CommandController'; import SettingController from '../controllers/SettingController'; import FindController from '../controllers/FindController'; @@ -8,6 +9,22 @@ import OperationController from '../controllers/OperationController'; import MarkController from '../controllers/MarkController'; export default class ContentMessageListener { + private settingController: SettingController; + + private commandController: CommandController; + + private findController: FindController; + + private addonEnabledController: AddonEnabledController; + + private linkController: LinkController; + + private backgroundOperationController: OperationController; + + private markController: MarkController; + + private consolePorts: {[tabId: number]: browser.runtime.Port}; + constructor() { this.settingController = new SettingController(); this.commandController = new CommandController(); @@ -20,20 +37,28 @@ export default class ContentMessageListener { this.consolePorts = {}; } - run() { - browser.runtime.onMessage.addListener((message, sender) => { + run(): void { + browser.runtime.onMessage.addListener(( + message: any, sender: browser.runtime.MessageSender, + ) => { try { - let ret = this.onMessage(message, sender); + let ret = this.onMessage(message, sender.tab as browser.tabs.Tab); if (!(ret instanceof Promise)) { return {}; } return ret.catch((e) => { + if (!sender.tab || !sender.tab.id) { + return; + } return browser.tabs.sendMessage(sender.tab.id, { type: messages.CONSOLE_SHOW_ERROR, text: e.message, }); }); } catch (e) { + if (!sender.tab || !sender.tab.id) { + return; + } return browser.tabs.sendMessage(sender.tab.id, { type: messages.CONSOLE_SHOW_ERROR, text: e.message, @@ -43,7 +68,9 @@ export default class ContentMessageListener { browser.runtime.onConnect.addListener(this.onConnected.bind(this)); } - onMessage(message, sender) { + onMessage( + message: messages.Message, senderTab: browser.tabs.Tab, + ): Promise<any> | any { switch (message.type) { case messages.CONSOLE_QUERY_COMPLETIONS: return this.onConsoleQueryCompletions(message.text); @@ -59,7 +86,10 @@ export default class ContentMessageListener { return this.onAddonEnabledResponse(message.enabled); case messages.OPEN_URL: return this.onOpenUrl( - message.newTab, message.url, sender.tab.id, message.background); + message.newTab, + message.url, + senderTab.id as number, + message.background); case messages.BACKGROUND_OPERATION: return this.onBackgroundOperation(message.operation); case messages.MARK_SET_GLOBAL: @@ -67,56 +97,60 @@ export default class ContentMessageListener { case messages.MARK_JUMP_GLOBAL: return this.onMarkJumpGlobal(message.key); case messages.CONSOLE_FRAME_MESSAGE: - return this.onConsoleFrameMessage(sender.tab.id, message.message); + return this.onConsoleFrameMessage( + senderTab.id as number, message.message, + ); } + throw new Error('unsupported message: ' + message.type); } - async onConsoleQueryCompletions(line) { + async onConsoleQueryCompletions(line: string): Promise<CompletionGroup[]> { let completions = await this.commandController.getCompletions(line); - return Promise.resolve(completions.serialize()); + return Promise.resolve(completions); } - onConsoleEnterCommand(text) { + onConsoleEnterCommand(text: string): Promise<any> { return this.commandController.exec(text); } - - onSettingsQuery() { + onSettingsQuery(): Promise<any> { return this.settingController.getSetting(); } - onFindGetKeyword() { + onFindGetKeyword(): Promise<string> { return this.findController.getKeyword(); } - onFindSetKeyword(keyword) { + onFindSetKeyword(keyword: string): Promise<any> { return this.findController.setKeyword(keyword); } - onAddonEnabledResponse(enabled) { + onAddonEnabledResponse(enabled: boolean): Promise<any> { return this.addonEnabledController.indicate(enabled); } - onOpenUrl(newTab, url, openerId, background) { + onOpenUrl( + newTab: boolean, url: string, openerId: number, background: boolean, + ): Promise<any> { if (newTab) { return this.linkController.openNewTab(url, openerId, background); } return this.linkController.openToTab(url, openerId); } - onBackgroundOperation(operation) { + onBackgroundOperation(operation: any): Promise<any> { return this.backgroundOperationController.exec(operation); } - onMarkSetGlobal(key, x, y) { + onMarkSetGlobal(key: string, x: number, y: number): Promise<any> { return this.markController.setGlobal(key, x, y); } - onMarkJumpGlobal(key) { + onMarkJumpGlobal(key: string): Promise<any> { return this.markController.jumpGlobal(key); } - onConsoleFrameMessage(tabId, message) { + onConsoleFrameMessage(tabId: number, message: any): void { let port = this.consolePorts[tabId]; if (!port) { return; @@ -124,12 +158,14 @@ export default class ContentMessageListener { port.postMessage(message); } - onConnected(port) { + onConnected(port: browser.runtime.Port): void { if (port.name !== 'vimvixen-console') { return; } - let id = port.sender.tab.id; - this.consolePorts[id] = port; + if (port.sender && port.sender.tab && port.sender.tab.id) { + let id = port.sender.tab.id; + this.consolePorts[id] = port; + } } } diff --git a/src/background/infrastructures/MemoryStorage.js b/src/background/infrastructures/MemoryStorage.ts index 3a7e4f2..baf9ffa 100644 --- a/src/background/infrastructures/MemoryStorage.js +++ b/src/background/infrastructures/MemoryStorage.ts @@ -1,7 +1,7 @@ -const db = {}; +const db: {[key: string]: any} = {}; export default class MemoryStorage { - set(name, value) { + set(name: string, value: any): void { let data = JSON.stringify(value); if (typeof data === 'undefined') { throw new Error('value is not serializable'); @@ -9,7 +9,7 @@ export default class MemoryStorage { db[name] = data; } - get(name) { + get(name: string): any { let data = db[name]; if (!data) { return undefined; diff --git a/src/background/presenters/IndicatorPresenter.js b/src/background/presenters/IndicatorPresenter.ts index 5737519..d9a615a 100644 --- a/src/background/presenters/IndicatorPresenter.js +++ b/src/background/presenters/IndicatorPresenter.ts @@ -1,12 +1,12 @@ export default class IndicatorPresenter { - indicate(enabled) { + indicate(enabled: boolean): Promise<void> { let path = enabled ? 'resources/enabled_32x32.png' : 'resources/disabled_32x32.png'; return browser.browserAction.setIcon({ path }); } - onClick(listener) { + onClick(listener: (arg: browser.tabs.Tab) => void): void { browser.browserAction.onClicked.addListener(listener); } } diff --git a/src/background/presenters/NotifyPresenter.js b/src/background/presenters/NotifyPresenter.ts index a81f227..23932f7 100644 --- a/src/background/presenters/NotifyPresenter.js +++ b/src/background/presenters/NotifyPresenter.ts @@ -1,8 +1,12 @@ const NOTIFICATION_ID = 'vimvixen-update'; export default class NotifyPresenter { - notify(title, message, onclick) { - const listener = (id) => { + async notify( + title: string, + message: string, + onclick: () => void, + ): Promise<void> { + const listener = (id: string) => { if (id !== NOTIFICATION_ID) { return; } @@ -13,7 +17,7 @@ export default class NotifyPresenter { }; browser.notifications.onClicked.addListener(listener); - return browser.notifications.create(NOTIFICATION_ID, { + await browser.notifications.create(NOTIFICATION_ID, { 'type': 'basic', 'iconUrl': browser.extension.getURL('resources/icon_48x48.png'), title, diff --git a/src/background/presenters/TabPresenter.js b/src/background/presenters/TabPresenter.ts index 744be39..33c6513 100644 --- a/src/background/presenters/TabPresenter.js +++ b/src/background/presenters/TabPresenter.ts @@ -3,27 +3,29 @@ import MemoryStorage from '../infrastructures/MemoryStorage'; const CURRENT_SELECTED_KEY = 'tabs.current.selected'; const LAST_SELECTED_KEY = 'tabs.last.selected'; +type Tab = browser.tabs.Tab; + export default class TabPresenter { - open(url, tabId) { + open(url: string, tabId?: number): Promise<Tab> { return browser.tabs.update(tabId, { url }); } - create(url, opts) { + create(url: string, opts?: object): Promise<Tab> { return browser.tabs.create({ url, ...opts }); } - async getCurrent() { + async getCurrent(): Promise<Tab> { let tabs = await browser.tabs.query({ active: true, currentWindow: true }); return tabs[0]; } - getAll() { + getAll(): Promise<Tab[]> { return browser.tabs.query({ currentWindow: true }); } - async getLastSelectedId() { + async getLastSelectedId(): Promise<number | undefined> { let cache = new MemoryStorage(); let tabId = await cache.get(LAST_SELECTED_KEY); if (tabId === null || typeof tabId === 'undefined') { @@ -32,25 +34,25 @@ export default class TabPresenter { return tabId; } - async getByKeyword(keyword, excludePinned = false) { + async getByKeyword(keyword: string, excludePinned = false): Promise<Tab[]> { let tabs = await browser.tabs.query({ currentWindow: true }); return tabs.filter((t) => { - return t.url.toLowerCase().includes(keyword.toLowerCase()) || + return t.url && t.url.toLowerCase().includes(keyword.toLowerCase()) || t.title && t.title.toLowerCase().includes(keyword.toLowerCase()); }).filter((t) => { return !(excludePinned && t.pinned); }); } - select(tabId) { + select(tabId: number): Promise<Tab> { return browser.tabs.update(tabId, { active: true }); } - remove(ids) { + remove(ids: number[]): Promise<void> { return browser.tabs.remove(ids); } - async reopen() { + async reopen(): Promise<any> { let window = await browser.windows.getCurrent(); let sessions = await browser.sessions.getRecentlyClosed(); let session = sessions.find((s) => { @@ -59,39 +61,43 @@ export default class TabPresenter { if (!session) { return; } - if (session.tab) { + if (session.tab && session.tab.sessionId) { return browser.sessions.restore(session.tab.sessionId); } - return browser.sessions.restore(session.window.sessionId); + if (session.window && session.window.sessionId) { + return browser.sessions.restore(session.window.sessionId); + } } - reload(tabId, cache) { + reload(tabId: number, cache: boolean): Promise<void> { return browser.tabs.reload(tabId, { bypassCache: cache }); } - setPinned(tabId, pinned) { + setPinned(tabId: number, pinned: boolean): Promise<Tab> { return browser.tabs.update(tabId, { pinned }); } - duplicate(id) { + duplicate(id: number): Promise<Tab> { return browser.tabs.duplicate(id); } - getZoom(tabId) { + getZoom(tabId: number): Promise<number> { return browser.tabs.getZoom(tabId); } - setZoom(tabId, factor) { + setZoom(tabId: number, factor: number): Promise<void> { return browser.tabs.setZoom(tabId, factor); } - onSelected(listener) { + onSelected( + listener: (arg: { tabId: number, windowId: number}) => void, + ): void { browser.tabs.onActivated.addListener(listener); } } let tabPresenter = new TabPresenter(); -tabPresenter.onSelected((tab) => { +tabPresenter.onSelected((tab: any) => { let cache = new MemoryStorage(); let lastId = cache.get(CURRENT_SELECTED_KEY); diff --git a/src/background/presenters/WindowPresenter.js b/src/background/presenters/WindowPresenter.ts index a82c4a2..e04f258 100644 --- a/src/background/presenters/WindowPresenter.js +++ b/src/background/presenters/WindowPresenter.ts @@ -1,5 +1,5 @@ export default class WindowPresenter { - create(url) { + create(url: string): Promise<browser.windows.Window> { return browser.windows.create({ url }); } } diff --git a/src/background/repositories/BookmarkRepository.js b/src/background/repositories/BookmarkRepository.ts index 99f7ec4..b4da509 100644 --- a/src/background/repositories/BookmarkRepository.js +++ b/src/background/repositories/BookmarkRepository.ts @@ -1,5 +1,7 @@ export default class BookmarkRepository { - async create(title, url) { + async create( + title: string, url: string + ): Promise<browser.bookmarks.BookmarkTreeNode> { let item = await browser.bookmarks.create({ type: 'bookmark', title, diff --git a/src/background/repositories/BrowserSettingRepository.js b/src/background/repositories/BrowserSettingRepository.js deleted file mode 100644 index a9d2c06..0000000 --- a/src/background/repositories/BrowserSettingRepository.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as urls from '../../shared/urls'; - -export default class BrowserSettingRepository { - async getHomepageUrls() { - let { value } = await browser.browserSettings.homepageOverride.get({}); - return value.split('|').map(urls.normalizeUrl); - } -} diff --git a/src/background/repositories/BrowserSettingRepository.ts b/src/background/repositories/BrowserSettingRepository.ts new file mode 100644 index 0000000..33b35dd --- /dev/null +++ b/src/background/repositories/BrowserSettingRepository.ts @@ -0,0 +1,24 @@ +import * as urls from '../../shared/urls'; + +declare namespace browser.browserSettings.homepageOverride { + + type BrowserSettings = { + value: string; + levelOfControl: LevelOfControlType; + }; + + type LevelOfControlType = + 'not_controllable' | + 'controlled_by_other_extensions' | + 'controllable_by_this_extension' | + 'controlled_by_this_extension'; + + function get(param: object): Promise<BrowserSettings>; +} + +export default class BrowserSettingRepository { + async getHomepageUrls(): Promise<string[]> { + let { value } = await browser.browserSettings.homepageOverride.get({}); + return value.split('|').map(urls.normalizeUrl); + } +} diff --git a/src/background/repositories/CompletionsRepository.js b/src/background/repositories/CompletionsRepository.ts index 1318d36..18af587 100644 --- a/src/background/repositories/CompletionsRepository.js +++ b/src/background/repositories/CompletionsRepository.ts @@ -1,7 +1,13 @@ +type Tab = browser.tabs.Tab; +type BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; + export default class CompletionsRepository { - async queryBookmarks(keywords) { + async queryBookmarks(keywords: string): Promise<BookmarkTreeNode[]> { let items = await browser.bookmarks.search({ query: keywords }); return items.filter((item) => { + if (!item.url) { + return false; + } let url = undefined; try { url = new URL(item.url); @@ -12,17 +18,17 @@ export default class CompletionsRepository { }); } - queryHistories(keywords) { + queryHistories(keywords: string): Promise<browser.history.HistoryItem[]> { return browser.history.search({ text: keywords, startTime: 0, }); } - async queryTabs(keywords, excludePinned) { + async queryTabs(keywords: string, excludePinned: boolean): Promise<Tab[]> { let tabs = await browser.tabs.query({ currentWindow: true }); return tabs.filter((t) => { - return t.url.toLowerCase().includes(keywords.toLowerCase()) || + return t.url && t.url.toLowerCase().includes(keywords.toLowerCase()) || t.title && t.title.toLowerCase().includes(keywords.toLowerCase()); }).filter((t) => { return !(excludePinned && t.pinned); diff --git a/src/background/repositories/FindRepository.js b/src/background/repositories/FindRepository.ts index 74ec914..bf286e6 100644 --- a/src/background/repositories/FindRepository.js +++ b/src/background/repositories/FindRepository.ts @@ -3,15 +3,17 @@ import MemoryStorage from '../infrastructures/MemoryStorage'; const FIND_KEYWORD_KEY = 'find-keyword'; export default class FindRepository { + private cache: MemoryStorage; + constructor() { this.cache = new MemoryStorage(); } - getKeyword() { + getKeyword(): Promise<string> { return Promise.resolve(this.cache.get(FIND_KEYWORD_KEY)); } - setKeyword(keyword) { + setKeyword(keyword: string): Promise<any> { this.cache.set(FIND_KEYWORD_KEY, keyword); return Promise.resolve(); } diff --git a/src/background/repositories/MarkRepository.js b/src/background/repositories/MarkRepository.ts index 282c712..69c85f6 100644 --- a/src/background/repositories/MarkRepository.js +++ b/src/background/repositories/MarkRepository.ts @@ -4,21 +4,23 @@ import GlobalMark from '../domains/GlobalMark'; const MARK_KEY = 'mark'; export default class MarkRepository { + private cache: MemoryStorage; + constructor() { this.cache = new MemoryStorage(); } - getMark(key) { + getMark(key: string): Promise<GlobalMark | undefined> { let marks = this.getOrEmptyMarks(); let data = marks[key]; if (!data) { return Promise.resolve(undefined); } - let mark = new GlobalMark(data.tabId, data.url, data.x, data.y); + let mark = { tabId: data.tabId, url: data.url, x: data.x, y: data.y }; return Promise.resolve(mark); } - setMark(key, mark) { + setMark(key: string, mark: GlobalMark): Promise<any> { let marks = this.getOrEmptyMarks(); marks[key] = { tabId: mark.tabId, url: mark.url, x: mark.x, y: mark.y }; this.cache.set(MARK_KEY, marks); diff --git a/src/background/repositories/PersistentSettingRepository.js b/src/background/repositories/PersistentSettingRepository.ts index 4cab107..ff882a5 100644 --- a/src/background/repositories/PersistentSettingRepository.js +++ b/src/background/repositories/PersistentSettingRepository.ts @@ -1,12 +1,12 @@ -import Setting from '../domains/Setting'; +import SettingData from '../../shared/SettingData'; export default class SettingRepository { - async load() { + async load(): Promise<SettingData | null> { let { settings } = await browser.storage.local.get('settings'); if (!settings) { return null; } - return Setting.deserialize(settings); + return SettingData.valueOf(settings as any); } } diff --git a/src/background/repositories/SettingRepository.js b/src/background/repositories/SettingRepository.js deleted file mode 100644 index c4667a9..0000000 --- a/src/background/repositories/SettingRepository.js +++ /dev/null @@ -1,23 +0,0 @@ -import MemoryStorage from '../infrastructures/MemoryStorage'; - -const CACHED_SETTING_KEY = 'setting'; - -export default class SettingRepository { - constructor() { - this.cache = new MemoryStorage(); - } - - get() { - return Promise.resolve(this.cache.get(CACHED_SETTING_KEY)); - } - - update(value) { - return this.cache.set(CACHED_SETTING_KEY, value); - } - - async setProperty(name, value) { - let current = await this.get(); - current.properties[name] = value; - return this.update(current); - } -} diff --git a/src/background/repositories/SettingRepository.ts b/src/background/repositories/SettingRepository.ts new file mode 100644 index 0000000..eb83a2c --- /dev/null +++ b/src/background/repositories/SettingRepository.ts @@ -0,0 +1,51 @@ +import MemoryStorage from '../infrastructures/MemoryStorage'; +import Settings from '../../shared/Settings'; +import * as PropertyDefs from '../../shared/property-defs'; + +const CACHED_SETTING_KEY = 'setting'; + +export default class SettingRepository { + private cache: MemoryStorage; + + constructor() { + this.cache = new MemoryStorage(); + } + + get(): Promise<Settings> { + return Promise.resolve(this.cache.get(CACHED_SETTING_KEY)); + } + + update(value: Settings): void { + return this.cache.set(CACHED_SETTING_KEY, value); + } + + async setProperty( + name: string, value: string | number | boolean, + ): Promise<void> { + let def = PropertyDefs.defs.find(d => name === d.name); + if (!def) { + throw new Error('unknown property: ' + name); + } + if (typeof value !== def.type) { + throw new TypeError(`property type of ${name} mismatch: ${typeof value}`); + } + let newValue = value; + if (typeof value === 'string' && value === '') { + newValue = def.defaultValue; + } + + let current = await this.get(); + switch (name) { + case 'hintchars': + current.properties.hintchars = newValue as string; + break; + case 'smoothscroll': + current.properties.smoothscroll = newValue as boolean; + break; + case 'complete': + current.properties.complete = newValue as string; + break; + } + return this.update(current); + } +} diff --git a/src/background/repositories/VersionRepository.js b/src/background/repositories/VersionRepository.js deleted file mode 100644 index 4c71d05..0000000 --- a/src/background/repositories/VersionRepository.js +++ /dev/null @@ -1,10 +0,0 @@ -export default class VersionRepository { - async get() { - let { version } = await browser.storage.local.get('version'); - return version; - } - - update(version) { - return browser.storage.local.set({ version }); - } -} diff --git a/src/background/usecases/AddonEnabledUseCase.js b/src/background/usecases/AddonEnabledUseCase.ts index bb2c347..0a6fb03 100644 --- a/src/background/usecases/AddonEnabledUseCase.js +++ b/src/background/usecases/AddonEnabledUseCase.ts @@ -3,10 +3,20 @@ import TabPresenter from '../presenters/TabPresenter'; import ContentMessageClient from '../infrastructures/ContentMessageClient'; export default class AddonEnabledUseCase { + private indicatorPresentor: IndicatorPresenter; + + private tabPresenter: TabPresenter; + + private contentMessageClient: ContentMessageClient; + constructor() { this.indicatorPresentor = new IndicatorPresenter(); - this.indicatorPresentor.onClick(tab => this.onIndicatorClick(tab.id)); + this.indicatorPresentor.onClick((tab) => { + if (tab.id) { + this.onIndicatorClick(tab.id); + } + }); this.tabPresenter = new TabPresenter(); this.tabPresenter.onSelected(info => this.onTabSelected(info.tabId)); @@ -14,15 +24,15 @@ export default class AddonEnabledUseCase { this.contentMessageClient = new ContentMessageClient(); } - indicate(enabled) { + indicate(enabled: boolean): Promise<void> { return this.indicatorPresentor.indicate(enabled); } - onIndicatorClick(tabId) { + onIndicatorClick(tabId: number): Promise<void> { return this.contentMessageClient.toggleAddonEnabled(tabId); } - async onTabSelected(tabId) { + async onTabSelected(tabId: number): Promise<void> { let enabled = await this.contentMessageClient.getAddonEnabled(tabId); return this.indicatorPresentor.indicate(enabled); } diff --git a/src/background/usecases/CommandUseCase.js b/src/background/usecases/CommandUseCase.ts index 9ec46fe..2247d7b 100644 --- a/src/background/usecases/CommandUseCase.js +++ b/src/background/usecases/CommandUseCase.ts @@ -6,9 +6,20 @@ import SettingRepository from '../repositories/SettingRepository'; import BookmarkRepository from '../repositories/BookmarkRepository'; import ConsoleClient from '../infrastructures/ConsoleClient'; import ContentMessageClient from '../infrastructures/ContentMessageClient'; -import * as properties from 'shared/settings/properties'; export default class CommandIndicator { + private tabPresenter: TabPresenter; + + private windowPresenter: WindowPresenter; + + private settingRepository: SettingRepository; + + private bookmarkRepository: BookmarkRepository; + + private consoleClient: ConsoleClient; + + private contentMessageClient: ContentMessageClient; + constructor() { this.tabPresenter = new TabPresenter(); this.windowPresenter = new WindowPresenter(); @@ -19,34 +30,34 @@ export default class CommandIndicator { this.contentMessageClient = new ContentMessageClient(); } - async open(keywords) { + async open(keywords: string): Promise<browser.tabs.Tab> { let url = await this.urlOrSearch(keywords); return this.tabPresenter.open(url); } - async tabopen(keywords) { + async tabopen(keywords: string): Promise<browser.tabs.Tab> { let url = await this.urlOrSearch(keywords); return this.tabPresenter.create(url); } - async winopen(keywords) { + async winopen(keywords: string): Promise<browser.windows.Window> { let url = await this.urlOrSearch(keywords); return this.windowPresenter.create(url); } // eslint-disable-next-line max-statements - async buffer(keywords) { + async buffer(keywords: string): Promise<any> { if (keywords.length === 0) { return; } - if (!isNaN(keywords)) { + if (!isNaN(Number(keywords))) { let tabs = await this.tabPresenter.getAll(); let index = parseInt(keywords, 10) - 1; if (index < 0 || tabs.length <= index) { throw new RangeError(`tab ${index + 1} does not exist`); } - return this.tabPresenter.select(tabs[index].id); + return this.tabPresenter.select(tabs[index].id as number); } else if (keywords.trim() === '%') { // Select current window return; @@ -66,13 +77,13 @@ export default class CommandIndicator { } for (let tab of tabs) { if (tab.index > current.index) { - return this.tabPresenter.select(tab.id); + return this.tabPresenter.select(tab.id as number); } } - return this.tabPresenter.select(tabs[0].id); + return this.tabPresenter.select(tabs[0].id as number); } - async bdelete(force, keywords) { + async bdelete(force: boolean, keywords: string): Promise<any> { let excludePinned = !force; let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); if (tabs.length === 0) { @@ -80,45 +91,45 @@ export default class CommandIndicator { } else if (tabs.length > 1) { throw new Error('More than one match for ' + keywords); } - return this.tabPresenter.remove([tabs[0].id]); + return this.tabPresenter.remove([tabs[0].id as number]); } - async bdeletes(force, keywords) { + async bdeletes(force: boolean, keywords: string): Promise<any> { let excludePinned = !force; let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); - let ids = tabs.map(tab => tab.id); + let ids = tabs.map(tab => tab.id as number); return this.tabPresenter.remove(ids); } - async quit() { + async quit(): Promise<any> { let tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.remove([tab.id]); + return this.tabPresenter.remove([tab.id as number]); } - async quitAll() { + async quitAll(): Promise<any> { let tabs = await this.tabPresenter.getAll(); - let ids = tabs.map(tab => tab.id); + let ids = tabs.map(tab => tab.id as number); this.tabPresenter.remove(ids); } - async addbookmark(title) { + async addbookmark(title: string): Promise<any> { let tab = await this.tabPresenter.getCurrent(); - let item = await this.bookmarkRepository.create(title, tab.url); + let item = await this.bookmarkRepository.create(title, tab.url as string); let message = 'Saved current page: ' + item.url; - return this.consoleClient.showInfo(tab.id, message); + return this.consoleClient.showInfo(tab.id as number, message); } - async set(keywords) { + async set(keywords: string): Promise<any> { if (keywords.length === 0) { return; } - let [name, value] = parsers.parseSetOption(keywords, properties.types); + let [name, value] = parsers.parseSetOption(keywords); await this.settingRepository.setProperty(name, value); return this.contentMessageClient.broadcastSettingsChanged(); } - async urlOrSearch(keywords) { + async urlOrSearch(keywords: string): Promise<any> { let settings = await this.settingRepository.get(); return urls.searchUrl(keywords, settings.search); } diff --git a/src/background/usecases/CompletionsUseCase.js b/src/background/usecases/CompletionsUseCase.ts index 7dc30ac..ae1ceed 100644 --- a/src/background/usecases/CompletionsUseCase.js +++ b/src/background/usecases/CompletionsUseCase.ts @@ -1,23 +1,30 @@ -import CompletionItem from '../domains/CompletionItem'; import CompletionGroup from '../domains/CompletionGroup'; -import Completions from '../domains/Completions'; import CommandDocs from '../domains/CommandDocs'; import CompletionsRepository from '../repositories/CompletionsRepository'; import * as filters from './filters'; import SettingRepository from '../repositories/SettingRepository'; import TabPresenter from '../presenters/TabPresenter'; -import * as properties from '../../shared/settings/properties'; +import * as PropertyDefs from '../../shared/property-defs'; const COMPLETION_ITEM_LIMIT = 10; +type Tab = browser.tabs.Tab; +type HistoryItem = browser.history.HistoryItem; + export default class CompletionsUseCase { + private tabPresenter: TabPresenter; + + private completionsRepository: CompletionsRepository; + + private settingRepository: SettingRepository; + constructor() { this.tabPresenter = new TabPresenter(); this.completionsRepository = new CompletionsRepository(); this.settingRepository = new SettingRepository(); } - queryConsoleCommand(prefix) { + queryConsoleCommand(prefix: string): Promise<CompletionGroup[]> { let keys = Object.keys(CommandDocs); let items = keys .filter(name => name.startsWith(prefix)) @@ -28,48 +35,49 @@ export default class CompletionsUseCase { })); if (items.length === 0) { - return Promise.resolve(Completions.empty()); + return Promise.resolve([]); } - return Promise.resolve( - new Completions([new CompletionGroup('Console Command', items)]) - ); + return Promise.resolve([{ name: 'Console Command', items }]); } - async queryOpen(name, keywords) { + async queryOpen(name: string, keywords: string): Promise<CompletionGroup[]> { let settings = await this.settingRepository.get(); - let groups = []; + let groups: CompletionGroup[] = []; - let complete = settings.properties.complete || properties.defaults.complete; + let complete = settings.properties.complete; for (let c of complete) { if (c === 's') { // eslint-disable-next-line no-await-in-loop let engines = await this.querySearchEngineItems(name, keywords); if (engines.length > 0) { - groups.push(new CompletionGroup('Search Engines', engines)); + groups.push({ name: 'Search Engines', items: engines }); } } else if (c === 'h') { // eslint-disable-next-line no-await-in-loop let histories = await this.queryHistoryItems(name, keywords); if (histories.length > 0) { - groups.push(new CompletionGroup('History', histories)); + groups.push({ name: 'History', items: histories }); } } else if (c === 'b') { // eslint-disable-next-line no-await-in-loop let bookmarks = await this.queryBookmarkItems(name, keywords); if (bookmarks.length > 0) { - groups.push(new CompletionGroup('Bookmarks', bookmarks)); + groups.push({ name: 'Bookmarks', items: bookmarks }); } } } - return new Completions(groups); + return groups; } // eslint-disable-next-line max-statements - async queryBuffer(name, keywords) { + async queryBuffer( + name: string, + keywords: string, + ): Promise<CompletionGroup[]> { let lastId = await this.tabPresenter.getLastSelectedId(); let trimmed = keywords.trim(); - let tabs = []; - if (trimmed.length > 0 && !isNaN(trimmed)) { + let tabs: Tab[] = []; + if (trimmed.length > 0 && !isNaN(Number(trimmed))) { let all = await this.tabPresenter.getAll(); let index = parseInt(trimmed, 10) - 1; if (index >= 0 && index < all.length) { @@ -77,18 +85,18 @@ export default class CompletionsUseCase { } } else if (trimmed === '%') { let all = await this.tabPresenter.getAll(); - let tab = all.find(t => t.active); + let tab = all.find(t => t.active) as Tab; tabs = [tab]; } else if (trimmed === '#') { if (typeof lastId !== 'undefined' && lastId !== null) { let all = await this.tabPresenter.getAll(); - let tab = all.find(t => t.id === lastId); + let tab = all.find(t => t.id === lastId) as Tab; tabs = [tab]; } } else { tabs = await this.completionsRepository.queryTabs(keywords, false); } - const flag = (tab) => { + const flag = (tab: Tab) => { if (tab.active) { return '%'; } else if (tab.id === lastId) { @@ -96,87 +104,90 @@ export default class CompletionsUseCase { } return ' '; }; - let items = tabs.map(tab => new CompletionItem({ + let items = tabs.map(tab => ({ caption: tab.index + 1 + ': ' + flag(tab) + ' ' + tab.title, content: name + ' ' + tab.title, url: tab.url, - icon: tab.favIconUrl + icon: tab.favIconUrl, })); if (items.length === 0) { - return Promise.resolve(Completions.empty()); + return Promise.resolve([]); } - return new Completions([new CompletionGroup('Buffers', items)]); + return [{ name: 'Buffers', items }]; } - queryBdelete(name, keywords) { + queryBdelete(name: string, keywords: string): Promise<CompletionGroup[]> { return this.queryTabs(name, true, keywords); } - queryBdeleteForce(name, keywords) { + queryBdeleteForce( + name: string, keywords: string, + ): Promise<CompletionGroup[]> { return this.queryTabs(name, false, keywords); } - querySet(name, keywords) { - let items = Object.keys(properties.docs).map((key) => { - if (properties.types[key] === 'boolean') { + querySet(name: string, keywords: string): Promise<CompletionGroup[]> { + let items = PropertyDefs.defs.map((def) => { + if (def.type === 'boolean') { return [ - new CompletionItem({ - caption: key, - content: name + ' ' + key, - url: 'Enable ' + properties.docs[key], - }), - new CompletionItem({ - caption: 'no' + key, - content: name + ' no' + key, - url: 'Disable ' + properties.docs[key], - }), + { + caption: def.name, + content: name + ' ' + def.name, + url: 'Enable ' + def.description, + }, { + caption: 'no' + def.name, + content: name + ' no' + def.name, + url: 'Disable ' + def.description + } ]; } return [ - new CompletionItem({ - caption: key, - content: name + ' ' + key, - url: 'Set ' + properties.docs[key], - }) + { + caption: def.name, + content: name + ' ' + def.name, + url: 'Set ' + def.description, + } ]; }); - items = items.reduce((acc, val) => acc.concat(val), []); - items = items.filter((item) => { + let flatten = items.reduce((acc, val) => acc.concat(val), []); + flatten = flatten.filter((item) => { return item.caption.startsWith(keywords); }); - if (items.length === 0) { - return Promise.resolve(Completions.empty()); + if (flatten.length === 0) { + return Promise.resolve([]); } return Promise.resolve( - new Completions([new CompletionGroup('Properties', items)]) + [{ name: 'Properties', items: flatten }], ); } - async queryTabs(name, excludePinned, args) { + async queryTabs( + name: string, excludePinned: boolean, args: string, + ): Promise<CompletionGroup[]> { let tabs = await this.completionsRepository.queryTabs(args, excludePinned); - let items = tabs.map(tab => new CompletionItem({ + let items = tabs.map(tab => ({ caption: tab.title, content: name + ' ' + tab.title, url: tab.url, icon: tab.favIconUrl })); if (items.length === 0) { - return Promise.resolve(Completions.empty()); + return Promise.resolve([]); } - return new Completions([new CompletionGroup('Buffers', items)]); + return [{ name: 'Buffers', items }]; } - async querySearchEngineItems(name, keywords) { + async querySearchEngineItems(name: string, keywords: string) { let settings = await this.settingRepository.get(); let engines = Object.keys(settings.search.engines) .filter(key => key.startsWith(keywords)); - return engines.map(key => new CompletionItem({ + return engines.map(key => ({ caption: key, content: name + ' ' + key, })); } - async queryHistoryItems(name, keywords) { + async queryHistoryItems(name: string, keywords: string) { let histories = await this.completionsRepository.queryHistories(keywords); histories = [histories] .map(filters.filterBlankTitle) @@ -184,19 +195,21 @@ export default class CompletionsUseCase { .map(filters.filterByTailingSlash) .map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT)) .map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0] - .sort((x, y) => x.visitCount < y.visitCount) + .sort((x: HistoryItem, y: HistoryItem): number => { + return Number(x.visitCount) - Number(y.visitCount); + }) .slice(0, COMPLETION_ITEM_LIMIT); - return histories.map(page => new CompletionItem({ + return histories.map(page => ({ caption: page.title, content: name + ' ' + page.url, url: page.url })); } - async queryBookmarkItems(name, keywords) { + async queryBookmarkItems(name: string, keywords: string) { let bookmarks = await this.completionsRepository.queryBookmarks(keywords); return bookmarks.slice(0, COMPLETION_ITEM_LIMIT) - .map(page => new CompletionItem({ + .map(page => ({ caption: page.title, content: name + ' ' + page.url, url: page.url diff --git a/src/background/usecases/ConsoleUseCase.js b/src/background/usecases/ConsoleUseCase.js deleted file mode 100644 index e8e5d4a..0000000 --- a/src/background/usecases/ConsoleUseCase.js +++ /dev/null @@ -1,61 +0,0 @@ -import TabPresenter from '../presenters/TabPresenter'; -import ConsoleClient from '../infrastructures/ConsoleClient'; - -export default class ConsoleUseCase { - constructor() { - this.tabPresenter = new TabPresenter(); - this.consoleClient = new ConsoleClient(); - } - - async showCommand() { - let tab = await this.tabPresenter.getCurrent(); - return this.consoleClient.showCommand(tab.id, ''); - } - - async showOpenCommand(alter) { - let tab = await this.tabPresenter.getCurrent(); - let command = 'open '; - if (alter) { - command += tab.url; - } - return this.consoleClient.showCommand(tab.id, command); - } - - async showTabopenCommand(alter) { - let tab = await this.tabPresenter.getCurrent(); - let command = 'tabopen '; - if (alter) { - command += tab.url; - } - return this.consoleClient.showCommand(tab.id, command); - } - - async showWinopenCommand(alter) { - let tab = await this.tabPresenter.getCurrent(); - let command = 'winopen '; - if (alter) { - command += tab.url; - } - return this.consoleClient.showCommand(tab.id, command); - } - - async showBufferCommand() { - let tab = await this.tabPresenter.getCurrent(); - let command = 'buffer '; - return this.consoleClient.showCommand(tab.id, command); - } - - async showAddbookmarkCommand(alter) { - let tab = await this.tabPresenter.getCurrent(); - let command = 'addbookmark '; - if (alter) { - command += tab.title; - } - return this.consoleClient.showCommand(tab.id, command); - } - - async hideConsole() { - let tab = await this.tabPresenter.getCurrent(); - return this.consoleClient.hide(tab.id); - } -} diff --git a/src/background/usecases/ConsoleUseCase.ts b/src/background/usecases/ConsoleUseCase.ts new file mode 100644 index 0000000..60c0439 --- /dev/null +++ b/src/background/usecases/ConsoleUseCase.ts @@ -0,0 +1,65 @@ +import TabPresenter from '../presenters/TabPresenter'; +import ConsoleClient from '../infrastructures/ConsoleClient'; + +export default class ConsoleUseCase { + private tabPresenter: TabPresenter; + + private consoleClient: ConsoleClient; + + constructor() { + this.tabPresenter = new TabPresenter(); + this.consoleClient = new ConsoleClient(); + } + + async showCommand(): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + return this.consoleClient.showCommand(tab.id as number, ''); + } + + async showOpenCommand(alter: boolean): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + let command = 'open '; + if (alter) { + command += tab.url || ''; + } + return this.consoleClient.showCommand(tab.id as number, command); + } + + async showTabopenCommand(alter: boolean): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + let command = 'tabopen '; + if (alter) { + command += tab.url || ''; + } + return this.consoleClient.showCommand(tab.id as number, command); + } + + async showWinopenCommand(alter: boolean): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + let command = 'winopen '; + if (alter) { + command += tab.url || ''; + } + return this.consoleClient.showCommand(tab.id as number, command); + } + + async showBufferCommand(): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + let command = 'buffer '; + return this.consoleClient.showCommand(tab.id as number, command); + } + + async showAddbookmarkCommand(alter: boolean): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + let command = 'addbookmark '; + if (alter) { + command += tab.title || ''; + } + return this.consoleClient.showCommand(tab.id as number, command); + } + + async hideConsole(): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + return this.consoleClient.hide(tab.id as number); + } +} diff --git a/src/background/usecases/FindUseCase.js b/src/background/usecases/FindUseCase.ts index 224e4a9..d567800 100644 --- a/src/background/usecases/FindUseCase.js +++ b/src/background/usecases/FindUseCase.ts @@ -3,22 +3,28 @@ import TabPresenter from '../presenters/TabPresenter'; import ConsoleClient from '../infrastructures/ConsoleClient'; export default class FindUseCase { + private tabPresenter: TabPresenter; + + private findRepository: FindRepository; + + private consoleClient: ConsoleClient; + constructor() { this.tabPresenter = new TabPresenter(); this.findRepository = new FindRepository(); this.consoleClient = new ConsoleClient(); } - getKeyword() { + getKeyword(): Promise<string> { return this.findRepository.getKeyword(); } - setKeyword(keyword) { + setKeyword(keyword: string): Promise<any> { return this.findRepository.setKeyword(keyword); } - async findStart() { + async findStart(): Promise<any> { let tab = await this.tabPresenter.getCurrent(); - return this.consoleClient.showFind(tab.id); + return this.consoleClient.showFind(tab.id as number); } } diff --git a/src/background/usecases/LinkUseCase.js b/src/background/usecases/LinkUseCase.ts index 89412c5..2f4df7b 100644 --- a/src/background/usecases/LinkUseCase.js +++ b/src/background/usecases/LinkUseCase.ts @@ -1,17 +1,17 @@ -import SettingRepository from '../repositories/SettingRepository'; import TabPresenter from '../presenters/TabPresenter'; export default class LinkUseCase { + private tabPresenter: TabPresenter; + constructor() { - this.settingRepository = new SettingRepository(); this.tabPresenter = new TabPresenter(); } - openToTab(url, tabId) { + openToTab(url: string, tabId: number): Promise<any> { return this.tabPresenter.open(url, tabId); } - openNewTab(url, openerId, background) { + openNewTab(url: string, openerId: number, background: boolean): Promise<any> { return this.tabPresenter.create(url, { openerTabId: openerId, active: !background }); diff --git a/src/background/usecases/MarkUseCase.js b/src/background/usecases/MarkUseCase.ts index 39c796b..e376c55 100644 --- a/src/background/usecases/MarkUseCase.js +++ b/src/background/usecases/MarkUseCase.ts @@ -1,10 +1,17 @@ -import GlobalMark from '../domains/GlobalMark'; import TabPresenter from '../presenters/TabPresenter'; import MarkRepository from '../repositories/MarkRepository'; import ConsoleClient from '../infrastructures/ConsoleClient'; import ContentMessageClient from '../infrastructures/ContentMessageClient'; export default class MarkUseCase { + private tabPresenter: TabPresenter; + + private markRepository: MarkRepository; + + private consoleClient: ConsoleClient; + + private contentMessageClient: ContentMessageClient; + constructor() { this.tabPresenter = new TabPresenter(); this.markRepository = new MarkRepository(); @@ -12,28 +19,28 @@ export default class MarkUseCase { this.contentMessageClient = new ContentMessageClient(); } - async setGlobal(key, x, y) { + async setGlobal(key: string, x: number, y: number): Promise<any> { let tab = await this.tabPresenter.getCurrent(); - let mark = new GlobalMark(tab.id, tab.url, x, y); + let mark = { tabId: tab.id as number, url: tab.url as string, x, y }; return this.markRepository.setMark(key, mark); } - async jumpGlobal(key) { + async jumpGlobal(key: string): Promise<any> { let current = await this.tabPresenter.getCurrent(); let mark = await this.markRepository.getMark(key); if (!mark) { - return this.consoleClient.showError(current.id, 'Mark is not set'); + return this.consoleClient.showError( + current.id as number, 'Mark is not set'); } - - return this.contentMessageClient.scrollTo( - mark.tabId, mark.x, mark.y - ).then(() => { + try { + await this.contentMessageClient.scrollTo(mark.tabId, mark.x, mark.y); return this.tabPresenter.select(mark.tabId); - }).catch(async() => { + } catch (e) { let tab = await this.tabPresenter.create(mark.url); - let mark2 = new GlobalMark(tab.id, mark.url, mark.x, mark.y); - return this.markRepository.setMark(key, mark2); - }); + return this.markRepository.setMark(key, { + tabId: tab.id as number, url: mark.url, x: mark.x, y: mark.y, + }); + } } } diff --git a/src/background/usecases/SettingUseCase.js b/src/background/usecases/SettingUseCase.ts index 9e17408..aa3b534 100644 --- a/src/background/usecases/SettingUseCase.js +++ b/src/background/usecases/SettingUseCase.ts @@ -1,28 +1,31 @@ -import Setting from '../domains/Setting'; // eslint-disable-next-line max-len import PersistentSettingRepository from '../repositories/PersistentSettingRepository'; import SettingRepository from '../repositories/SettingRepository'; +import { DefaultSettingData } from '../../shared/SettingData'; +import Settings from '../../shared/Settings'; export default class SettingUseCase { + private persistentSettingRepository: PersistentSettingRepository; + + private settingRepository: SettingRepository; + constructor() { this.persistentSettingRepository = new PersistentSettingRepository(); this.settingRepository = new SettingRepository(); } - get() { + get(): Promise<Settings> { return this.settingRepository.get(); } - async reload() { - let settings = await this.persistentSettingRepository.load(); - if (!settings) { - settings = Setting.defaultSettings(); + async reload(): Promise<Settings> { + let data = await this.persistentSettingRepository.load(); + if (!data) { + data = DefaultSettingData; } - let value = settings.value(); - + let value = data.toSettings(); this.settingRepository.update(value); - return value; } } diff --git a/src/background/usecases/TabSelectUseCase.js b/src/background/usecases/TabSelectUseCase.ts index 16b3e14..a0b52f0 100644 --- a/src/background/usecases/TabSelectUseCase.js +++ b/src/background/usecases/TabSelectUseCase.ts @@ -1,11 +1,13 @@ import TabPresenter from '../presenters/TabPresenter'; export default class TabSelectUseCase { + private tabPresenter: TabPresenter; + constructor() { this.tabPresenter = new TabPresenter(); } - async selectPrev(count) { + async selectPrev(count: number): Promise<any> { let tabs = await this.tabPresenter.getAll(); if (tabs.length < 2) { return; @@ -15,10 +17,10 @@ export default class TabSelectUseCase { return; } let select = (tab.index - count + tabs.length) % tabs.length; - return this.tabPresenter.select(tabs[select].id); + return this.tabPresenter.select(tabs[select].id as number); } - async selectNext(count) { + async selectNext(count: number): Promise<any> { let tabs = await this.tabPresenter.getAll(); if (tabs.length < 2) { return; @@ -28,24 +30,24 @@ export default class TabSelectUseCase { return; } let select = (tab.index + count) % tabs.length; - return this.tabPresenter.select(tabs[select].id); + return this.tabPresenter.select(tabs[select].id as number); } - async selectFirst() { + async selectFirst(): Promise<any> { let tabs = await this.tabPresenter.getAll(); - return this.tabPresenter.select(tabs[0].id); + return this.tabPresenter.select(tabs[0].id as number); } - async selectLast() { + async selectLast(): Promise<any> { let tabs = await this.tabPresenter.getAll(); - return this.tabPresenter.select(tabs[tabs.length - 1].id); + return this.tabPresenter.select(tabs[tabs.length - 1].id as number); } - async selectPrevSelected() { + async selectPrevSelected(): Promise<any> { let tabId = await this.tabPresenter.getLastSelectedId(); if (tabId === null || typeof tabId === 'undefined') { - return; + return Promise.resolve(); } - this.tabPresenter.select(tabId); + return this.tabPresenter.select(tabId); } } diff --git a/src/background/usecases/TabUseCase.js b/src/background/usecases/TabUseCase.ts index d930842..1615333 100644 --- a/src/background/usecases/TabUseCase.js +++ b/src/background/usecases/TabUseCase.ts @@ -2,20 +2,24 @@ import TabPresenter from '../presenters/TabPresenter'; import BrowserSettingRepository from '../repositories/BrowserSettingRepository'; export default class TabUseCase { + private tabPresenter: TabPresenter; + + private browserSettingRepository: BrowserSettingRepository; + constructor() { this.tabPresenter = new TabPresenter(); this.browserSettingRepository = new BrowserSettingRepository(); } - async close(force) { + async close(force: boolean): Promise<any> { let tab = await this.tabPresenter.getCurrent(); if (!force && tab.pinned) { - return; + return Promise.resolve(); } - return this.tabPresenter.remove([tab.id]); + return this.tabPresenter.remove([tab.id as number]); } - async closeRight() { + async closeRight(): Promise<any> { let tabs = await this.tabPresenter.getAll(); tabs.sort((t1, t2) => t1.index - t2.index); let index = tabs.findIndex(t => t.active); @@ -25,42 +29,42 @@ export default class TabUseCase { for (let i = index + 1; i < tabs.length; ++i) { let tab = tabs[i]; if (!tab.pinned) { - this.tabPresenter.remove(tab.id); + this.tabPresenter.remove([tab.id as number]); } } } - reopen() { + reopen(): Promise<any> { return this.tabPresenter.reopen(); } - async reload(cache) { + async reload(cache: boolean): Promise<any> { let tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.reload(tab.id, cache); + return this.tabPresenter.reload(tab.id as number, cache); } - async setPinned(pinned) { + async setPinned(pinned: boolean): Promise<any> { let tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.setPinned(tab.id, pinned); + return this.tabPresenter.setPinned(tab.id as number, pinned); } - async togglePinned() { + async togglePinned(): Promise<any> { let tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.setPinned(tab.id, !tab.pinned); + return this.tabPresenter.setPinned(tab.id as number, !tab.pinned); } - async duplicate() { + async duplicate(): Promise<any> { let tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.duplicate(tab.id); + return this.tabPresenter.duplicate(tab.id as number); } - async openPageSource() { + async openPageSource(): Promise<any> { let tab = await this.tabPresenter.getCurrent(); let url = 'view-source:' + tab.url; return this.tabPresenter.create(url); } - async openHome(newTab) { + async openHome(newTab: boolean): Promise<any> { let tab = await this.tabPresenter.getCurrent(); let urls = await this.browserSettingRepository.getHomepageUrls(); if (urls.length === 1 && urls[0] === 'about:home') { diff --git a/src/background/usecases/VersionUseCase.js b/src/background/usecases/VersionUseCase.ts index ed5112b..8154eba 100644 --- a/src/background/usecases/VersionUseCase.js +++ b/src/background/usecases/VersionUseCase.ts @@ -1,23 +1,27 @@ -import manifest from '../../../manifest.json'; import TabPresenter from '../presenters/TabPresenter'; import NotifyPresenter from '../presenters/NotifyPresenter'; export default class VersionUseCase { + private tabPresenter: TabPresenter; + + private notifyPresenter: NotifyPresenter; + constructor() { this.tabPresenter = new TabPresenter(); this.notifyPresenter = new NotifyPresenter(); } - notify() { + notify(): Promise<void> { + let manifest = browser.runtime.getManifest(); let title = `Vim Vixen ${manifest.version} has been installed`; let message = 'Click here to see release notes'; let url = this.releaseNoteUrl(manifest.version); - this.notifyPresenter.notify(title, message, () => { + return this.notifyPresenter.notify(title, message, () => { this.tabPresenter.create(url); }); } - releaseNoteUrl(version) { + releaseNoteUrl(version?: string): string { if (version) { return `https://github.com/ueokande/vim-vixen/releases/tag/${version}`; } diff --git a/src/background/usecases/ZoomUseCase.js b/src/background/usecases/ZoomUseCase.js deleted file mode 100644 index 692d6d9..0000000 --- a/src/background/usecases/ZoomUseCase.js +++ /dev/null @@ -1,35 +0,0 @@ -import TabPresenter from '../presenters/TabPresenter'; - -const ZOOM_SETTINGS = [ - 0.33, 0.50, 0.66, 0.75, 0.80, 0.90, 1.00, - 1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00 -]; - -export default class ZoomUseCase { - constructor() { - this.tabPresenter = new TabPresenter(); - } - - async zoomIn(tabId) { - let tab = await this.tabPresenter.getCurrent(); - let current = await this.tabPresenter.getZoom(tab.id); - let factor = ZOOM_SETTINGS.find(f => f > current); - if (factor) { - return this.tabPresenter.setZoom(tabId, factor); - } - } - - async zoomOut(tabId) { - let tab = await this.tabPresenter.getCurrent(); - let current = await this.tabPresenter.getZoom(tab.id); - let factor = [].concat(ZOOM_SETTINGS).reverse().find(f => f < current); - if (factor) { - return this.tabPresenter.setZoom(tabId, factor); - } - } - - zoomNutoral(tabId) { - return this.tabPresenter.setZoom(tabId, 1); - } - -} diff --git a/src/background/usecases/ZoomUseCase.ts b/src/background/usecases/ZoomUseCase.ts new file mode 100644 index 0000000..661c3cd --- /dev/null +++ b/src/background/usecases/ZoomUseCase.ts @@ -0,0 +1,39 @@ +import TabPresenter from '../presenters/TabPresenter'; + +const ZOOM_SETTINGS: number[] = [ + 0.33, 0.50, 0.66, 0.75, 0.80, 0.90, 1.00, + 1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00 +]; + +export default class ZoomUseCase { + private tabPresenter: TabPresenter; + + constructor() { + this.tabPresenter = new TabPresenter(); + } + + async zoomIn(): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + let tabId = tab.id as number; + let current = await this.tabPresenter.getZoom(tabId); + let factor = ZOOM_SETTINGS.find(f => f > current); + if (factor) { + return this.tabPresenter.setZoom(tabId as number, factor); + } + } + + async zoomOut(): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + let tabId = tab.id as number; + let current = await this.tabPresenter.getZoom(tabId); + let factor = ZOOM_SETTINGS.slice(0).reverse().find(f => f < current); + if (factor) { + return this.tabPresenter.setZoom(tabId as number, factor); + } + } + + async zoomNutoral(): Promise<any> { + let tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.setZoom(tab.id as number, 1); + } +} diff --git a/src/background/usecases/filters.js b/src/background/usecases/filters.js deleted file mode 100644 index d057dca..0000000 --- a/src/background/usecases/filters.js +++ /dev/null @@ -1,72 +0,0 @@ -const filterHttp = (items) => { - let httpsHosts = items.map(x => new URL(x.url)) - .filter(x => x.protocol === 'https:') - .map(x => x.host); - httpsHosts = new Set(httpsHosts); - - return items.filter((item) => { - let url = new URL(item.url); - return url.protocol === 'https:' || !httpsHosts.has(url.host); - }); -}; - -const filterBlankTitle = (items) => { - return items.filter(item => item.title && item.title !== ''); -}; - -const filterByTailingSlash = (items) => { - let urls = items.map(item => new URL(item.url)); - let simplePaths = urls - .filter(url => url.hash === '' && url.search === '') - .map(url => url.origin + url.pathname); - simplePaths = new Set(simplePaths); - - return items.filter((item) => { - let url = new URL(item.url); - if (url.hash !== '' || url.search !== '' || - url.pathname.slice(-1) !== '/') { - return true; - } - return !simplePaths.has(url.origin + url.pathname.slice(0, -1)); - }); -}; - -const filterByPathname = (items, min) => { - let hash = {}; - for (let item of items) { - let url = new URL(item.url); - let pathname = url.origin + url.pathname; - if (!hash[pathname]) { - hash[pathname] = item; - } else if (hash[pathname].url.length > item.url.length) { - hash[pathname] = item; - } - } - let filtered = Object.values(hash); - if (filtered.length < min) { - return items; - } - return filtered; -}; - -const filterByOrigin = (items, min) => { - let hash = {}; - for (let item of items) { - let origin = new URL(item.url).origin; - if (!hash[origin]) { - hash[origin] = item; - } else if (hash[origin].url.length > item.url.length) { - hash[origin] = item; - } - } - let filtered = Object.values(hash); - if (filtered.length < min) { - return items; - } - return filtered; -}; - -export { - filterHttp, filterBlankTitle, filterByTailingSlash, - filterByPathname, filterByOrigin -}; diff --git a/src/background/usecases/filters.ts b/src/background/usecases/filters.ts new file mode 100644 index 0000000..84a42fb --- /dev/null +++ b/src/background/usecases/filters.ts @@ -0,0 +1,76 @@ +type Item = browser.history.HistoryItem; + +const filterHttp = (items: Item[]): Item[] => { + let httpsHosts = items.map(x => new URL(x.url as string)) + .filter(x => x.protocol === 'https:') + .map(x => x.host); + let hostsSet = new Set(httpsHosts); + + return items.filter((item: Item) => { + let url = new URL(item.url as string); + return url.protocol === 'https:' || !hostsSet.has(url.host); + }); +}; + +const filterBlankTitle = (items: Item[]): Item[] => { + return items.filter(item => item.title && item.title !== ''); +}; + +const filterByTailingSlash = (items: Item[]): Item[] => { + let urls = items.map(item => new URL(item.url as string)); + let simplePaths = urls + .filter(url => url.hash === '' && url.search === '') + .map(url => url.origin + url.pathname); + let pathsSet = new Set(simplePaths); + + return items.filter((item) => { + let url = new URL(item.url as string); + if (url.hash !== '' || url.search !== '' || + url.pathname.slice(-1) !== '/') { + return true; + } + return !pathsSet.has(url.origin + url.pathname.slice(0, -1)); + }); +}; + +const filterByPathname = (items: Item[], min: number): Item[] => { + let hash: {[key: string]: Item} = {}; + for (let item of items) { + let url = new URL(item.url as string); + let pathname = url.origin + url.pathname; + if (!hash[pathname]) { + hash[pathname] = item; + } else if ((hash[pathname].url as string).length > + (item.url as string).length) { + hash[pathname] = item; + } + } + let filtered = Object.values(hash); + if (filtered.length < min) { + return items; + } + return filtered; +}; + +const filterByOrigin = (items: Item[], min: number): Item[] => { + let hash: {[key: string]: Item} = {}; + for (let item of items) { + let origin = new URL(item.url as string).origin; + if (!hash[origin]) { + hash[origin] = item; + } else if ((hash[origin].url as string).length > + (item.url as string).length) { + hash[origin] = item; + } + } + let filtered = Object.values(hash); + if (filtered.length < min) { + return items; + } + return filtered; +}; + +export { + filterHttp, filterBlankTitle, filterByTailingSlash, + filterByPathname, filterByOrigin +}; diff --git a/src/background/usecases/parsers.js b/src/background/usecases/parsers.js deleted file mode 100644 index 43c8177..0000000 --- a/src/background/usecases/parsers.js +++ /dev/null @@ -1,31 +0,0 @@ -const mustNumber = (v) => { - let num = Number(v); - if (isNaN(num)) { - throw new Error('Not number: ' + v); - } - return num; -}; - -const parseSetOption = (word, types) => { - let [key, value] = word.split('='); - if (value === undefined) { - value = !key.startsWith('no'); - key = value ? key : key.slice(2); - } - let type = types[key]; - if (!type) { - throw new Error('Unknown property: ' + key); - } - if (type === 'boolean' && typeof value !== 'boolean' || - type !== 'boolean' && typeof value === 'boolean') { - throw new Error('Invalid argument: ' + word); - } - - switch (type) { - case 'string': return [key, value]; - case 'number': return [key, mustNumber(value)]; - case 'boolean': return [key, value]; - } -}; - -export { parseSetOption }; diff --git a/src/background/usecases/parsers.ts b/src/background/usecases/parsers.ts new file mode 100644 index 0000000..6135fd8 --- /dev/null +++ b/src/background/usecases/parsers.ts @@ -0,0 +1,36 @@ +import * as PropertyDefs from '../../shared//property-defs'; + +const mustNumber = (v: any): number => { + let num = Number(v); + if (isNaN(num)) { + throw new Error('Not number: ' + v); + } + return num; +}; + +const parseSetOption = ( + args: string, +): any[] => { + let [key, value]: any[] = args.split('='); + if (value === undefined) { + value = !key.startsWith('no'); + key = value ? key : key.slice(2); + } + let def = PropertyDefs.defs.find(d => d.name === key); + if (!def) { + throw new Error('Unknown property: ' + key); + } + if (def.type === 'boolean' && typeof value !== 'boolean' || + def.type !== 'boolean' && typeof value === 'boolean') { + throw new Error('Invalid argument: ' + args); + } + + switch (def.type) { + case 'string': return [key, value]; + case 'number': return [key, mustNumber(value)]; + case 'boolean': return [key, value]; + } + throw new Error('Unknown property type: ' + def.type); +}; + +export { parseSetOption }; |