refactor application.yaml to become an os definition #40
This commit is contained in:
@@ -32,8 +32,8 @@ export default class CardList extends StatefulVue {
|
||||
public activeCategoryId?: number = null;
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.setCategories(state.app.actions);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.setCategories(context.app.actions);
|
||||
this.onOutsideOfActiveCardClicked((element) => {
|
||||
if (hasDirective(element)) {
|
||||
return;
|
||||
|
||||
@@ -69,8 +69,8 @@ export default class CardListItem extends StatefulVue {
|
||||
}
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
state.selection.changed.on(() => {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
context.state.selection.changed.on(() => {
|
||||
this.updateStateAsync(this.categoryId);
|
||||
});
|
||||
this.updateStateAsync(this.categoryId);
|
||||
@@ -78,11 +78,12 @@ export default class CardListItem extends StatefulVue {
|
||||
|
||||
@Watch('categoryId')
|
||||
public async updateStateAsync(value: |number) {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
const category = !value ? undefined : state.app.findCategory(this.categoryId);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const category = !value ? undefined : context.app.findCategory(this.categoryId);
|
||||
this.cardTitle = category ? category.name : undefined;
|
||||
this.isAnyChildSelected = category ? state.selection.isAnySelected(category) : false;
|
||||
this.areAllChildrenSelected = category ? state.selection.areAllSelected(category) : false;
|
||||
const currentSelection = context.state.selection;
|
||||
this.isAnyChildSelected = category ? currentSelection.isAnySelected(category) : false;
|
||||
this.areAllChildrenSelected = category ? currentSelection.areAllSelected(category) : false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,24 +42,19 @@
|
||||
private filtered?: IFilterResult;
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
// React to state changes
|
||||
state.selection.changed.on(this.handleSelectionChanged);
|
||||
state.filter.filterRemoved.on(this.handleFilterRemoved);
|
||||
state.filter.filtered.on(this.handleFiltered);
|
||||
// Update initial state
|
||||
await this.initializeNodesAsync(this.categoryId);
|
||||
await this.initializeFilter(state.filter.currentFilter);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.beginReactingToStateChanges(context.state);
|
||||
this.setInitialState(context.state);
|
||||
}
|
||||
|
||||
public async toggleNodeSelectionAsync(event: INodeSelectedEvent) {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
const context = await this.getCurrentContextAsync();
|
||||
switch (event.node.type) {
|
||||
case NodeType.Category:
|
||||
toggleCategoryNodeSelection(event, state);
|
||||
toggleCategoryNodeSelection(event, context.state);
|
||||
break;
|
||||
case NodeType.Script:
|
||||
toggleScriptNodeSelection(event, state);
|
||||
toggleScriptNodeSelection(event, context.state);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown node type: ${event.node.id}`);
|
||||
@@ -68,13 +63,13 @@
|
||||
|
||||
@Watch('categoryId')
|
||||
public async initializeNodesAsync(categoryId?: number) {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
const context = await this.getCurrentContextAsync();
|
||||
if (categoryId) {
|
||||
this.nodes = parseSingleCategory(categoryId, state.app);
|
||||
this.nodes = parseSingleCategory(categoryId, context.app);
|
||||
} else {
|
||||
this.nodes = parseAllCategories(state.app);
|
||||
this.nodes = parseAllCategories(context.app);
|
||||
}
|
||||
this.selectedNodeIds = state.selection.selectedScripts
|
||||
this.selectedNodeIds = context.state.selection.selectedScripts
|
||||
.map((selected) => getScriptNodeId(selected.script));
|
||||
}
|
||||
|
||||
@@ -85,6 +80,17 @@
|
||||
(category: ICategory) => node.id === getCategoryNodeId(category));
|
||||
}
|
||||
|
||||
private beginReactingToStateChanges(state: IApplicationState) {
|
||||
state.selection.changed.on(this.handleSelectionChanged);
|
||||
state.filter.filterRemoved.on(this.handleFilterRemoved);
|
||||
state.filter.filtered.on(this.handleFiltered);
|
||||
}
|
||||
|
||||
private setInitialState(state: IApplicationState) {
|
||||
this.initializeNodesAsync(this.categoryId);
|
||||
this.initializeFilter(state.filter.currentFilter);
|
||||
}
|
||||
|
||||
private initializeFilter(currentFilter: IFilterResult | undefined) {
|
||||
if (!currentFilter) {
|
||||
this.handleFilterRemoved();
|
||||
|
||||
@@ -29,19 +29,20 @@
|
||||
|
||||
public async mounted() {
|
||||
await this.onNodeChangedAsync(this.node);
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.updateState(state.selection.selectedScripts);
|
||||
state.selection.changed.on((scripts) => this.updateState(scripts));
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const currentSelection = context.state.selection;
|
||||
this.updateState(currentSelection.selectedScripts);
|
||||
currentSelection.changed.on((scripts) => this.updateState(scripts));
|
||||
}
|
||||
|
||||
@Watch('node') public async onNodeChangedAsync(node: INode) {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.handler = getReverter(node, state.app);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.handler = getReverter(node, context.app);
|
||||
}
|
||||
|
||||
public async onRevertToggledAsync() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.handler.selectWithRevertState(this.isReverted, state.selection);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.handler.selectWithRevertState(this.isReverted, context.state.selection);
|
||||
}
|
||||
|
||||
private updateState(scripts: ReadonlyArray<SelectedScript>) {
|
||||
|
||||
@@ -49,6 +49,7 @@ import { IApplicationState } from '@/application/State/IApplicationState';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { SelectedScript } from '@/application/State/Selection/SelectedScript';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { IApplicationContext } from '../../../application/State/IApplicationContext';
|
||||
|
||||
enum SelectionState {
|
||||
Standard,
|
||||
@@ -67,66 +68,79 @@ export default class TheSelector extends StatefulVue {
|
||||
public currentSelection = SelectionState.None;
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.updateSelections(state);
|
||||
state.selection.changed.on(() => {
|
||||
this.updateSelections(state);
|
||||
});
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.updateSelections(context);
|
||||
this.beginReactingToChanges(context);
|
||||
}
|
||||
public async selectAsync(type: SelectionState): Promise<void> {
|
||||
if (this.currentSelection === type) {
|
||||
return;
|
||||
}
|
||||
const state = await this.getCurrentStateAsync();
|
||||
selectType(state, type);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
selectType(context, type);
|
||||
}
|
||||
|
||||
private updateSelections(state: IApplicationState) {
|
||||
this.currentSelection = getCurrentSelectionState(state);
|
||||
private updateSelections(context: IApplicationContext) {
|
||||
this.currentSelection = getCurrentSelectionState(context);
|
||||
}
|
||||
|
||||
private beginReactingToChanges(context: IApplicationContext) {
|
||||
context.state.selection.changed.on(() => {
|
||||
this.updateSelections(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface ITypeSelector {
|
||||
isSelected: (state: IApplicationState) => boolean;
|
||||
select: (state: IApplicationState) => void;
|
||||
isSelected: (context: IApplicationContext) => boolean;
|
||||
select: (context: IApplicationContext) => void;
|
||||
}
|
||||
|
||||
const selectors = new Map<SelectionState, ITypeSelector>([
|
||||
[SelectionState.None, {
|
||||
select: (state) => state.selection.deselectAll(),
|
||||
isSelected: (state) => state.selection.totalSelected === 0,
|
||||
select: (context) =>
|
||||
context.state.selection.deselectAll(),
|
||||
isSelected: (context) =>
|
||||
context.state.selection.totalSelected === 0,
|
||||
}],
|
||||
[SelectionState.Standard, {
|
||||
select: (state) => state.selection.selectOnly(state.app.getScriptsByLevel(RecommendationLevel.Standard)),
|
||||
isSelected: (state) => hasAllSelectedLevelOf(RecommendationLevel.Standard, state),
|
||||
select: (context) =>
|
||||
context.state.selection.selectOnly(
|
||||
context.app.getScriptsByLevel(RecommendationLevel.Standard)),
|
||||
isSelected: (context) =>
|
||||
hasAllSelectedLevelOf(RecommendationLevel.Standard, context),
|
||||
}],
|
||||
[SelectionState.Strict, {
|
||||
select: (state) => state.selection.selectOnly(state.app.getScriptsByLevel(RecommendationLevel.Strict)),
|
||||
isSelected: (state) => hasAllSelectedLevelOf(RecommendationLevel.Strict, state),
|
||||
select: (context) =>
|
||||
context.state.selection.selectOnly(context.app.getScriptsByLevel(RecommendationLevel.Strict)),
|
||||
isSelected: (context) =>
|
||||
hasAllSelectedLevelOf(RecommendationLevel.Strict, context),
|
||||
}],
|
||||
[SelectionState.All, {
|
||||
select: (state) => state.selection.selectAll(),
|
||||
isSelected: (state) => state.selection.totalSelected === state.app.totalScripts,
|
||||
select: (context) =>
|
||||
context.state.selection.selectAll(),
|
||||
isSelected: (context) =>
|
||||
context.state.selection.totalSelected === context.app.totalScripts,
|
||||
}],
|
||||
]);
|
||||
|
||||
function selectType(state: IApplicationState, type: SelectionState) {
|
||||
function selectType(context: IApplicationContext, type: SelectionState) {
|
||||
const selector = selectors.get(type);
|
||||
selector.select(state);
|
||||
selector.select(context);
|
||||
}
|
||||
|
||||
function getCurrentSelectionState(state: IApplicationState): SelectionState {
|
||||
function getCurrentSelectionState(context: IApplicationContext): SelectionState {
|
||||
for (const [type, selector] of Array.from(selectors.entries())) {
|
||||
if (selector.isSelected(state)) {
|
||||
if (selector.isSelected(context)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return SelectionState.Custom;
|
||||
}
|
||||
|
||||
function hasAllSelectedLevelOf(level: RecommendationLevel, state: IApplicationState) {
|
||||
const scripts = state.app.getScriptsByLevel(level);
|
||||
const selectedScripts = state.selection.selectedScripts;
|
||||
function hasAllSelectedLevelOf(level: RecommendationLevel, context: IApplicationContext) {
|
||||
const scripts = context.app.getScriptsByLevel(level);
|
||||
const selectedScripts = context.state.selection.selectedScripts;
|
||||
return areAllSelected(scripts, selectedScripts);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,12 +73,13 @@
|
||||
public searchHasMatches = false;
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.repositoryUrl = state.app.info.repositoryWebUrl;
|
||||
state.filter.filterRemoved.on(() => {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.repositoryUrl = context.app.info.repositoryWebUrl;
|
||||
const filter = context.state.filter;
|
||||
filter.filterRemoved.on(() => {
|
||||
this.isSearching = false;
|
||||
});
|
||||
state.filter.filtered.on((result: IFilterResult) => {
|
||||
filter.filtered.on((result: IFilterResult) => {
|
||||
this.searchQuery = result.query;
|
||||
this.isSearching = true;
|
||||
this.searchHasMatches = result.hasAnyMatches();
|
||||
@@ -86,8 +87,9 @@
|
||||
}
|
||||
|
||||
public async clearSearchQueryAsync() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
state.filter.removeFilter();
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const filter = context.state.filter;
|
||||
filter.removeFilter();
|
||||
}
|
||||
|
||||
public onGroupingChanged(group: Grouping) {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { ApplicationState } from '@/application/State/ApplicationState';
|
||||
import { IApplicationState } from '@/application/State/IApplicationState';
|
||||
import { Vue } from 'vue-property-decorator';
|
||||
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
|
||||
import { IApplicationContext } from '@/application/State/IApplicationContext';
|
||||
import { buildContext } from '@/application/State/ApplicationContextProvider';
|
||||
|
||||
export abstract class StatefulVue extends Vue {
|
||||
public isLoading = true;
|
||||
private static instance = new AsyncLazy<IApplicationContext>(
|
||||
() => Promise.resolve(buildContext()));
|
||||
|
||||
protected getCurrentStateAsync(): Promise<IApplicationState> {
|
||||
return ApplicationState.GetAsync();
|
||||
protected getCurrentContextAsync(): Promise<IApplicationContext> {
|
||||
return StatefulVue.instance.getValueAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'ace-builds/webpack-resolver';
|
||||
import { CodeBuilder } from '@/application/State/Code/Generation/CodeBuilder';
|
||||
import { ICodeChangedEvent } from '@/application/State/Code/Event/ICodeChangedEvent';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
|
||||
const NothingChosenCode =
|
||||
new CodeBuilder()
|
||||
@@ -38,10 +39,11 @@ export default class TheCodeArea extends StatefulVue {
|
||||
@Prop() private theme!: string;
|
||||
|
||||
public async mounted() {
|
||||
this.editor = initializeEditor(this.theme, this.editorId);
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.editor.setValue(state.code.current || NothingChosenCode, 1);
|
||||
state.code.changed.on((code) => this.updateCode(code));
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.editor = initializeEditor(this.theme, this.editorId, context.app.scripting.language);
|
||||
const appCode = context.state.code;
|
||||
this.editor.setValue(appCode.current || NothingChosenCode, 1);
|
||||
appCode.changed.on((code) => this.updateCode(code));
|
||||
}
|
||||
|
||||
private updateCode(event: ICodeChangedEvent) {
|
||||
@@ -93,10 +95,10 @@ export default class TheCodeArea extends StatefulVue {
|
||||
}
|
||||
}
|
||||
|
||||
function initializeEditor(theme: string, editorId: string): ace.Ace.Editor {
|
||||
const lang = 'batchfile';
|
||||
function initializeEditor(theme: string, editorId: string, language: ScriptingLanguage): ace.Ace.Editor {
|
||||
theme = theme || 'github';
|
||||
const editor = ace.edit(editorId);
|
||||
const lang = getLanguage(language);
|
||||
editor.getSession().setMode(`ace/mode/${lang}`);
|
||||
editor.setTheme(`ace/theme/${theme}`);
|
||||
editor.setReadOnly(true);
|
||||
@@ -105,6 +107,15 @@ function initializeEditor(theme: string, editorId: string): ace.Ace.Editor {
|
||||
return editor;
|
||||
}
|
||||
|
||||
function getLanguage(language: ScriptingLanguage) {
|
||||
switch (language) {
|
||||
case ScriptingLanguage.batchfile:
|
||||
return 'batchfile';
|
||||
default:
|
||||
throw new Error('unkown language');
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="container" v-if="hasCode">
|
||||
</IconButton>
|
||||
<IconButton
|
||||
:text="this.isDesktop ? 'Save' : 'Download'"
|
||||
v-on:click="saveCodeAsync"
|
||||
@@ -22,6 +21,9 @@ import { SaveFileDialog, FileType } from '@/infrastructure/SaveFileDialog';
|
||||
import { Clipboard } from '@/infrastructure/Clipboard';
|
||||
import IconButton from './IconButton.vue';
|
||||
import { Environment } from '@/application/Environment/Environment';
|
||||
import { IApplicationCode } from '../application/State/IApplicationState';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { IApplicationContext } from '@/application/State/IApplicationContext';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@@ -33,22 +35,44 @@ export default class TheCodeButtons extends StatefulVue {
|
||||
public isDesktop = false;
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.isDesktop = Environment.CurrentEnvironment.isDesktop;
|
||||
this.hasCode = state.code.current && state.code.current.length > 0;
|
||||
state.code.changed.on((code) => {
|
||||
this.hasCode = code && code.code.length > 0;
|
||||
const code = await this.getCurrentCodeAsync();
|
||||
this.hasCode = code.current && code.current.length > 0;
|
||||
code.changed.on((newCode) => {
|
||||
this.hasCode = newCode && newCode.code.length > 0;
|
||||
});
|
||||
this.isDesktop = Environment.CurrentEnvironment.isDesktop;
|
||||
}
|
||||
|
||||
public async copyCodeAsync() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
Clipboard.copyText(state.code.current);
|
||||
const code = await this.getCurrentCodeAsync();
|
||||
Clipboard.copyText(code.current);
|
||||
}
|
||||
|
||||
public async saveCodeAsync() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
SaveFileDialog.saveFile(state.code.current, 'privacy-script.bat', FileType.BatchFile);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
saveCode(context);
|
||||
}
|
||||
|
||||
private async getCurrentCodeAsync(): Promise<IApplicationCode> {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const code = context.state.code;
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
function saveCode(context: IApplicationContext) {
|
||||
const fileName = `privacy-script.${context.app.scripting.fileExtension}`;
|
||||
const content = context.state.code.current;
|
||||
const type = getType(context.app.scripting.language);
|
||||
SaveFileDialog.saveFile(content, fileName, FileType.BatchFile);
|
||||
}
|
||||
|
||||
function getType(language: ScriptingLanguage) {
|
||||
switch (language) {
|
||||
case ScriptingLanguage.batchfile:
|
||||
return FileType.BatchFile;
|
||||
default:
|
||||
throw new Error('unknown file type');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -38,8 +38,8 @@ export default class DownloadUrlListItem extends StatefulVue {
|
||||
}
|
||||
|
||||
private async getDownloadUrlAsync(os: OperatingSystem): Promise<string> {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
return state.app.info.getDownloadUrl(os);
|
||||
const context = await this.getCurrentContextAsync();
|
||||
return context.app.info.getDownloadUrl(os);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@ export default class TheFooter extends StatefulVue {
|
||||
}
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.repositoryUrl = state.app.info.repositoryWebUrl;
|
||||
this.feedbackUrl = state.app.info.feedbackUrl;
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.repositoryUrl = context.app.info.repositoryWebUrl;
|
||||
this.feedbackUrl = context.app.info.feedbackUrl;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -74,7 +74,7 @@ export default class TheFooter extends StatefulVue {
|
||||
}
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
const state = await this.getCurrentContextAsync();
|
||||
const info = state.app.info;
|
||||
this.version = info.version;
|
||||
this.homepageUrl = info.homepage;
|
||||
|
||||
@@ -15,8 +15,8 @@ export default class TheHeader extends StatefulVue {
|
||||
public subtitle = '';
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.title = state.app.info.name;
|
||||
const context = await this.getCurrentContextAsync();
|
||||
this.title = context.app.info.name;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -24,19 +24,20 @@ export default class TheSearchBar extends StatefulVue {
|
||||
public searchQuery = '';
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
const totalScripts = state.app.totalScripts;
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const totalScripts = context.app.totalScripts;
|
||||
this.searchPlaceHolder = `Search in ${totalScripts} scripts`;
|
||||
this.beginReacting(state.filter);
|
||||
this.beginReacting(context.state.filter);
|
||||
}
|
||||
|
||||
@Watch('searchQuery')
|
||||
public async updateFilterAsync(filter: |string) {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
if (!filter) {
|
||||
state.filter.removeFilter();
|
||||
public async updateFilterAsync(newFilter: |string) {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
const filter = context.state.filter;
|
||||
if (!newFilter) {
|
||||
filter.removeFilter();
|
||||
} else {
|
||||
state.filter.setFilter(filter);
|
||||
filter.setFilter(newFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user