diff options
Diffstat (limited to 'src/content/usecases/FollowMasterUseCase.ts')
-rw-r--r-- | src/content/usecases/FollowMasterUseCase.ts | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/content/usecases/FollowMasterUseCase.ts b/src/content/usecases/FollowMasterUseCase.ts new file mode 100644 index 0000000..c2c6835 --- /dev/null +++ b/src/content/usecases/FollowMasterUseCase.ts @@ -0,0 +1,150 @@ +import FollowKeyRepository, { FollowKeyRepositoryImpl } + from '../repositories/FollowKeyRepository'; +import FollowMasterRepository, { FollowMasterRepositoryImpl } + from '../repositories/FollowMasterRepository'; +import FollowSlaveClient, { FollowSlaveClientImpl } + from '../client/FollowSlaveClient'; +import HintKeyProducer from '../hint-key-producer'; +import SettingRepository, { SettingRepositoryImpl } + from '../repositories/SettingRepository'; + +export default class FollowMasterUseCase { + private followKeyRepository: FollowKeyRepository; + + private followMasterRepository: FollowMasterRepository; + + private settingRepository: SettingRepository; + + // TODO Make repository + private producer: HintKeyProducer | null; + + constructor({ + followKeyRepository = new FollowKeyRepositoryImpl(), + followMasterRepository = new FollowMasterRepositoryImpl(), + settingRepository = new SettingRepositoryImpl(), + } = {}) { + this.followKeyRepository = followKeyRepository; + this.followMasterRepository = followMasterRepository; + this.settingRepository = settingRepository; + this.producer = null; + } + + startFollow(newTab: boolean, background: boolean): void { + let hintchars = this.settingRepository.get().properties.hintchars; + this.producer = new HintKeyProducer(hintchars); + + this.followKeyRepository.clearKeys(); + this.followMasterRepository.setCurrentFollowMode(newTab, background); + + let viewWidth = window.top.innerWidth; + let viewHeight = window.top.innerHeight; + new FollowSlaveClientImpl(window.top).requestHintCount( + { width: viewWidth, height: viewHeight }, + { x: 0, y: 0 }, + ); + + let frameElements = window.document.querySelectorAll('iframe'); + for (let i = 0; i < frameElements.length; ++i) { + let ele = frameElements[i] as HTMLFrameElement | HTMLIFrameElement; + let { left: frameX, top: frameY } = ele.getBoundingClientRect(); + new FollowSlaveClientImpl(ele.contentWindow!!).requestHintCount( + { width: viewWidth, height: viewHeight }, + { x: frameX, y: frameY }, + ); + } + } + + // eslint-disable-next-line max-statements + createSlaveHints(count: number, sender: Window): void { + let produced = []; + for (let i = 0; i < count; ++i) { + let tag = this.producer!!.produce(); + produced.push(tag); + this.followMasterRepository.addTag(tag); + } + + let doc = window.document; + let viewWidth = window.innerWidth || doc.documentElement.clientWidth; + let viewHeight = window.innerHeight || doc.documentElement.clientHeight; + let pos = { x: 0, y: 0 }; + if (sender !== window) { + let frameElements = window.document.querySelectorAll('iframe'); + let ele = Array.from(frameElements).find(e => e.contentWindow === sender); + if (!ele) { + // elements of the sender is gone + return; + } + let { left: frameX, top: frameY } = ele.getBoundingClientRect(); + pos = { x: frameX, y: frameY }; + } + new FollowSlaveClientImpl(sender).createHints( + { width: viewWidth, height: viewHeight }, + pos, + produced, + ); + } + + cancelFollow(): void { + this.followMasterRepository.clearTags(); + this.broadcastToSlaves((client) => { + client.clearHints(); + }); + } + + filter(prefix: string): void { + this.broadcastToSlaves((client) => { + client.filterHints(prefix); + }); + } + + activate(tag: string): void { + this.followMasterRepository.clearTags(); + + let newTab = this.followMasterRepository.getCurrentNewTabMode(); + let background = this.followMasterRepository.getCurrentBackgroundMode(); + this.broadcastToSlaves((client) => { + client.activateIfExists(tag, newTab, background); + client.clearHints(); + }); + } + + enqueue(key: string): void { + switch (key) { + case 'Enter': + this.activate(this.getCurrentTag()); + return; + case 'Esc': + this.cancelFollow(); + return; + case 'Backspace': + case 'Delete': + this.followKeyRepository.popKey(); + this.filter(this.getCurrentTag()); + return; + } + + this.followKeyRepository.pushKey(key); + + let tag = this.getCurrentTag(); + let matched = this.followMasterRepository.getTagsByPrefix(tag); + if (matched.length === 0) { + this.cancelFollow(); + } else if (matched.length === 1) { + this.activate(tag); + } else { + this.filter(tag); + } + } + + private broadcastToSlaves(handler: (client: FollowSlaveClient) => void) { + let allFrames = [window.self].concat(Array.from(window.frames as any)); + let clients = allFrames.map(frame => new FollowSlaveClientImpl(frame)); + for (let client of clients) { + handler(client); + } + } + + private getCurrentTag(): string { + return this.followKeyRepository.getKeys().join(''); + } +} |