diff --git a/src/application/Context/IApplicationContext.ts b/src/application/Context/IApplicationContext.ts index 6d3e815e..9a8090a3 100644 --- a/src/application/Context/IApplicationContext.ts +++ b/src/application/Context/IApplicationContext.ts @@ -1,12 +1,16 @@ -import { ICategoryCollectionState } from './State/ICategoryCollectionState'; +import { ICategoryCollectionState, IReadOnlyCategoryCollectionState } from './State/ICategoryCollectionState'; import { OperatingSystem } from '@/domain/OperatingSystem'; import { IEventSource } from '@/infrastructure/Events/IEventSource'; import { IApplication } from '@/domain/IApplication'; -export interface IApplicationContext { +export interface IReadOnlyApplicationContext { readonly app: IApplication; - readonly state: ICategoryCollectionState; + readonly state: IReadOnlyCategoryCollectionState; readonly contextChanged: IEventSource; +} + +export interface IApplicationContext extends IReadOnlyApplicationContext { + readonly state: ICategoryCollectionState; changeContext(os: OperatingSystem): void; } diff --git a/src/application/Context/State/CategoryCollectionState.ts b/src/application/Context/State/CategoryCollectionState.ts index 0f31539e..7638a384 100644 --- a/src/application/Context/State/CategoryCollectionState.ts +++ b/src/application/Context/State/CategoryCollectionState.ts @@ -5,7 +5,7 @@ import { UserSelection } from './Selection/UserSelection'; import { IUserSelection } from './Selection/IUserSelection'; import { ICategoryCollectionState } from './ICategoryCollectionState'; import { IApplicationCode } from './Code/IApplicationCode'; -import { ICategoryCollection } from '../../../domain/ICategoryCollection'; +import { ICategoryCollection } from '@/domain/ICategoryCollection'; import { OperatingSystem } from '@/domain/OperatingSystem'; export class CategoryCollectionState implements ICategoryCollectionState { diff --git a/src/application/Context/State/Code/ApplicationCode.ts b/src/application/Context/State/Code/ApplicationCode.ts index 94214cc4..f0a4db23 100644 --- a/src/application/Context/State/Code/ApplicationCode.ts +++ b/src/application/Context/State/Code/ApplicationCode.ts @@ -2,7 +2,7 @@ import { CodeChangedEvent } from './Event/CodeChangedEvent'; import { CodePosition } from './Position/CodePosition'; import { ICodeChangedEvent } from './Event/ICodeChangedEvent'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; -import { IUserSelection } from '@/application/Context/State/Selection/IUserSelection'; +import { IReadOnlyUserSelection } from '@/application/Context/State/Selection/IUserSelection'; import { UserScriptGenerator } from './Generation/UserScriptGenerator'; import { EventSource } from '@/infrastructure/Events/EventSource'; import { IApplicationCode } from './IApplicationCode'; @@ -16,7 +16,7 @@ export class ApplicationCode implements IApplicationCode { private scriptPositions = new Map(); constructor( - userSelection: IUserSelection, + userSelection: IReadOnlyUserSelection, private readonly scriptingDefinition: IScriptingDefinition, private readonly generator: IUserScriptGenerator = new UserScriptGenerator()) { if (!userSelection) { throw new Error('userSelection is null or undefined'); } diff --git a/src/application/Context/State/Filter/IUserFilter.ts b/src/application/Context/State/Filter/IUserFilter.ts index 6de39103..1cec5486 100644 --- a/src/application/Context/State/Filter/IUserFilter.ts +++ b/src/application/Context/State/Filter/IUserFilter.ts @@ -1,10 +1,13 @@ import { IEventSource } from '@/infrastructure/Events/IEventSource'; import { IFilterResult } from './IFilterResult'; -export interface IUserFilter { +export interface IReadOnlyUserFilter { readonly currentFilter: IFilterResult | undefined; readonly filtered: IEventSource; readonly filterRemoved: IEventSource; +} + +export interface IUserFilter extends IReadOnlyUserFilter { setFilter(filter: string): void; removeFilter(): void; } diff --git a/src/application/Context/State/ICategoryCollectionState.ts b/src/application/Context/State/ICategoryCollectionState.ts index af846bc6..01664bc8 100644 --- a/src/application/Context/State/ICategoryCollectionState.ts +++ b/src/application/Context/State/ICategoryCollectionState.ts @@ -1,13 +1,18 @@ -import { IUserFilter } from './Filter/IUserFilter'; -import { IUserSelection } from './Selection/IUserSelection'; +import { IReadOnlyUserFilter, IUserFilter } from './Filter/IUserFilter'; +import { IReadOnlyUserSelection, IUserSelection } from './Selection/IUserSelection'; import { IApplicationCode } from './Code/IApplicationCode'; import { ICategoryCollection } from '@/domain/ICategoryCollection'; import { OperatingSystem } from '@/domain/OperatingSystem'; -export interface ICategoryCollectionState { +export interface IReadOnlyCategoryCollectionState { readonly code: IApplicationCode; + readonly os: OperatingSystem; + readonly filter: IReadOnlyUserFilter; + readonly selection: IReadOnlyUserSelection; + readonly collection: ICategoryCollection; +} + +export interface ICategoryCollectionState extends IReadOnlyCategoryCollectionState { readonly filter: IUserFilter; readonly selection: IUserSelection; - readonly collection: ICategoryCollection; - readonly os: OperatingSystem; } diff --git a/src/application/Context/State/Selection/IUserSelection.ts b/src/application/Context/State/Selection/IUserSelection.ts index 0e23e7b3..1c5d7229 100644 --- a/src/application/Context/State/Selection/IUserSelection.ts +++ b/src/application/Context/State/Selection/IUserSelection.ts @@ -3,18 +3,21 @@ import { IScript } from '@/domain/IScript'; import { ICategory } from '@/domain/ICategory'; import { IEventSource } from '@/infrastructure/Events/IEventSource'; -export interface IUserSelection { +export interface IReadOnlyUserSelection { readonly changed: IEventSource>; readonly selectedScripts: ReadonlyArray; + isSelected(scriptId: string): boolean; areAllSelected(category: ICategory): boolean; isAnySelected(category: ICategory): boolean; +} + +export interface IUserSelection extends IReadOnlyUserSelection { removeAllInCategory(categoryId: number): void; addOrUpdateAllInCategory(categoryId: number, revert: boolean): void; addSelectedScript(scriptId: string, revert: boolean): void; addOrUpdateSelectedScript(scriptId: string, revert: boolean): void; removeSelectedScript(scriptId: string): void; selectOnly(scripts: ReadonlyArray): void; - isSelected(scriptId: string): boolean; selectAll(): void; deselectAll(): void; } diff --git a/src/presentation/components/Code/CodeButtons/TheCodeButtons.vue b/src/presentation/components/Code/CodeButtons/TheCodeButtons.vue index e0c5d5ff..871df1f1 100644 --- a/src/presentation/components/Code/CodeButtons/TheCodeButtons.vue +++ b/src/presentation/components/Code/CodeButtons/TheCodeButtons.vue @@ -32,13 +32,13 @@ import Dialog from '@/presentation/components/Shared/Dialog.vue'; import IconButton from './IconButton.vue'; import MacOsInstructions from './MacOsInstructions.vue'; import { Environment } from '@/application/Environment/Environment'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; import { ScriptingLanguage } from '@/domain/ScriptingLanguage'; import { IApplicationCode } from '@/application/Context/State/Code/IApplicationCode'; import { IScriptingDefinition } from '@/domain/IScriptingDefinition'; import { OperatingSystem } from '@/domain/OperatingSystem'; import { CodeRunner } from '@/infrastructure/CodeRunner'; -import { IApplicationContext } from '@/application/Context/IApplicationContext'; +import { IReadOnlyApplicationContext } from '@/application/Context/IApplicationContext'; @Component({ components: { @@ -70,7 +70,7 @@ export default class TheCodeButtons extends StatefulVue { await executeCode(context); } - protected handleCollectionState(newState: ICategoryCollectionState): void { + protected handleCollectionState(newState: IReadOnlyCategoryCollectionState): void { this.canRun = this.isDesktopVersion && newState.collection.os === Environment.CurrentEnvironment.os; this.isMacOsCollection = newState.collection.os === OperatingSystem.macOS; this.fileName = buildFileName(newState.collection.scripting); @@ -91,7 +91,7 @@ export default class TheCodeButtons extends StatefulVue { } } -function saveCode(fileName: string, state: ICategoryCollectionState) { +function saveCode(fileName: string, state: IReadOnlyCategoryCollectionState) { const content = state.code.current; const type = getType(state.collection.scripting.language); SaveFileDialog.saveFile(content, fileName, type); @@ -115,7 +115,7 @@ function buildFileName(scripting: IScriptingDefinition) { return fileName; } -async function executeCode(context: IApplicationContext) { +async function executeCode(context: IReadOnlyApplicationContext) { const runner = new CodeRunner(); await runner.runCode( /*code*/ context.state.code.current, diff --git a/src/presentation/components/Code/TheCodeArea.vue b/src/presentation/components/Code/TheCodeArea.vue index 0203a424..ee76f4e2 100644 --- a/src/presentation/components/Code/TheCodeArea.vue +++ b/src/presentation/components/Code/TheCodeArea.vue @@ -17,7 +17,7 @@ import 'ace-builds/webpack-resolver'; import { ICodeChangedEvent } from '@/application/Context/State/Code/Event/ICodeChangedEvent'; import { IScript } from '@/domain/IScript'; import { ScriptingLanguage } from '@/domain/ScriptingLanguage'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; import { CodeBuilderFactory } from '@/application/Context/State/Code/Generation/CodeBuilderFactory'; import Responsive from '@/presentation/components/Shared/Responsive.vue'; import { NonCollapsing } from '@/presentation/components/Scripts/View/Cards/NonCollapsingDirective'; @@ -45,7 +45,7 @@ export default class TheCodeArea extends StatefulVue { } } - protected handleCollectionState(newState: ICategoryCollectionState): void { + protected handleCollectionState(newState: IReadOnlyCategoryCollectionState): void { this.destroyEditor(); this.editor = initializeEditor(this.theme, this.editorId, newState.collection.scripting.language); const appCode = newState.code; diff --git a/src/presentation/components/Scripts/Menu/Selector/SelectionTypeHandler.ts b/src/presentation/components/Scripts/Menu/Selector/SelectionTypeHandler.ts index 2e630d08..456f6a30 100644 --- a/src/presentation/components/Scripts/Menu/Selector/SelectionTypeHandler.ts +++ b/src/presentation/components/Scripts/Menu/Selector/SelectionTypeHandler.ts @@ -2,7 +2,7 @@ import { IScript } from '@/domain/IScript'; import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript'; import { RecommendationLevel } from '@/domain/RecommendationLevel'; import { scrambledEqual } from '@/application/Common/Array'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { ICategoryCollectionState, IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; export enum SelectionType { Standard, @@ -34,7 +34,7 @@ export class SelectionTypeHandler { } interface ISingleTypeHandler { - isSelected: (state: ICategoryCollectionState) => boolean; + isSelected: (state: IReadOnlyCategoryCollectionState) => boolean; select: (state: ICategoryCollectionState) => void; } @@ -62,7 +62,7 @@ function getRecommendationLevelSelector(level: RecommendationLevel): ISingleType }; } -function hasAllSelectedLevelOf(level: RecommendationLevel, state: ICategoryCollectionState) { +function hasAllSelectedLevelOf(level: RecommendationLevel, state: IReadOnlyCategoryCollectionState) { const scripts = state.collection.getScriptsByLevel(level); const selectedScripts = state.selection.selectedScripts; return areAllSelected(scripts, selectedScripts); diff --git a/src/presentation/components/Scripts/Menu/TheOsChanger.vue b/src/presentation/components/Scripts/Menu/TheOsChanger.vue index 53251193..8bcd4e13 100644 --- a/src/presentation/components/Scripts/Menu/TheOsChanger.vue +++ b/src/presentation/components/Scripts/Menu/TheOsChanger.vue @@ -13,7 +13,7 @@ import { Component } from 'vue-property-decorator'; import { OperatingSystem } from '@/domain/OperatingSystem'; import { StatefulVue } from '@/presentation/components/Shared/StatefulVue'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; import { ApplicationFactory } from '@/application/ApplicationFactory'; import MenuOptionList from './MenuOptionList.vue'; import MenuOptionListItem from './MenuOptionListItem.vue'; @@ -38,7 +38,7 @@ export default class TheOsChanger extends StatefulVue { context.changeContext(newOs); } - protected handleCollectionState(newState: ICategoryCollectionState): void { + protected handleCollectionState(newState: IReadOnlyCategoryCollectionState): void { this.currentOs = newState.os; this.$forceUpdate(); // v-bind:class is not updated otherwise } diff --git a/src/presentation/components/Scripts/Menu/TheScriptsMenu.vue b/src/presentation/components/Scripts/Menu/TheScriptsMenu.vue index 25fc669e..b7764b0a 100644 --- a/src/presentation/components/Scripts/Menu/TheScriptsMenu.vue +++ b/src/presentation/components/Scripts/Menu/TheScriptsMenu.vue @@ -15,7 +15,7 @@ import TheOsChanger from './TheOsChanger.vue'; import TheSelector from './Selector/TheSelector.vue'; import TheViewChanger from './View/TheViewChanger.vue'; import { StatefulVue } from '@/presentation/components/Shared/StatefulVue'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; import { IEventSubscription } from '@/infrastructure/Events/IEventSource'; @Component({ @@ -37,11 +37,11 @@ export default class TheScriptsMenu extends StatefulVue { protected initialize(): void { return; } - protected handleCollectionState(newState: ICategoryCollectionState): void { + protected handleCollectionState(newState: IReadOnlyCategoryCollectionState): void { this.subscribe(newState); } - private subscribe(state: ICategoryCollectionState) { + private subscribe(state: IReadOnlyCategoryCollectionState) { this.listeners.push(state.filter.filterRemoved.on(() => { this.isSearching = false; })); diff --git a/src/presentation/components/Scripts/View/Cards/CardList.vue b/src/presentation/components/Scripts/View/Cards/CardList.vue index c65adf17..95aa225a 100644 --- a/src/presentation/components/Scripts/View/Cards/CardList.vue +++ b/src/presentation/components/Scripts/View/Cards/CardList.vue @@ -31,7 +31,7 @@ import { Component } from 'vue-property-decorator'; import { StatefulVue } from '@/presentation/components/Shared/StatefulVue'; import { ICategory } from '@/domain/ICategory'; import { hasDirective } from './NonCollapsingDirective'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; @Component({ components: { @@ -56,7 +56,7 @@ export default class CardList extends StatefulVue { this.activeCategoryId = isExpanded ? categoryId : undefined; } - protected handleCollectionState(newState: ICategoryCollectionState, oldState: ICategoryCollectionState): void { + protected handleCollectionState(newState: IReadOnlyCategoryCollectionState): void { this.setCategories(newState.collection.actions); this.activeCategoryId = undefined; } diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/RevertToggle.vue b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/RevertToggle.vue index f2c2f089..57f76a81 100644 --- a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/RevertToggle.vue +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/RevertToggle.vue @@ -19,7 +19,7 @@ import { StatefulVue } from '@/presentation/components/Shared/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 { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; @Component export default class RevertToggle extends StatefulVue { @@ -37,7 +37,7 @@ export default class RevertToggle extends StatefulVue { this.handler.selectWithRevertState(this.isReverted, context.state.selection); } - protected handleCollectionState(newState: ICategoryCollectionState): void { + protected handleCollectionState(newState: IReadOnlyCategoryCollectionState): void { this.updateStatus(newState.selection.selectedScripts); this.events.unsubscribeAll(); this.events.register(newState.selection.changed.on((scripts) => this.updateStatus(scripts))); diff --git a/src/presentation/components/Scripts/View/TheScriptsView.vue b/src/presentation/components/Scripts/View/TheScriptsView.vue index f045a135..3f915875 100644 --- a/src/presentation/components/Scripts/View/TheScriptsView.vue +++ b/src/presentation/components/Scripts/View/TheScriptsView.vue @@ -36,7 +36,7 @@ import { Component, Prop } from 'vue-property-decorator'; import { StatefulVue } from '@/presentation/components/Shared/StatefulVue'; import { ViewType } from '@/presentation/components/Scripts/Menu/View/ViewType'; import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; import { ApplicationFactory } from '@/application/ApplicationFactory'; /** Shows content of single category or many categories */ @@ -74,12 +74,12 @@ export default class TheScriptsView extends StatefulVue { filter.removeFilter(); } - protected handleCollectionState(newState: ICategoryCollectionState): void { + protected handleCollectionState(newState: IReadOnlyCategoryCollectionState): void { this.events.unsubscribeAll(); this.subscribeState(newState); } - private subscribeState(state: ICategoryCollectionState) { + private subscribeState(state: IReadOnlyCategoryCollectionState) { this.events.register( state.filter.filterRemoved.on(() => { this.isSearching = false; diff --git a/src/presentation/components/Shared/StatefulVue.ts b/src/presentation/components/Shared/StatefulVue.ts index 2f761d99..fd4dc8da 100644 --- a/src/presentation/components/Shared/StatefulVue.ts +++ b/src/presentation/components/Shared/StatefulVue.ts @@ -3,7 +3,7 @@ import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy'; import { IApplicationContext } from '@/application/Context/IApplicationContext'; import { buildContext } from '@/application/Context/ApplicationContextFactory'; import { IApplicationContextChangedEvent } from '@/application/Context/IApplicationContext'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; import { EventSubscriptionCollection } from '@/infrastructure/Events/EventSubscriptionCollection'; // @ts-ignore because https://github.com/vuejs/vue-class-component/issues/91 @@ -26,7 +26,8 @@ export abstract class StatefulVue extends Vue { } protected abstract handleCollectionState( - newState: ICategoryCollectionState, oldState: ICategoryCollectionState | undefined): void; + newState: IReadOnlyCategoryCollectionState, + oldState: IReadOnlyCategoryCollectionState | undefined): void; protected getCurrentContext(): Promise { return StatefulVue.instance.getValue(); } diff --git a/src/presentation/components/TheSearchBar.vue b/src/presentation/components/TheSearchBar.vue index 968bb116..28750423 100644 --- a/src/presentation/components/TheSearchBar.vue +++ b/src/presentation/components/TheSearchBar.vue @@ -13,9 +13,9 @@ import { Component, Watch } from 'vue-property-decorator'; import { StatefulVue } from '@/presentation/components/Shared/StatefulVue'; import { NonCollapsing } from '@/presentation/components/Scripts/View/Cards/NonCollapsingDirective'; -import { IUserFilter } from '@/application/Context/State/Filter/IUserFilter'; +import { IReadOnlyUserFilter } from '@/application/Context/State/Filter/IUserFilter'; import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult'; -import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; +import { IReadOnlyCategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState'; @Component( { directives: { NonCollapsing }, @@ -36,7 +36,7 @@ export default class TheSearchBar extends StatefulVue { } } - protected handleCollectionState(newState: ICategoryCollectionState, oldState: ICategoryCollectionState | undefined) { + protected handleCollectionState(newState: IReadOnlyCategoryCollectionState) { const totalScripts = newState.collection.totalScripts; this.searchPlaceHolder = `Search in ${totalScripts} scripts`; this.searchQuery = newState.filter.currentFilter ? newState.filter.currentFilter.query : ''; @@ -44,7 +44,7 @@ export default class TheSearchBar extends StatefulVue { this.subscribeFilter(newState.filter); } - private subscribeFilter(filter: IUserFilter) { + private subscribeFilter(filter: IReadOnlyUserFilter) { this.events.register(filter.filtered.on((result) => this.handleFiltered(result))); this.events.register(filter.filterRemoved.on(() => this.handleFilterRemoved())); }