import { injectable, inject } from 'tsyringe';
import FollowKeyRepository from '../repositories/FollowKeyRepository';
import FollowMasterRepository from '../repositories/FollowMasterRepository';
import FollowSlaveClient from '../client/FollowSlaveClient';
import FollowSlaveClientFactory from '../client/FollowSlaveClientFactory';
import SettingRepository from '../repositories/SettingRepository';
import HintKeyProducer from './HintKeyProducer';
@injectable()
export default class FollowMasterUseCase {
// TODO Make repository
private producer: HintKeyProducer | null;
constructor(
@inject('FollowKeyRepository')
private followKeyRepository: FollowKeyRepository,
@inject('FollowMasterRepository')
private followMasterRepository: FollowMasterRepository,
@inject('SettingRepository')
private settingRepository: SettingRepository,
@inject('FollowSlaveClientFactory')
private followSlaveClientFactory: FollowSlaveClientFactory,
) {
this.producer = null;
}
startFollow(newTab: boolean, background: boolean): void {
const hintchars = this.settingRepository.get().properties.hintchars;
this.producer = new HintKeyProducer(hintchars);
this.followKeyRepository.clearKeys();
this.followMasterRepository.setCurrentFollowMode(newTab, background);
const viewWidth = window.top.innerWidth;
const viewHeight = window.top.innerHeight;
this.followSlaveClientFactory.create(window.top).requestHintCount(
{ width: viewWidth, height: viewHeight },
{ x: 0, y: 0 },
);
const frameElements = window.document.querySelectorAll('iframe');
for (let i = 0; i < frameElements.length; ++i) {
const ele = frameElements[i] as HTMLFrameElement | HTMLIFrameElement;
const { left: frameX, top: frameY } = ele.getBoundingClientRect();
const client = this.followSlaveClientFactory.create(ele.contentWindow!!);
client.requestHintCount(
{ width: viewWidth, height: viewHeight },
{ x: frameX, y: frameY },
);
}
}
// eslint-disable-next-line max-statements
createSlaveHints(count: number, sender: Window): void {
const produced = [];
for (let i = 0; i < count; ++i) {
const tag = this.producer!!.produce();
produced.push(tag);
this.followMasterRepository.addTag(tag);
}
const doc = window.document;
const viewWidth = window.innerWidth || doc.documentElement.clientWidth;
const viewHeight = window.innerHeight || doc.documentElement.clientHeight;
let pos = { x: 0, y: 0 };
if (sender !== window) {
const frameElements = window.document.querySelectorAll('iframe');
const ele = Array.from(frameElements).find(e => e.contentWindow === sender);
if (!ele) {
// elements of the sender is gone
return;
}
const { left: frameX, top: frameY } = ele.getBoundingClientRect();
pos = { x: frameX, y: frameY };
}
const client = this.followSlaveClientFactory.create(sender);
client.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();
const newTab = this.followMasterRepository.getCurrentNewTabMode();
const 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);
const tag = this.getCurrentTag();
const 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) {
const allFrames = [window.self].concat(Array.from(window.frames as any));
const clients = allFrames.map(w => this.followSlaveClientFactory.create(w));
for (const client of clients) {
handler(client);
}
}
private getCurrentTag(): string {
return this.followKeyRepository.getKeys().join('');
}
}