Repeat last operation
This commit is contained in:
parent
03370301a7
commit
ccbe08cf66
7 changed files with 110 additions and 7 deletions
|
@ -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);
|
||||
}
|
||||
|
|
22
src/background/repositories/RepeatRepository.ts
Normal file
22
src/background/repositories/RepeatRepository.ts
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
49
src/background/usecases/RepeatUseCase.ts
Normal file
49
src/background/usecases/RepeatUseCase.ts
Normal file
|
@ -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);
|
||||
|
|
Reference in a new issue