aboutsummaryrefslogtreecommitdiffstats
path: root/src/stores/RecipesStore.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/stores/RecipesStore.ts')
-rw-r--r--src/stores/RecipesStore.ts169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/stores/RecipesStore.ts b/src/stores/RecipesStore.ts
new file mode 100644
index 000000000..af2aa7fb0
--- /dev/null
+++ b/src/stores/RecipesStore.ts
@@ -0,0 +1,169 @@
1import { action, computed, observable } from 'mobx';
2import { readJSONSync } from 'fs-extra';
3import semver from 'semver';
4
5import { Stores } from 'src/stores.types';
6import { ApiInterface } from 'src/api';
7import { Actions } from 'src/actions/lib/actions';
8import Recipe from 'src/models/Recipe';
9import CachedRequest from './lib/CachedRequest';
10import Request from './lib/Request';
11import { matchRoute } from '../helpers/routing-helpers';
12import { asarRecipesPath } from '../helpers/asar-helpers';
13import TypedStore from './lib/TypedStore';
14
15const debug = require('../preload-safe-debug')('Ferdium:RecipeStore');
16
17export default class RecipesStore extends TypedStore {
18 @observable allRecipesRequest = new CachedRequest(this.api.recipes, 'all');
19
20 @observable installRecipeRequest = new Request(this.api.recipes, 'install');
21
22 @observable getRecipeUpdatesRequest = new Request(this.api.recipes, 'update');
23
24 constructor(stores: Stores, api: ApiInterface, actions: Actions) {
25 super(stores, api, actions);
26
27 // Register action handlers
28 this.actions.recipe.install.listen(this._install.bind(this));
29 this.actions.recipe.update.listen(this._update.bind(this));
30
31 // Reactions
32 this.registerReactions([this._checkIfRecipeIsInstalled.bind(this)]);
33 }
34
35 async setup(): Promise<void> {
36 // Initially load all recipes
37 this.all;
38 }
39
40 @computed get all(): Recipe[] {
41 return this.allRecipesRequest.execute().result || [];
42 }
43
44 @computed get active() {
45 const match = matchRoute(
46 '/settings/services/add/:id',
47 this.stores.router.location.pathname,
48 );
49 if (match) {
50 const activeRecipe = this.one(match.id);
51 if (activeRecipe) {
52 return activeRecipe;
53 }
54
55 debug(`Recipe ${match.id} not installed`);
56 }
57
58 return null;
59 }
60
61 @computed get recipeIdForServices(): string[] {
62 return this.stores.services.all.map(s => s.recipe.id);
63 }
64
65 one(id: string): Recipe | undefined {
66 return this.all.find(recipe => recipe.id === id);
67 }
68
69 isInstalled(id: string): boolean {
70 return !!this.one(id);
71 }
72
73 // Actions
74 async _install({ recipeId }): Promise<Recipe> {
75 const recipe = await this.installRecipeRequest.execute(recipeId)._promise;
76 await this.allRecipesRequest.invalidate({ immediately: true })._promise;
77
78 return recipe;
79 }
80
81 @action async _update() {
82 const recipeIds = this.recipeIdForServices;
83 const recipes = {};
84
85 // Hackfix, reference this.all to fetch services
86 debug(`Check Recipe updates for ${this.all.map(recipe => recipe.id)}`);
87
88 for (const r of recipeIds) {
89 const recipe = this.one(r);
90 if (recipe) {
91 recipes[r] = recipe.version;
92 }
93 }
94
95 if (Object.keys(recipes).length === 0) return;
96
97 const remoteUpdates = await this.getRecipeUpdatesRequest.execute(recipes)
98 ._promise;
99
100 // Check for local updates
101 const allJsonFile = asarRecipesPath('all.json');
102 const allJson = readJSONSync(allJsonFile);
103 const localUpdates: string[] = [];
104
105 for (const recipe of Object.keys(recipes)) {
106 const version = recipes[recipe];
107
108 // Find recipe in local recipe repository
109 const localRecipe = allJson.find(r => r.id === recipe);
110
111 if (localRecipe && semver.lt(version, localRecipe.version)) {
112 localUpdates.push(recipe);
113 }
114 }
115
116 const updates = [...remoteUpdates, ...localUpdates];
117 debug(
118 'Got update information (local, remote):',
119 localUpdates,
120 remoteUpdates,
121 );
122
123 const length = updates.length - 1;
124 const syncUpdate = async i => {
125 const update = updates[i];
126
127 this.actions.recipe.install({ recipeId: update });
128 await this.installRecipeRequest._promise;
129
130 this.installRecipeRequest.reset();
131
132 if (i === length) {
133 this.stores.ui.showServicesUpdatedInfoBar = true;
134 } else if (i < length) {
135 syncUpdate(i + 1);
136 }
137 };
138
139 if (length >= 0) {
140 syncUpdate(0);
141 }
142 }
143
144 async _checkIfRecipeIsInstalled(): Promise<void> {
145 const { router } = this.stores;
146
147 const match =
148 router.location &&
149 matchRoute('/settings/services/add/:id', router.location.pathname);
150 if (match) {
151 const recipeId = match.id;
152
153 if (!this.stores.recipes.isInstalled(recipeId)) {
154 router.push('/settings/recipes');
155 debug(`Recipe ${recipeId} is not installed, trying to install it`);
156
157 const recipe = await this.installRecipeRequest.execute(recipeId)
158 ._promise;
159 if (recipe) {
160 await this.allRecipesRequest.invalidate({ immediately: true })
161 ._promise;
162 router.push(`/settings/services/add/${recipeId}`);
163 } else {
164 router.push('/settings/recipes');
165 }
166 }
167 }
168 }
169}