Repeat last operation

jh-changes
Shin'ya Ueoka 5 years ago
parent 03370301a7
commit ccbe08cf66
  1. 19
      src/background/controllers/OperationController.ts
  2. 22
      src/background/repositories/RepeatRepository.ts
  3. 49
      src/background/usecases/RepeatUseCase.ts
  4. 8
      src/content/client/OperationClient.ts
  5. 6
      src/content/controllers/KeymapController.ts
  6. 2
      src/content/di.ts
  7. 11
      src/shared/operations.ts

@ -6,6 +6,7 @@ import TabUseCase from '../usecases/TabUseCase';
import TabSelectUseCase from '../usecases/TabSelectUseCase';
import ZoomUseCase from '../usecases/ZoomUseCase';
import NavigateUseCase from '../usecases/NavigateUseCase';
import RepeatUseCase from '../usecases/RepeatUseCase';
@injectable()
export default class OperationController {
@ -16,11 +17,19 @@ export default class OperationController {
private tabSelectUseCase: TabSelectUseCase,
private zoomUseCase: ZoomUseCase,
private navigateUseCase: NavigateUseCase,
private repeatUseCase: RepeatUseCase,
) {
}
async exec(op: operations.Operation): Promise<any> {
await this.doOperation(op);
if (this.repeatUseCase.isRepeatable(op)) {
this.repeatUseCase.storeLastOperation(op);
}
}
// eslint-disable-next-line complexity, max-lines-per-function
exec(operation: operations.Operation): Promise<any> {
doOperation(operation: operations.Operation): Promise<any> {
switch (operation.type) {
case operations.TAB_CLOSE:
return this.tabUseCase.close(false);
@ -88,6 +97,14 @@ export default class OperationController {
return this.navigateUseCase.openParent();
case operations.NAVIGATE_ROOT:
return this.navigateUseCase.openRoot();
case operations.REPEAT_LAST:
{
let last = this.repeatUseCase.getLastOperation();
if (typeof last !== 'undefined') {
return this.doOperation(last);
}
return Promise.resolve();
}
}
throw new Error('unknown operation: ' + operation.type);
}

@ -0,0 +1,22 @@
import { injectable } from 'tsyringe';
import { Operation } from '../../shared/operations';
import MemoryStorage from '../infrastructures/MemoryStorage';
const REPEAT_KEY = 'repeat';
@injectable()
export default class RepeatRepository {
private cache: MemoryStorage;
constructor() {
this.cache = new MemoryStorage();
}
getLastOperation(): Operation | undefined {
return this.cache.get(REPEAT_KEY);
}
setLastOperation(op: Operation): void {
this.cache.set(REPEAT_KEY, op);
}
}

@ -0,0 +1,49 @@
import { injectable } from 'tsyringe';
import * as operations from '../../shared/operations';
import RepeatRepository from '../repositories/RepeatRepository';
type Operation = operations.Operation;
@injectable()
export default class RepeatUseCase {
constructor(
private repeatRepository: RepeatRepository,
) {
}
storeLastOperation(op: Operation): void {
this.repeatRepository.setLastOperation(op);
}
getLastOperation(): operations.Operation | undefined {
return this.repeatRepository.getLastOperation();
}
// eslint-disable-next-line complexity
isRepeatable(op: Operation): boolean {
switch (op.type) {
case operations.NAVIGATE_HISTORY_PREV:
case operations.NAVIGATE_HISTORY_NEXT:
case operations.NAVIGATE_LINK_PREV:
case operations.NAVIGATE_LINK_NEXT:
case operations.NAVIGATE_PARENT:
case operations.NAVIGATE_ROOT:
case operations.PAGE_SOURCE:
case operations.PAGE_HOME:
case operations.TAB_CLOSE:
case operations.TAB_CLOSE_FORCE:
case operations.TAB_CLOSE_RIGHT:
case operations.TAB_REOPEN:
case operations.TAB_RELOAD:
case operations.TAB_PIN:
case operations.TAB_UNPIN:
case operations.TAB_TOGGLE_PINNED:
case operations.TAB_DUPLICATE:
case operations.ZOOM_IN:
case operations.ZOOM_OUT:
case operations.ZOOM_NEUTRAL:
return true;
}
return false;
}
}

@ -1,9 +1,11 @@
import { injectable } from 'tsyringe';
import * as operations from '../../shared/operations';
import * as messages from '../../shared/messages';
@injectable()
export default class BackgroundClient {
export default interface OperationClient {
execBackgroundOp(op: operations.Operation): Promise<void>;
}
export class OperationClientImpl implements OperationClient {
execBackgroundOp(op: operations.Operation): Promise<void> {
return browser.runtime.sendMessage({
type: messages.BACKGROUND_OPERATION,

@ -6,7 +6,7 @@ import FindSlaveUseCase from '../usecases/FindSlaveUseCase';
import ScrollUseCase from '../usecases/ScrollUseCase';
import FocusUseCase from '../usecases/FocusUseCase';
import ClipboardUseCase from '../usecases/ClipboardUseCase';
import BackgroundClient from '../client/BackgroundClient';
import OperationClient from '../client/OperationClient';
import MarkKeyyUseCase from '../usecases/MarkKeyUseCase';
import FollowMasterClient from '../client/FollowMasterClient';
import Key from '../domains/Key';
@ -20,9 +20,11 @@ export default class KeymapController {
private scrollUseCase: ScrollUseCase,
private focusUseCase: FocusUseCase,
private clipbaordUseCase: ClipboardUseCase,
private backgroundClient: BackgroundClient,
private markKeyUseCase: MarkKeyyUseCase,
@inject('OperationClient')
private backgroundClient: OperationClient,
@inject('FollowMasterClient')
private followMasterClient: FollowMasterClient,
) {

@ -21,6 +21,7 @@ import { MarkClientImpl } from './client/MarkClient';
import { MarkKeyRepositoryImpl } from './repositories/MarkKeyRepository';
import { MarkRepositoryImpl } from './repositories/MarkRepository';
import { NavigationPresenterImpl } from './presenters/NavigationPresenter';
import { OperationClientImpl } from './client/OperationClient';
import { ScrollPresenterImpl } from './presenters/ScrollPresenter';
import { SettingClientImpl } from './client/SettingClient';
import { SettingRepositoryImpl } from './repositories/SettingRepository';
@ -48,6 +49,7 @@ container.register('MarkClient', { useClass: MarkClientImpl });
container.register('MarkKeyRepository', { useClass: MarkKeyRepositoryImpl });
container.register('MarkRepository', { useClass: MarkRepositoryImpl });
container.register('NavigationPresenter', { useClass: NavigationPresenterImpl });
container.register('OperationClient', { useClass: OperationClientImpl });
container.register('ScrollPresenter', { useClass: ScrollPresenterImpl });
container.register('SettingClient', { useClass: SettingClientImpl });
container.register('SettingRepository', { useClass: SettingRepositoryImpl });

@ -75,6 +75,9 @@ export const FIND_PREV = 'find.prev';
export const MARK_SET_PREFIX = 'mark.set.prefix';
export const MARK_JUMP_PREFIX = 'mark.jump.prefix';
// Repeat
export const REPEAT_LAST = 'repeat.last';
export interface CancelOperation {
type: typeof CANCEL;
}
@ -291,6 +294,10 @@ export interface MarkJumpPrefixOperation {
type: typeof MARK_JUMP_PREFIX;
}
export interface RepeatLastOperation {
type: typeof REPEAT_LAST;
}
export type Operation =
CancelOperation |
AddonEnableOperation |
@ -342,7 +349,8 @@ export type Operation =
FindNextOperation |
FindPrevOperation |
MarkSetPrefixOperation |
MarkJumpPrefixOperation;
MarkJumpPrefixOperation |
RepeatLastOperation;
const assertOptionalBoolean = (obj: any, name: string) => {
if (Object.prototype.hasOwnProperty.call(obj, name) &&
@ -441,6 +449,7 @@ export const valueOf = (o: any): Operation => {
case FIND_PREV:
case MARK_SET_PREFIX:
case MARK_JUMP_PREFIX:
case REPEAT_LAST:
return { type: o.type };
}
throw new TypeError('unknown operation type: ' + o.type);