refactor event handling to consume base class for lifecycling
This commit is contained in:
@@ -4,12 +4,12 @@ import { CategoryCollectionState } from './State/CategoryCollectionState';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { Signal } from '@/infrastructure/Events/Signal';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
|
||||
type StateMachine = Map<OperatingSystem, ICategoryCollectionState>;
|
||||
|
||||
export class ApplicationContext implements IApplicationContext {
|
||||
public readonly contextChanged = new Signal<IApplicationContextChangedEvent>();
|
||||
public readonly contextChanged = new EventSource<IApplicationContextChangedEvent>();
|
||||
public collection: ICategoryCollection;
|
||||
public currentOs: OperatingSystem;
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ICategoryCollectionState } from './State/ICategoryCollectionState';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { ISignal } from '@/infrastructure/Events/ISignal';
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
|
||||
export interface IApplicationContext {
|
||||
readonly app: IApplication;
|
||||
readonly state: ICategoryCollectionState;
|
||||
readonly contextChanged: ISignal<IApplicationContextChangedEvent>;
|
||||
readonly contextChanged: IEventSource<IApplicationContextChangedEvent>;
|
||||
changeContext(os: OperatingSystem): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import { ICodeChangedEvent } from './Event/ICodeChangedEvent';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { IUserSelection } from '@/application/Context/State/Selection/IUserSelection';
|
||||
import { UserScriptGenerator } from './Generation/UserScriptGenerator';
|
||||
import { Signal } from '@/infrastructure/Events/Signal';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import { IApplicationCode } from './IApplicationCode';
|
||||
import { IUserScriptGenerator } from './Generation/IUserScriptGenerator';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
|
||||
export class ApplicationCode implements IApplicationCode {
|
||||
public readonly changed = new Signal<ICodeChangedEvent>();
|
||||
public readonly changed = new EventSource<ICodeChangedEvent>();
|
||||
public current: string;
|
||||
|
||||
private scriptPositions = new Map<SelectedScript, CodePosition>();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ICodeChangedEvent } from './Event/ICodeChangedEvent';
|
||||
import { ISignal } from '@/infrastructure/Events/ISignal';
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
|
||||
export interface IApplicationCode {
|
||||
readonly changed: ISignal<ICodeChangedEvent>;
|
||||
readonly changed: IEventSource<ICodeChangedEvent>;
|
||||
readonly current: string;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ISignal } from '@/infrastructure/Events/ISignal';
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import { IFilterResult } from './IFilterResult';
|
||||
|
||||
export interface IUserFilter {
|
||||
readonly currentFilter: IFilterResult | undefined;
|
||||
readonly filtered: ISignal<IFilterResult>;
|
||||
readonly filterRemoved: ISignal<void>;
|
||||
readonly filtered: IEventSource<IFilterResult>;
|
||||
readonly filterRemoved: IEventSource<void>;
|
||||
setFilter(filter: string): void;
|
||||
removeFilter(): void;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ import { IScript } from '@/domain/IScript';
|
||||
import { FilterResult } from './FilterResult';
|
||||
import { IFilterResult } from './IFilterResult';
|
||||
import { IUserFilter } from './IUserFilter';
|
||||
import { Signal } from '@/infrastructure/Events/Signal';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
|
||||
export class UserFilter implements IUserFilter {
|
||||
public readonly filtered = new Signal<IFilterResult>();
|
||||
public readonly filterRemoved = new Signal<void>();
|
||||
public readonly filtered = new EventSource<IFilterResult>();
|
||||
public readonly filterRemoved = new EventSource<void>();
|
||||
public currentFilter: IFilterResult | undefined;
|
||||
|
||||
constructor(private collection: ICategoryCollection) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { SelectedScript } from './SelectedScript';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ICategory } from '@/domain/ICategory';
|
||||
import { ISignal } from '@/infrastructure/Events/ISignal';
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
|
||||
export interface IUserSelection {
|
||||
readonly changed: ISignal<ReadonlyArray<SelectedScript>>;
|
||||
readonly changed: IEventSource<ReadonlyArray<SelectedScript>>;
|
||||
readonly selectedScripts: ReadonlyArray<SelectedScript>;
|
||||
readonly totalSelected: number;
|
||||
areAllSelected(category: ICategory): boolean;
|
||||
|
||||
@@ -2,13 +2,13 @@ import { SelectedScript } from './SelectedScript';
|
||||
import { IUserSelection } from './IUserSelection';
|
||||
import { InMemoryRepository } from '@/infrastructure/Repository/InMemoryRepository';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { Signal } from '@/infrastructure/Events/Signal';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import { IRepository } from '@/infrastructure/Repository/IRepository';
|
||||
import { ICategory } from '@/domain/ICategory';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
|
||||
export class UserSelection implements IUserSelection {
|
||||
public readonly changed = new Signal<ReadonlyArray<SelectedScript>>();
|
||||
public readonly changed = new EventSource<ReadonlyArray<SelectedScript>>();
|
||||
private readonly scripts: IRepository<string, SelectedScript>;
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { EventHandler, ISignal } from './ISignal';
|
||||
import { IEventSubscription } from './ISubscription';
|
||||
import { EventHandler, IEventSource, IEventSubscription } from './IEventSource';
|
||||
|
||||
export class Signal<T> implements ISignal<T> {
|
||||
export class EventSource<T> implements IEventSource<T> {
|
||||
private handlers = new Map<number, EventHandler<T>>();
|
||||
|
||||
public on(handler: EventHandler<T>): IEventSubscription {
|
||||
12
src/infrastructure/Events/EventSubscriptionCollection.ts
Normal file
12
src/infrastructure/Events/EventSubscriptionCollection.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { IEventSubscription } from './IEventSource';
|
||||
|
||||
export class EventSubscriptionCollection {
|
||||
private readonly subscriptions = new Array<IEventSubscription>();
|
||||
public register(...subscriptions: IEventSubscription[]) {
|
||||
this.subscriptions.push(...subscriptions);
|
||||
}
|
||||
public unsubscribeAll() {
|
||||
this.subscriptions.forEach((listener) => listener.unsubscribe());
|
||||
this.subscriptions.splice(0, this.subscriptions.length);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
import { IEventSubscription } from './ISubscription';
|
||||
export interface ISignal<T> {
|
||||
export interface IEventSource<T> {
|
||||
on(handler: EventHandler<T>): IEventSubscription;
|
||||
}
|
||||
|
||||
export interface IEventSubscription {
|
||||
unsubscribe(): void;
|
||||
}
|
||||
|
||||
export type EventHandler<T> = (data: T) => void;
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export interface IEventSubscription {
|
||||
unsubscribe(): void;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Signal } from '../Events/Signal';
|
||||
import { EventSource } from '../Events/EventSource';
|
||||
|
||||
export class AsyncLazy<T> {
|
||||
private valueCreated = new Signal();
|
||||
private valueCreated = new EventSource();
|
||||
private isValueCreated = false;
|
||||
private isCreatingValue = false;
|
||||
private value: T | undefined;
|
||||
@@ -15,7 +15,7 @@ export class AsyncLazy<T> {
|
||||
public async getValueAsync(): Promise<T> {
|
||||
// If value is already created, return the value directly
|
||||
if (this.isValueCreated) {
|
||||
return Promise.resolve(this.value as T);
|
||||
return Promise.resolve(this.value);
|
||||
}
|
||||
// If value is being created, wait until the value is created and then return it.
|
||||
if (this.isCreatingValue) {
|
||||
|
||||
@@ -35,9 +35,7 @@ import MacOsInstructions from './MacOsInstructions.vue';
|
||||
import { Environment } from '@/application/Environment/Environment';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { IApplicationCode } from '@/application/Context/State/Code/IApplicationCode';
|
||||
import { IEventSubscription } from '@/infrastructure/Events/ISubscription';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
|
||||
@@ -55,8 +53,6 @@ export default class TheCodeButtons extends StatefulVue {
|
||||
public isMacOsCollection = false;
|
||||
public fileName = '';
|
||||
|
||||
private codeListener: IEventSubscription;
|
||||
|
||||
public async copyCodeAsync() {
|
||||
const code = await this.getCurrentCodeAsync();
|
||||
Clipboard.copyText(code.current);
|
||||
@@ -68,13 +64,8 @@ export default class TheCodeButtons extends StatefulVue {
|
||||
this.$modal.show(this.macOsModalName);
|
||||
}
|
||||
}
|
||||
public destroyed() {
|
||||
if (this.codeListener) {
|
||||
this.codeListener.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
protected initialize(app: IApplication): void {
|
||||
protected initialize(): void {
|
||||
return;
|
||||
}
|
||||
protected handleCollectionState(newState: ICategoryCollectionState): void {
|
||||
@@ -90,12 +81,10 @@ export default class TheCodeButtons extends StatefulVue {
|
||||
}
|
||||
private async react(code: IApplicationCode) {
|
||||
this.hasCode = code.current && code.current.length > 0;
|
||||
if (this.codeListener) {
|
||||
this.codeListener.unsubscribe();
|
||||
}
|
||||
this.codeListener = code.changed.on((newCode) => {
|
||||
this.events.unsubscribeAll();
|
||||
this.events.register(code.changed.on((newCode) => {
|
||||
this.hasCode = newCode && newCode.code.length > 0;
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
import { Component, Prop, Watch, Emit } from 'vue-property-decorator';
|
||||
import ScriptsTree from '@/presentation/Scripts/ScriptsTree/ScriptsTree.vue';
|
||||
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||
import { IEventSubscription } from '@/infrastructure/Events/ISubscription';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@@ -50,17 +49,11 @@ export default class CardListItem extends StatefulVue {
|
||||
public isAnyChildSelected = false;
|
||||
public areAllChildrenSelected = false;
|
||||
|
||||
private selectionChangedListener: IEventSubscription;
|
||||
|
||||
public async mounted() {
|
||||
this.updateStateAsync(this.categoryId);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.selectionChangedListener = context.state.selection.changed.on(() => this.updateStateAsync(this.categoryId));
|
||||
}
|
||||
public destroyed() {
|
||||
if (this.selectionChangedListener) {
|
||||
this.selectionChangedListener.unsubscribe();
|
||||
}
|
||||
this.events.register(context.state.selection.changed.on(
|
||||
() => this.updateSelectionIndicatorsAsync(this.categoryId)));
|
||||
await this.updateStateAsync(this.categoryId);
|
||||
}
|
||||
@Emit('selected')
|
||||
public onSelected(isExpanded: boolean) {
|
||||
@@ -81,19 +74,24 @@ export default class CardListItem extends StatefulVue {
|
||||
@Watch('categoryId')
|
||||
public async updateStateAsync(value: |number) {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const category = !value ? undefined : context.state.collection.findCategory(this.categoryId);
|
||||
const category = !value ? undefined : context.state.collection.findCategory(value);
|
||||
this.cardTitle = category ? category.name : undefined;
|
||||
const currentSelection = context.state.selection;
|
||||
this.isAnyChildSelected = category ? currentSelection.isAnySelected(category) : false;
|
||||
this.areAllChildrenSelected = category ? currentSelection.areAllSelected(category) : false;
|
||||
await this.updateSelectionIndicatorsAsync(value);
|
||||
}
|
||||
protected initialize(): void {
|
||||
return;
|
||||
}
|
||||
protected handleCollectionState(): void {
|
||||
// No need, as categoryId will be updated instead
|
||||
return;
|
||||
}
|
||||
|
||||
private async updateSelectionIndicatorsAsync(categoryId: number) {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const selection = context.state.selection;
|
||||
const category = context.state.collection.findCategory(categoryId);
|
||||
this.isAnyChildSelected = category ? selection.isAnySelected(category) : false;
|
||||
this.areAllChildrenSelected = category ? selection.areAllSelected(category) : false;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -27,8 +27,6 @@ import SelectableTree from './SelectableTree/SelectableTree.vue';
|
||||
import { INode, NodeType } from './SelectableTree/Node/INode';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { INodeSelectedEvent } from './SelectableTree/INodeSelectedEvent';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { IEventSubscription } from '@/infrastructure/Events/ISubscription';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@@ -43,7 +41,6 @@ export default class ScriptsTree extends StatefulVue {
|
||||
public filterText?: string = null;
|
||||
|
||||
private filtered?: IFilterResult;
|
||||
private listeners = new Array<IEventSubscription>();
|
||||
|
||||
public async toggleNodeSelectionAsync(event: INodeSelectedEvent) {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
@@ -75,11 +72,8 @@ export default class ScriptsTree extends StatefulVue {
|
||||
|| this.filtered.categoryMatches.some(
|
||||
(category: ICategory) => node.id === getCategoryNodeId(category));
|
||||
}
|
||||
public destroyed() {
|
||||
this.unsubscribeAll();
|
||||
}
|
||||
|
||||
protected initialize(app: IApplication): void {
|
||||
protected initialize(): void {
|
||||
return;
|
||||
}
|
||||
protected async handleCollectionState(newState: ICategoryCollectionState) {
|
||||
@@ -87,19 +81,18 @@ export default class ScriptsTree extends StatefulVue {
|
||||
if (!this.categoryId) {
|
||||
this.nodes = parseAllCategories(newState.collection);
|
||||
}
|
||||
this.unsubscribeAll();
|
||||
this.subscribe(newState);
|
||||
this.events.unsubscribeAll();
|
||||
this.subscribeState(newState);
|
||||
}
|
||||
|
||||
private subscribe(state: ICategoryCollectionState) {
|
||||
this.listeners.push(state.selection.changed.on(this.handleSelectionChanged));
|
||||
this.listeners.push(state.filter.filterRemoved.on(this.handleFilterRemoved));
|
||||
this.listeners.push(state.filter.filtered.on(this.handleFiltered));
|
||||
}
|
||||
private unsubscribeAll() {
|
||||
this.listeners.forEach((listener) => listener.unsubscribe());
|
||||
this.listeners.splice(0, this.listeners.length);
|
||||
private subscribeState(state: ICategoryCollectionState) {
|
||||
this.events.register(
|
||||
state.selection.changed.on(this.handleSelectionChanged),
|
||||
state.filter.filterRemoved.on(this.handleFilterRemoved),
|
||||
state.filter.filtered.on(this.handleFiltered),
|
||||
);
|
||||
}
|
||||
|
||||
private setCurrentFilter(currentFilter: IFilterResult | undefined) {
|
||||
if (!currentFilter) {
|
||||
this.handleFilterRemoved();
|
||||
|
||||
@@ -13,49 +13,43 @@
|
||||
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Watch } from 'vue-property-decorator';
|
||||
import { IReverter } from './Reverter/IReverter';
|
||||
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||
import { INode } from './INode';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { getReverter } from './Reverter/ReverterFactory';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { IEventSubscription } from '@/infrastructure/Events/ISubscription';
|
||||
import { Component, Prop, Watch } from 'vue-property-decorator';
|
||||
import { IReverter } from './Reverter/IReverter';
|
||||
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||
import { INode } from './INode';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { getReverter } from './Reverter/ReverterFactory';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
|
||||
@Component
|
||||
export default class RevertToggle extends StatefulVue {
|
||||
@Prop() public node: INode;
|
||||
public isReverted = false;
|
||||
@Component
|
||||
export default class RevertToggle extends StatefulVue {
|
||||
@Prop() public node: INode;
|
||||
public isReverted = false;
|
||||
|
||||
private handler: IReverter;
|
||||
private selectionChangeListener: IEventSubscription;
|
||||
private handler: IReverter;
|
||||
|
||||
@Watch('node', {immediate: true}) public async onNodeChangedAsync(node: INode) {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.handler = getReverter(node, context.state.collection);
|
||||
}
|
||||
public async onRevertToggledAsync() {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.handler.selectWithRevertState(this.isReverted, context.state.selection);
|
||||
}
|
||||
|
||||
protected initialize(app: IApplication): void {
|
||||
return;
|
||||
}
|
||||
protected handleCollectionState(newState: ICategoryCollectionState, oldState: ICategoryCollectionState): void {
|
||||
this.updateStatus(newState.selection.selectedScripts);
|
||||
if (this.selectionChangeListener) {
|
||||
this.selectionChangeListener.unsubscribe();
|
||||
}
|
||||
this.selectionChangeListener = newState.selection.changed.on(
|
||||
(scripts) => this.updateStatus(scripts));
|
||||
}
|
||||
|
||||
private updateStatus(scripts: ReadonlyArray<SelectedScript>) {
|
||||
this.isReverted = this.handler.getState(scripts);
|
||||
}
|
||||
@Watch('node', {immediate: true}) public async onNodeChangedAsync(node: INode) {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.handler = getReverter(node, context.state.collection);
|
||||
}
|
||||
public async onRevertToggledAsync() {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.handler.selectWithRevertState(this.isReverted, context.state.selection);
|
||||
}
|
||||
|
||||
protected initialize(): void {
|
||||
return;
|
||||
}
|
||||
protected handleCollectionState(newState: ICategoryCollectionState): void {
|
||||
this.updateStatus(newState.selection.selectedScripts);
|
||||
this.events.unsubscribeAll();
|
||||
this.events.register(newState.selection.changed.on((scripts) => this.updateStatus(scripts)));
|
||||
}
|
||||
|
||||
private updateStatus(scripts: ReadonlyArray<SelectedScript>) {
|
||||
this.isReverted = this.handler.getState(scripts);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ import { Grouping } from './Grouping/Grouping';
|
||||
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { IEventSubscription } from '@/infrastructure/Events/ISubscription';
|
||||
|
||||
/** Shows content of single category or many categories */
|
||||
@Component({
|
||||
@@ -79,11 +78,6 @@ export default class TheScripts extends StatefulVue {
|
||||
public isSearching = false;
|
||||
public searchHasMatches = false;
|
||||
|
||||
private listeners = new Array<IEventSubscription>();
|
||||
|
||||
public destroyed() {
|
||||
this.unsubscribeAll();
|
||||
}
|
||||
public async clearSearchQueryAsync() {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const filter = context.state.filter;
|
||||
@@ -97,23 +91,21 @@ export default class TheScripts extends StatefulVue {
|
||||
this.repositoryUrl = app.info.repositoryWebUrl;
|
||||
}
|
||||
protected handleCollectionState(newState: ICategoryCollectionState): void {
|
||||
this.unsubscribeAll();
|
||||
this.subscribe(newState);
|
||||
this.events.unsubscribeAll();
|
||||
this.subscribeState(newState);
|
||||
}
|
||||
|
||||
private subscribe(state: ICategoryCollectionState) {
|
||||
this.listeners.push(state.filter.filterRemoved.on(() => {
|
||||
this.isSearching = false;
|
||||
}));
|
||||
state.filter.filtered.on((result: IFilterResult) => {
|
||||
this.searchQuery = result.query;
|
||||
this.isSearching = true;
|
||||
this.searchHasMatches = result.hasAnyMatches();
|
||||
});
|
||||
}
|
||||
private unsubscribeAll() {
|
||||
this.listeners.forEach((listener) => listener.unsubscribe());
|
||||
this.listeners.splice(0, this.listeners.length);
|
||||
private subscribeState(state: ICategoryCollectionState) {
|
||||
this.events.register(
|
||||
state.filter.filterRemoved.on(() => {
|
||||
this.isSearching = false;
|
||||
}),
|
||||
state.filter.filtered.on((result: IFilterResult) => {
|
||||
this.searchQuery = result.query;
|
||||
this.isSearching = true;
|
||||
this.searchHasMatches = result.hasAnyMatches();
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Component, Vue } from 'vue-property-decorator';
|
||||
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
|
||||
import { IApplicationContext } from '@/application/Context/IApplicationContext';
|
||||
import { buildContext } from '@/application/Context/ApplicationContextProvider';
|
||||
import { IApplicationContextChangedEvent } from '../application/Context/IApplicationContext';
|
||||
import { IApplicationContextChangedEvent } from '@/application/Context/IApplicationContext';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { ICategoryCollectionState } from '../application/Context/State/ICategoryCollectionState';
|
||||
import { IEventSubscription } from '../infrastructure/Events/ISubscription';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { EventSubscriptionCollection } from '../infrastructure/Events/EventSubscriptionCollection';
|
||||
|
||||
// @ts-ignore because https://github.com/vuejs/vue-class-component/issues/91
|
||||
@Component
|
||||
@@ -13,18 +13,19 @@ export abstract class StatefulVue extends Vue {
|
||||
public static instance = new AsyncLazy<IApplicationContext>(
|
||||
() => Promise.resolve(buildContext()));
|
||||
|
||||
private listener: IEventSubscription;
|
||||
protected readonly events = new EventSubscriptionCollection();
|
||||
|
||||
private readonly ownEvents = new EventSubscriptionCollection();
|
||||
|
||||
public async mounted() {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.listener = context.contextChanged.on((event) => this.handleStateChangedEvent(event));
|
||||
this.ownEvents.register(context.contextChanged.on((event) => this.handleStateChangedEvent(event)));
|
||||
this.initialize(context.app);
|
||||
this.handleCollectionState(context.state, undefined);
|
||||
}
|
||||
public destroyed() {
|
||||
if (this.listener) {
|
||||
this.listener.unsubscribe();
|
||||
}
|
||||
this.ownEvents.unsubscribeAll();
|
||||
this.events.unsubscribeAll();
|
||||
}
|
||||
|
||||
protected abstract initialize(app: IApplication): void;
|
||||
|
||||
@@ -11,9 +11,6 @@ import { ICodeChangedEvent } from '@/application/Context/State/Code/Event/ICodeC
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { IEventSubscription } from '@/infrastructure/Events/ISubscription';
|
||||
import { IApplicationCode } from '@/application/Context/State/Code/IApplicationCode';
|
||||
import { CodeBuilderFactory } from '@/application/Context/State/Code/Generation/CodeBuilderFactory';
|
||||
|
||||
@Component
|
||||
@@ -22,16 +19,14 @@ export default class TheCodeArea extends StatefulVue {
|
||||
|
||||
private editor!: ace.Ace.Editor;
|
||||
private currentMarkerId?: number;
|
||||
private codeListener: IEventSubscription;
|
||||
|
||||
@Prop() private theme!: string;
|
||||
|
||||
public destroyed() {
|
||||
this.unsubscribeCodeListening();
|
||||
this.destroyEditor();
|
||||
}
|
||||
|
||||
protected initialize(app: IApplication): void {
|
||||
protected initialize(): void {
|
||||
return;
|
||||
}
|
||||
protected handleCollectionState(newState: ICategoryCollectionState): void {
|
||||
@@ -39,18 +34,10 @@ export default class TheCodeArea extends StatefulVue {
|
||||
this.editor = initializeEditor(this.theme, this.editorId, newState.collection.scripting.language);
|
||||
const appCode = newState.code;
|
||||
this.editor.setValue(appCode.current || getDefaultCode(newState.collection.scripting.language), 1);
|
||||
this.unsubscribeCodeListening();
|
||||
this.subscribe(appCode);
|
||||
this.events.unsubscribeAll();
|
||||
this.events.register(appCode.changed.on((code) => this.updateCodeAsync(code)));
|
||||
}
|
||||
|
||||
private subscribe(appCode: IApplicationCode) {
|
||||
this.codeListener = appCode.changed.on((code) => this.updateCodeAsync(code));
|
||||
}
|
||||
private unsubscribeCodeListening() {
|
||||
if (this.codeListener) {
|
||||
this.codeListener.unsubscribe();
|
||||
}
|
||||
}
|
||||
private async updateCodeAsync(event: ICodeChangedEvent) {
|
||||
this.removeCurrentHighlighting();
|
||||
if (event.isEmpty()) {
|
||||
|
||||
@@ -15,9 +15,7 @@ import { StatefulVue } from './StatefulVue';
|
||||
import { NonCollapsing } from '@/presentation/Scripts/Cards/NonCollapsingDirective';
|
||||
import { IUserFilter } from '@/application/Context/State/Filter/IUserFilter';
|
||||
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { IEventSubscription } from '@/infrastructure/Events/ISubscription';
|
||||
|
||||
@Component( {
|
||||
directives: { NonCollapsing },
|
||||
@@ -27,8 +25,6 @@ export default class TheSearchBar extends StatefulVue {
|
||||
public searchPlaceHolder = 'Search';
|
||||
public searchQuery = '';
|
||||
|
||||
private readonly listeners = new Array<IEventSubscription>();
|
||||
|
||||
@Watch('searchQuery')
|
||||
public async updateFilterAsync(newFilter: |string) {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
@@ -39,28 +35,21 @@ export default class TheSearchBar extends StatefulVue {
|
||||
filter.setFilter(newFilter);
|
||||
}
|
||||
}
|
||||
public destroyed() {
|
||||
this.unsubscribeAll();
|
||||
}
|
||||
|
||||
protected initialize(app: IApplication): void {
|
||||
protected initialize(): void {
|
||||
return;
|
||||
}
|
||||
protected handleCollectionState(newState: ICategoryCollectionState, oldState: ICategoryCollectionState | undefined) {
|
||||
const totalScripts = newState.collection.totalScripts;
|
||||
this.searchPlaceHolder = `Search in ${totalScripts} scripts`;
|
||||
this.searchQuery = newState.filter.currentFilter ? newState.filter.currentFilter.query : '';
|
||||
this.unsubscribeAll();
|
||||
this.subscribe(newState.filter);
|
||||
this.events.unsubscribeAll();
|
||||
this.subscribeFilter(newState.filter);
|
||||
}
|
||||
|
||||
private subscribe(filter: IUserFilter) {
|
||||
this.listeners.push(filter.filtered.on((result) => this.handleFiltered(result)));
|
||||
this.listeners.push(filter.filterRemoved.on(() => this.handleFilterRemoved()));
|
||||
}
|
||||
private unsubscribeAll() {
|
||||
this.listeners.forEach((listener) => listener.unsubscribe());
|
||||
this.listeners.splice(0, this.listeners.length);
|
||||
private subscribeFilter(filter: IUserFilter) {
|
||||
this.events.register(filter.filtered.on((result) => this.handleFiltered(result)));
|
||||
this.events.register(filter.filterRemoved.on(() => this.handleFilterRemoved()));
|
||||
}
|
||||
private handleFiltered(result: IFilterResult) {
|
||||
this.searchQuery = result.query;
|
||||
|
||||
Reference in New Issue
Block a user