diff options
author | muhamedsalih-tw <104364298+muhamedsalih-tw@users.noreply.github.com> | 2022-10-27 07:13:47 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-27 01:43:47 +0000 |
commit | 81c43ecc3d17e0dbf7ad1d949b6d977f2c65bd48 (patch) | |
tree | dfa7c08cb54fb81b7d2e788d350de52c2ebd05d2 /src | |
parent | 6.2.1-nightly.30 [skip ci] (diff) | |
download | ferdium-app-81c43ecc3d17e0dbf7ad1d949b6d977f2c65bd48.tar.gz ferdium-app-81c43ecc3d17e0dbf7ad1d949b6d977f2c65bd48.tar.zst ferdium-app-81c43ecc3d17e0dbf7ad1d949b6d977f2c65bd48.zip |
fix: 'failed prop' warning in QuickSwitchModal, SettingsNavigation, SettingsWindow and Recipe component tree (#713)
* chore: turn off eslint rule @typescript-eslint/no-useless-constructor to initialize dynamic props & state
Co-authored-by: Muhamed <>
Co-authored-by: Vijay A <vraravam@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/settings/navigation/SettingsNavigation.tsx (renamed from src/components/settings/navigation/SettingsNavigation.jsx) | 70 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipeItem.tsx (renamed from src/components/settings/recipes/RecipeItem.js) | 23 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipesDashboard.tsx (renamed from src/components/settings/recipes/RecipesDashboard.jsx) | 65 | ||||
-rw-r--r-- | src/components/ui/SearchInput.tsx | 89 | ||||
-rw-r--r-- | src/components/ui/effects/Appear.tsx | 14 | ||||
-rw-r--r-- | src/containers/settings/RecipesScreen.tsx | 36 | ||||
-rw-r--r-- | src/containers/settings/SettingsWindow.tsx | 25 | ||||
-rw-r--r-- | src/features/quickSwitch/Component.tsx (renamed from src/features/quickSwitch/Component.js) | 102 | ||||
-rw-r--r-- | src/models/RecipePreview.ts | 10 |
9 files changed, 211 insertions, 223 deletions
diff --git a/src/components/settings/navigation/SettingsNavigation.jsx b/src/components/settings/navigation/SettingsNavigation.tsx index e1242a7fe..95c69027c 100644 --- a/src/components/settings/navigation/SettingsNavigation.jsx +++ b/src/components/settings/navigation/SettingsNavigation.tsx | |||
@@ -1,18 +1,13 @@ | |||
1 | import { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
3 | import { defineMessages, injectIntl } from 'react-intl'; | ||
4 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
5 | import { RouterStore } from '@superwf/mobx-react-router'; | ||
6 | |||
7 | import { NavLink } from 'react-router-dom'; | 4 | import { NavLink } from 'react-router-dom'; |
5 | import { StoresProps } from '../../../@types/ferdium-components.types'; | ||
8 | import { | 6 | import { |
9 | LOCAL_SERVER, | 7 | LOCAL_SERVER, |
10 | LIVE_FERDIUM_API, | 8 | LIVE_FERDIUM_API, |
11 | LIVE_FRANZ_API, | 9 | LIVE_FRANZ_API, |
12 | } from '../../../config'; | 10 | } from '../../../config'; |
13 | import UIStore from '../../../stores/UIStore'; | ||
14 | import SettingsStore from '../../../stores/SettingsStore'; | ||
15 | import UserStore from '../../../stores/UserStore'; | ||
16 | import globalMessages from '../../../i18n/globalMessages'; | 11 | import globalMessages from '../../../i18n/globalMessages'; |
17 | 12 | ||
18 | const messages = defineMessages({ | 13 | const messages = defineMessages({ |
@@ -50,40 +45,37 @@ const messages = defineMessages({ | |||
50 | }, | 45 | }, |
51 | }); | 46 | }); |
52 | 47 | ||
53 | class SettingsNavigation extends Component { | 48 | interface IProps extends Partial<StoresProps>, WrappedComponentProps { |
54 | static propTypes = { | 49 | serviceCount: number; |
55 | stores: PropTypes.shape({ | 50 | workspaceCount: number; |
56 | ui: PropTypes.instanceOf(UIStore).isRequired, | 51 | } |
57 | user: PropTypes.instanceOf(UserStore).isRequired, | 52 | |
58 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | 53 | @inject('stores', 'actions') |
59 | router: PropTypes.instanceOf(RouterStore).isRequired, | 54 | @observer |
60 | }).isRequired, | 55 | class SettingsNavigation extends Component<IProps> { |
61 | actions: PropTypes.shape({ | 56 | constructor(props: IProps) { |
62 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | 57 | super(props); |
63 | }).isRequired, | 58 | } |
64 | serviceCount: PropTypes.number.isRequired, | ||
65 | workspaceCount: PropTypes.number.isRequired, | ||
66 | }; | ||
67 | 59 | ||
68 | handleLogout() { | 60 | handleLogout(): void { |
69 | const isUsingWithoutAccount = | 61 | const isUsingWithoutAccount = |
70 | this.props.stores.settings.app.server === LOCAL_SERVER; | 62 | this.props.stores!.settings.app.server === LOCAL_SERVER; |
71 | 63 | ||
72 | // Remove current auth token | 64 | // Remove current auth token |
73 | localStorage.removeItem('authToken'); | 65 | localStorage.removeItem('authToken'); |
74 | 66 | ||
75 | if (isUsingWithoutAccount) { | 67 | if (isUsingWithoutAccount) { |
76 | // Reset server back to Ferdium API | 68 | // Reset server back to Ferdium API |
77 | this.props.actions.settings.update({ | 69 | this.props.actions!.settings.update({ |
78 | type: 'app', | 70 | type: 'app', |
79 | data: { | 71 | data: { |
80 | server: LIVE_FERDIUM_API, | 72 | server: LIVE_FERDIUM_API, |
81 | }, | 73 | }, |
82 | }); | 74 | }); |
83 | } | 75 | } |
84 | this.props.stores.user.isLoggingOut = true; | 76 | this.props.stores!.user.isLoggingOut = true; |
85 | 77 | ||
86 | this.props.stores.router.push('/auth/welcome'); | 78 | this.props.stores!.router.push('/auth/welcome'); |
87 | 79 | ||
88 | // Reload Ferdium, otherwise many settings won't sync correctly with the server | 80 | // Reload Ferdium, otherwise many settings won't sync correctly with the server |
89 | // after logging into another account | 81 | // after logging into another account |
@@ -91,10 +83,9 @@ class SettingsNavigation extends Component { | |||
91 | } | 83 | } |
92 | 84 | ||
93 | render() { | 85 | render() { |
94 | const { serviceCount, workspaceCount, stores } = this.props; | 86 | const { serviceCount, workspaceCount, stores, intl } = this.props; |
95 | const { intl } = this.props; | 87 | const isUsingWithoutAccount = stores!.settings.app.server === LOCAL_SERVER; |
96 | const isUsingWithoutAccount = stores.settings.app.server === LOCAL_SERVER; | 88 | const isUsingFranzServer = stores!.settings.app.server === LIVE_FRANZ_API; |
97 | const isUsingFranzServer = stores.settings.app.server === LIVE_FRANZ_API; | ||
98 | 89 | ||
99 | return ( | 90 | return ( |
100 | <div className="settings-navigation"> | 91 | <div className="settings-navigation"> |
@@ -163,12 +154,12 @@ class SettingsNavigation extends Component { | |||
163 | } | 154 | } |
164 | > | 155 | > |
165 | {intl.formatMessage(globalMessages.settings)} | 156 | {intl.formatMessage(globalMessages.settings)} |
166 | {stores.settings.app.automaticUpdates && | 157 | {stores!.settings.app.automaticUpdates && |
167 | (stores.ui.showServicesUpdatedInfoBar || | 158 | (stores!.ui.showServicesUpdatedInfoBar || |
168 | stores.app.updateStatus === | 159 | stores!.app.updateStatus === |
169 | stores.app.updateStatusTypes.AVAILABLE || | 160 | stores!.app.updateStatusTypes.AVAILABLE || |
170 | stores.app.updateStatus === | 161 | stores!.app.updateStatus === |
171 | stores.app.updateStatusTypes.DOWNLOADED) && ( | 162 | stores!.app.updateStatusTypes.DOWNLOADED) && ( |
172 | <span className="update-available">•</span> | 163 | <span className="update-available">•</span> |
173 | )} | 164 | )} |
174 | </NavLink> | 165 | </NavLink> |
@@ -195,7 +186,8 @@ class SettingsNavigation extends Component { | |||
195 | <span className="settings-navigation__expander" /> | 186 | <span className="settings-navigation__expander" /> |
196 | <button | 187 | <button |
197 | type="button" | 188 | type="button" |
198 | to="/auth/logout" | 189 | // @ts-ignore |
190 | to="/auth/logout" // TODO - [TS DEBT] Need to check if button take this prop | ||
199 | className="settings-navigation__link" | 191 | className="settings-navigation__link" |
200 | onClick={this.handleLogout.bind(this)} | 192 | onClick={this.handleLogout.bind(this)} |
201 | > | 193 | > |
@@ -208,6 +200,4 @@ class SettingsNavigation extends Component { | |||
208 | } | 200 | } |
209 | } | 201 | } |
210 | 202 | ||
211 | export default injectIntl( | 203 | export default injectIntl(SettingsNavigation); |
212 | inject('stores', 'actions')(observer(SettingsNavigation)), | ||
213 | ); | ||
diff --git a/src/components/settings/recipes/RecipeItem.js b/src/components/settings/recipes/RecipeItem.tsx index df5b42222..432e4e6a1 100644 --- a/src/components/settings/recipes/RecipeItem.js +++ b/src/components/settings/recipes/RecipeItem.tsx | |||
@@ -1,14 +1,17 @@ | |||
1 | import { Component } from 'react'; | 1 | import { Component, MouseEventHandler } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
3 | import RecipePreview from '../../../models/RecipePreview'; | ||
4 | 4 | ||
5 | import RecipePreviewModel from '../../../models/RecipePreview'; | 5 | interface IProps { |
6 | recipe: RecipePreview; | ||
7 | onClick: MouseEventHandler<HTMLButtonElement>; | ||
8 | } | ||
6 | 9 | ||
7 | class RecipeItem extends Component { | 10 | @observer |
8 | static propTypes = { | 11 | class RecipeItem extends Component<IProps> { |
9 | recipe: PropTypes.instanceOf(RecipePreviewModel).isRequired, | 12 | constructor(props: IProps) { |
10 | onClick: PropTypes.func.isRequired, | 13 | super(props); |
11 | }; | 14 | } |
12 | 15 | ||
13 | render() { | 16 | render() { |
14 | const { recipe, onClick } = this.props; | 17 | const { recipe, onClick } = this.props; |
@@ -18,7 +21,7 @@ class RecipeItem extends Component { | |||
18 | {recipe.isDevRecipe && ( | 21 | {recipe.isDevRecipe && ( |
19 | <span className="recipe-teaser__dev-badge">dev</span> | 22 | <span className="recipe-teaser__dev-badge">dev</span> |
20 | )} | 23 | )} |
21 | <img src={recipe.icons.svg} className="recipe-teaser__icon" alt="" /> | 24 | <img src={recipe.icons?.svg} className="recipe-teaser__icon" alt="" /> |
22 | <span className="recipe-teaser__label">{recipe.name}</span> | 25 | <span className="recipe-teaser__label">{recipe.name}</span> |
23 | {recipe.aliases && recipe.aliases.length > 0 && ( | 26 | {recipe.aliases && recipe.aliases.length > 0 && ( |
24 | <span className="recipe-teaser__alias_label"> | 27 | <span className="recipe-teaser__alias_label"> |
@@ -30,4 +33,4 @@ class RecipeItem extends Component { | |||
30 | } | 33 | } |
31 | } | 34 | } |
32 | 35 | ||
33 | export default observer(RecipeItem); | 36 | export default RecipeItem; |
diff --git a/src/components/settings/recipes/RecipesDashboard.jsx b/src/components/settings/recipes/RecipesDashboard.tsx index d6150d300..fc687bc79 100644 --- a/src/components/settings/recipes/RecipesDashboard.jsx +++ b/src/components/settings/recipes/RecipesDashboard.tsx | |||
@@ -1,17 +1,14 @@ | |||
1 | import { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import { observer } from 'mobx-react'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | ||
5 | import { NavLink } from 'react-router-dom'; | 4 | import { NavLink } from 'react-router-dom'; |
6 | 5 | import withStyles, { WithStylesProps } from 'react-jss'; | |
7 | import injectSheet from 'react-jss'; | ||
8 | |||
9 | import { mdiOpenInNew } from '@mdi/js'; | 6 | import { mdiOpenInNew } from '@mdi/js'; |
10 | import Button from '../../ui/button'; | 7 | import Button from '../../ui/button'; |
11 | import Input from '../../ui/input/index'; | 8 | import Input from '../../ui/input/index'; |
12 | import { H1, H2, H3 } from '../../ui/headline'; | 9 | import { H1, H2, H3 } from '../../ui/headline'; |
13 | import SearchInput from '../../ui/SearchInput'; | 10 | import SearchInput from '../../ui/SearchInput'; |
14 | import Infobox from '../../ui/Infobox'; | 11 | import Infobox from '../../ui/infobox'; |
15 | import RecipeItem from './RecipeItem'; | 12 | import RecipeItem from './RecipeItem'; |
16 | import Loader from '../../ui/Loader'; | 13 | import Loader from '../../ui/Loader'; |
17 | import Appear from '../../ui/effects/Appear'; | 14 | import Appear from '../../ui/effects/Appear'; |
@@ -109,28 +106,32 @@ const styles = { | |||
109 | }, | 106 | }, |
110 | }; | 107 | }; |
111 | 108 | ||
112 | class RecipesDashboard extends Component { | 109 | interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps { |
113 | static propTypes = { | 110 | recipes: RecipePreview[]; |
114 | recipes: MobxPropTypes.arrayOrObservableArray.isRequired, | 111 | customWebsiteRecipe?: RecipePreview; |
115 | customWebsiteRecipe: PropTypes.instanceOf(RecipePreview).isRequired, | 112 | isLoading: boolean; |
116 | isLoading: PropTypes.bool.isRequired, | 113 | hasLoadedRecipes: boolean; |
117 | hasLoadedRecipes: PropTypes.bool.isRequired, | 114 | showAddServiceInterface: (...args: any[]) => void; |
118 | showAddServiceInterface: PropTypes.func.isRequired, | 115 | searchRecipes: (e: string | null) => void; |
119 | searchRecipes: PropTypes.func.isRequired, | 116 | resetSearch: () => void; |
120 | resetSearch: PropTypes.func.isRequired, | 117 | serviceStatus: string[]; |
121 | serviceStatus: MobxPropTypes.arrayOrObservableArray.isRequired, | 118 | searchNeedle: string | null; |
122 | searchNeedle: PropTypes.string, | 119 | recipeFilter?: string; |
123 | recipeFilter: PropTypes.string, | 120 | recipeDirectory: string; |
124 | recipeDirectory: PropTypes.string.isRequired, | 121 | openRecipeDirectory: () => void; |
125 | openRecipeDirectory: PropTypes.func.isRequired, | 122 | openDevDocs: () => void; |
126 | openDevDocs: PropTypes.func.isRequired, | 123 | } |
127 | classes: PropTypes.object.isRequired, | 124 | |
128 | }; | 125 | interface IState { |
126 | searchNeedle: string | null; | ||
127 | recipeFilter: string; | ||
128 | } | ||
129 | 129 | ||
130 | static defaultProps = { | 130 | @observer |
131 | searchNeedle: '', | 131 | class RecipesDashboard extends Component<IProps, IState> { |
132 | recipeFilter: 'all', | 132 | constructor(props: IProps) { |
133 | }; | 133 | super(props); |
134 | } | ||
134 | 135 | ||
135 | render() { | 136 | render() { |
136 | const { | 137 | const { |
@@ -141,15 +142,15 @@ class RecipesDashboard extends Component { | |||
141 | showAddServiceInterface, | 142 | showAddServiceInterface, |
142 | searchRecipes, | 143 | searchRecipes, |
143 | resetSearch, | 144 | resetSearch, |
144 | serviceStatus, | 145 | serviceStatus = 'all', |
145 | searchNeedle, | 146 | searchNeedle = '', |
146 | recipeFilter, | 147 | recipeFilter, |
147 | recipeDirectory, | 148 | recipeDirectory, |
148 | openRecipeDirectory, | 149 | openRecipeDirectory, |
149 | openDevDocs, | 150 | openDevDocs, |
150 | classes, | 151 | classes, |
152 | intl, | ||
151 | } = this.props; | 153 | } = this.props; |
152 | const { intl } = this.props; | ||
153 | 154 | ||
154 | const communityRecipes = recipes.filter(r => !r.isDevRecipe); | 155 | const communityRecipes = recipes.filter(r => !r.isDevRecipe); |
155 | const devRecipes = recipes.filter(r => r.isDevRecipe); | 156 | const devRecipes = recipes.filter(r => r.isDevRecipe); |
@@ -307,5 +308,5 @@ class RecipesDashboard extends Component { | |||
307 | } | 308 | } |
308 | 309 | ||
309 | export default injectIntl( | 310 | export default injectIntl( |
310 | injectSheet(styles, { injectTheme: true })(observer(RecipesDashboard)), | 311 | withStyles(styles, { injectTheme: true })(RecipesDashboard), |
311 | ); | 312 | ); |
diff --git a/src/components/ui/SearchInput.tsx b/src/components/ui/SearchInput.tsx index 6a8c4de8f..39b8f95bf 100644 --- a/src/components/ui/SearchInput.tsx +++ b/src/components/ui/SearchInput.tsx | |||
@@ -1,43 +1,35 @@ | |||
1 | import { ChangeEvent, Component } from 'react'; | 1 | import { ChangeEvent, Component, ReactElement } from 'react'; |
2 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
3 | import classnames from 'classnames'; | 3 | import classnames from 'classnames'; |
4 | import { debounce } from 'lodash'; | 4 | import { debounce, noop } from 'lodash'; |
5 | import { mdiCloseCircleOutline, mdiMagnify } from '@mdi/js'; | 5 | import { mdiCloseCircleOutline, mdiMagnify } from '@mdi/js'; |
6 | import Icon from './icon'; | 6 | import Icon from './icon'; |
7 | 7 | ||
8 | type Props = { | 8 | interface IProps { |
9 | value: string; | 9 | value?: string; |
10 | placeholder: string; | 10 | placeholder: string; |
11 | className: string; | 11 | className?: string; |
12 | onChange: (e: ChangeEvent<HTMLInputElement>) => void; | 12 | onChange?: (e: string) => void; |
13 | onReset: () => void; | 13 | onReset: () => void; |
14 | name: string; | 14 | name?: string; |
15 | throttle: boolean; | 15 | throttle?: boolean; |
16 | throttleDelay: number; | 16 | throttleDelay?: number; |
17 | autoFocus: boolean; | 17 | autoFocus?: boolean; |
18 | }; | 18 | } |
19 | 19 | ||
20 | // Should this file be converted into the coding style similar to './toggle/index.tsx'? | 20 | interface IState { |
21 | class SearchInput extends Component<Props> { | 21 | value: string; |
22 | static defaultProps = { | 22 | } |
23 | value: '', | 23 | |
24 | placeholder: '', | 24 | @observer |
25 | className: '', | 25 | class SearchInput extends Component<IProps, IState> { |
26 | name: 'searchInput', | 26 | input: HTMLInputElement | null = null; |
27 | throttle: false, | 27 | |
28 | throttleDelay: 250, | 28 | constructor(props: IProps) { |
29 | onChange: () => null, | ||
30 | onReset: () => null, | ||
31 | autoFocus: false, | ||
32 | }; | ||
33 | |||
34 | input = null; | ||
35 | |||
36 | constructor(props: Props) { | ||
37 | super(props); | 29 | super(props); |
38 | 30 | ||
39 | this.state = { | 31 | this.state = { |
40 | value: props.value, | 32 | value: props.value || '', |
41 | }; | 33 | }; |
42 | 34 | ||
43 | this.throttledOnChange = debounce( | 35 | this.throttledOnChange = debounce( |
@@ -46,47 +38,47 @@ class SearchInput extends Component<Props> { | |||
46 | ); | 38 | ); |
47 | } | 39 | } |
48 | 40 | ||
49 | componentDidMount() { | 41 | componentDidMount(): void { |
50 | const { autoFocus } = this.props; | 42 | const { autoFocus = false } = this.props; |
51 | 43 | ||
52 | if (autoFocus) { | 44 | if (autoFocus && this.input) { |
53 | // @ts-expect-error Object is possibly 'null'. | ||
54 | this.input.focus(); | 45 | this.input.focus(); |
55 | } | 46 | } |
56 | } | 47 | } |
57 | 48 | ||
58 | onChange(e: ChangeEvent<HTMLInputElement>) { | 49 | onChange(e: ChangeEvent<HTMLInputElement>): void { |
59 | const { throttle, onChange } = this.props; | 50 | const { throttle = false, onChange = noop } = this.props; |
60 | const { value } = e.target; | 51 | const { value } = e.target; |
61 | this.setState({ value }); | 52 | this.setState({ value }); |
62 | 53 | ||
63 | if (throttle) { | 54 | if (throttle) { |
64 | e.persist(); | 55 | e.persist(); |
65 | // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'ChangeEvent<HTMLInputElement>'. | ||
66 | this.throttledOnChange(value); | 56 | this.throttledOnChange(value); |
67 | } else { | 57 | } else { |
68 | // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'ChangeEvent<HTMLInputElement>'. | ||
69 | onChange(value); | 58 | onChange(value); |
70 | } | 59 | } |
71 | } | 60 | } |
72 | 61 | ||
73 | throttledOnChange(e: ChangeEvent<HTMLInputElement>) { | 62 | throttledOnChange(e: string): void { |
74 | const { onChange } = this.props; | 63 | const { onChange = noop } = this.props; |
75 | 64 | ||
76 | onChange(e); | 65 | onChange(e); |
77 | } | 66 | } |
78 | 67 | ||
79 | reset() { | 68 | reset(): void { |
80 | const { onReset } = this.props; | 69 | const { onReset = noop } = this.props; |
81 | this.setState({ value: '' }); | 70 | this.setState({ value: '' }); |
82 | 71 | ||
83 | onReset(); | 72 | onReset(); |
84 | } | 73 | } |
85 | 74 | ||
86 | render() { | 75 | render(): ReactElement { |
87 | const { className, name, placeholder } = this.props; | 76 | const { |
88 | // @ts-expect-error Property 'value' does not exist on type 'Readonly<{}>'. | 77 | className = '', |
89 | const { value } = this.state; | 78 | name = 'searchInput', |
79 | placeholder = '', | ||
80 | } = this.props; | ||
81 | const { value = '' } = this.state; | ||
90 | 82 | ||
91 | return ( | 83 | return ( |
92 | <div className={classnames([className, 'search-input'])}> | 84 | <div className={classnames([className, 'search-input'])}> |
@@ -100,13 +92,12 @@ class SearchInput extends Component<Props> { | |||
100 | value={value} | 92 | value={value} |
101 | onChange={e => this.onChange(e)} | 93 | onChange={e => this.onChange(e)} |
102 | ref={ref => { | 94 | ref={ref => { |
103 | // @ts-expect-error Type 'HTMLInputElement | null' is not assignable to type 'null'. | ||
104 | this.input = ref; | 95 | this.input = ref; |
105 | }} | 96 | }} |
106 | /> | 97 | /> |
107 | </label> | 98 | </label> |
108 | {value.length > 0 && ( | 99 | {value.length > 0 && ( |
109 | <span onClick={() => this.reset()}> | 100 | <span onClick={() => this.reset()} onKeyDown={noop}> |
110 | <Icon icon={mdiCloseCircleOutline} /> | 101 | <Icon icon={mdiCloseCircleOutline} /> |
111 | </span> | 102 | </span> |
112 | )} | 103 | )} |
@@ -115,4 +106,4 @@ class SearchInput extends Component<Props> { | |||
115 | } | 106 | } |
116 | } | 107 | } |
117 | 108 | ||
118 | export default observer(SearchInput); | 109 | export default SearchInput; |
diff --git a/src/components/ui/effects/Appear.tsx b/src/components/ui/effects/Appear.tsx index 416017c83..bf097b6a6 100644 --- a/src/components/ui/effects/Appear.tsx +++ b/src/components/ui/effects/Appear.tsx | |||
@@ -1,16 +1,16 @@ | |||
1 | import { ReactNode, useEffect, useState } from 'react'; | 1 | import { ReactElement, ReactNode, useEffect, useState } from 'react'; |
2 | import { CSSTransitionGroup } from 'react-transition-group'; | 2 | import { CSSTransitionGroup } from 'react-transition-group'; |
3 | 3 | ||
4 | type Props = { | 4 | interface IProps { |
5 | children: ReactNode; | 5 | children: ReactNode; |
6 | transitionName: string; | 6 | transitionName?: string; |
7 | className?: string; | 7 | className?: string; |
8 | }; | 8 | } |
9 | const Appear = ({ | 9 | const Appear = ({ |
10 | children, | 10 | children, |
11 | transitionName = 'fadeIn', | 11 | transitionName = 'fadeIn', |
12 | className = '', | 12 | className = '', |
13 | }: Props) => { | 13 | }: IProps): ReactElement | null => { |
14 | const [mounted, setMounted] = useState(false); | 14 | const [mounted, setMounted] = useState(false); |
15 | 15 | ||
16 | useEffect(() => { | 16 | useEffect(() => { |
@@ -36,8 +36,4 @@ const Appear = ({ | |||
36 | ); | 36 | ); |
37 | }; | 37 | }; |
38 | 38 | ||
39 | Appear.defaultProps = { | ||
40 | className: '', | ||
41 | }; | ||
42 | |||
43 | export default Appear; | 39 | export default Appear; |
diff --git a/src/containers/settings/RecipesScreen.tsx b/src/containers/settings/RecipesScreen.tsx index fffdd39fa..abbb79b39 100644 --- a/src/containers/settings/RecipesScreen.tsx +++ b/src/containers/settings/RecipesScreen.tsx | |||
@@ -16,27 +16,30 @@ import RecipePreview from '../../models/RecipePreview'; | |||
16 | import { openPath } from '../../helpers/url-helpers'; | 16 | import { openPath } from '../../helpers/url-helpers'; |
17 | import withParams from '../../components/util/WithParams'; | 17 | import withParams from '../../components/util/WithParams'; |
18 | 18 | ||
19 | interface RecipesScreenProps extends StoresProps { | 19 | interface IProps extends Partial<StoresProps> { |
20 | params: Params; | 20 | params: Params; |
21 | } | 21 | } |
22 | 22 | ||
23 | class RecipesScreen extends Component<RecipesScreenProps> { | 23 | interface IState { |
24 | state: { | 24 | needle: string | null; |
25 | needle: string | null; | 25 | currentFilter: string; |
26 | currentFilter: string; | 26 | } |
27 | } = { | ||
28 | needle: null, | ||
29 | currentFilter: 'featured', | ||
30 | }; | ||
31 | 27 | ||
28 | @inject('stores', 'actions') | ||
29 | @observer | ||
30 | class RecipesScreen extends Component<IProps, IState> { | ||
32 | autorunDisposer: IReactionDisposer | null = null; | 31 | autorunDisposer: IReactionDisposer | null = null; |
33 | 32 | ||
34 | customRecipes: Recipe[] = []; | 33 | customRecipes: Recipe[] = []; |
35 | 34 | ||
36 | constructor(props: RecipesScreenProps) { | 35 | constructor(props: IProps) { |
37 | super(props); | 36 | super(props); |
38 | 37 | ||
39 | this.customRecipes = readJsonSync(asarRecipesPath('all.json')); | 38 | this.customRecipes = readJsonSync(asarRecipesPath('all.json')); |
39 | this.state = { | ||
40 | needle: null, | ||
41 | currentFilter: 'featured', | ||
42 | }; | ||
40 | } | 43 | } |
41 | 44 | ||
42 | componentDidMount(): void { | 45 | componentDidMount(): void { |
@@ -55,7 +58,7 @@ class RecipesScreen extends Component<RecipesScreenProps> { | |||
55 | } | 58 | } |
56 | 59 | ||
57 | componentWillUnmount(): void { | 60 | componentWillUnmount(): void { |
58 | this.props.stores.services.resetStatus(); | 61 | this.props.stores!.services.resetStatus(); |
59 | 62 | ||
60 | if (typeof this.autorunDisposer === 'function') { | 63 | if (typeof this.autorunDisposer === 'function') { |
61 | this.autorunDisposer(); | 64 | this.autorunDisposer(); |
@@ -66,7 +69,7 @@ class RecipesScreen extends Component<RecipesScreenProps> { | |||
66 | if (needle === '') { | 69 | if (needle === '') { |
67 | this.resetSearch(); | 70 | this.resetSearch(); |
68 | } else { | 71 | } else { |
69 | const { search } = this.props.actions.recipePreview; | 72 | const { search } = this.props.actions!.recipePreview; |
70 | this.setState({ needle }); | 73 | this.setState({ needle }); |
71 | search({ needle }); | 74 | search({ needle }); |
72 | } | 75 | } |
@@ -106,10 +109,8 @@ class RecipesScreen extends Component<RecipesScreenProps> { | |||
106 | } | 109 | } |
107 | 110 | ||
108 | render(): ReactElement { | 111 | render(): ReactElement { |
109 | const { recipePreviews, recipes, services } = this.props.stores; | 112 | const { recipePreviews, recipes, services } = this.props.stores!; |
110 | 113 | const { app: appActions, service: serviceActions } = this.props.actions!; | |
111 | const { app: appActions, service: serviceActions } = this.props.actions; | ||
112 | |||
113 | const filter = this.state.currentFilter; | 114 | const filter = this.state.currentFilter; |
114 | 115 | ||
115 | let recipeFilter; | 116 | let recipeFilter; |
@@ -163,7 +164,6 @@ class RecipesScreen extends Component<RecipesScreenProps> { | |||
163 | recipes={allRecipes} | 164 | recipes={allRecipes} |
164 | customWebsiteRecipe={customWebsiteRecipe} | 165 | customWebsiteRecipe={customWebsiteRecipe} |
165 | isLoading={isLoading} | 166 | isLoading={isLoading} |
166 | addedServiceCount={services.all.length} | ||
167 | hasLoadedRecipes={ | 167 | hasLoadedRecipes={ |
168 | recipePreviews.featuredRecipePreviewsRequest.wasExecuted | 168 | recipePreviews.featuredRecipePreviewsRequest.wasExecuted |
169 | } | 169 | } |
@@ -184,4 +184,4 @@ class RecipesScreen extends Component<RecipesScreenProps> { | |||
184 | } | 184 | } |
185 | } | 185 | } |
186 | 186 | ||
187 | export default withParams(inject('stores', 'actions')(observer(RecipesScreen))); | 187 | export default withParams(RecipesScreen); |
diff --git a/src/containers/settings/SettingsWindow.tsx b/src/containers/settings/SettingsWindow.tsx index 93bb08c7c..d2cdf3eb3 100644 --- a/src/containers/settings/SettingsWindow.tsx +++ b/src/containers/settings/SettingsWindow.tsx | |||
@@ -1,20 +1,23 @@ | |||
1 | import { inject, observer } from 'mobx-react'; | 1 | import { inject, observer } from 'mobx-react'; |
2 | import { Component, ReactPortal } from 'react'; | 2 | import { Component, ReactElement, ReactPortal } from 'react'; |
3 | import ReactDOM from 'react-dom'; | 3 | import ReactDOM from 'react-dom'; |
4 | import { Outlet } from 'react-router-dom'; | 4 | import { Outlet } from 'react-router-dom'; |
5 | |||
6 | import { StoresProps } from '../../@types/ferdium-components.types'; | 5 | import { StoresProps } from '../../@types/ferdium-components.types'; |
7 | import Navigation from '../../components/settings/navigation/SettingsNavigation'; | 6 | import Navigation from '../../components/settings/navigation/SettingsNavigation'; |
8 | import Layout from '../../components/settings/SettingsLayout'; | 7 | import Layout from '../../components/settings/SettingsLayout'; |
9 | import ErrorBoundary from '../../components/util/ErrorBoundary'; | 8 | import ErrorBoundary from '../../components/util/ErrorBoundary'; |
10 | import { workspaceStore } from '../../features/workspaces'; | 9 | import { workspaceStore } from '../../features/workspaces'; |
11 | 10 | ||
12 | class SettingsContainer extends Component<StoresProps> { | 11 | interface IProps extends Partial<StoresProps> {} |
13 | portalRoot: any; | 12 | |
13 | @inject('stores', 'actions') | ||
14 | @observer | ||
15 | class SettingsContainer extends Component<IProps> { | ||
16 | portalRoot: HTMLElement | null; | ||
14 | 17 | ||
15 | el: HTMLDivElement; | 18 | el: HTMLDivElement; |
16 | 19 | ||
17 | constructor(props: StoresProps) { | 20 | constructor(props: IProps) { |
18 | super(props); | 21 | super(props); |
19 | 22 | ||
20 | this.portalRoot = document.querySelector('#portalContainer'); | 23 | this.portalRoot = document.querySelector('#portalContainer'); |
@@ -22,7 +25,9 @@ class SettingsContainer extends Component<StoresProps> { | |||
22 | } | 25 | } |
23 | 26 | ||
24 | componentDidMount(): void { | 27 | componentDidMount(): void { |
25 | this.portalRoot.append(this.el); | 28 | if (this.portalRoot) { |
29 | this.portalRoot.append(this.el); | ||
30 | } | ||
26 | } | 31 | } |
27 | 32 | ||
28 | componentWillUnmount(): void { | 33 | componentWillUnmount(): void { |
@@ -31,11 +36,11 @@ class SettingsContainer extends Component<StoresProps> { | |||
31 | 36 | ||
32 | render(): ReactPortal { | 37 | render(): ReactPortal { |
33 | const { stores } = this.props; | 38 | const { stores } = this.props; |
34 | const { closeSettings } = this.props.actions.ui; | 39 | const { closeSettings } = this.props.actions!.ui; |
35 | 40 | ||
36 | const navigation = ( | 41 | const navigation: ReactElement = ( |
37 | <Navigation | 42 | <Navigation |
38 | serviceCount={stores.services.all.length} | 43 | serviceCount={stores!.services.all.length} |
39 | workspaceCount={workspaceStore.workspaces.length} | 44 | workspaceCount={workspaceStore.workspaces.length} |
40 | /> | 45 | /> |
41 | ); | 46 | ); |
@@ -51,4 +56,4 @@ class SettingsContainer extends Component<StoresProps> { | |||
51 | } | 56 | } |
52 | } | 57 | } |
53 | 58 | ||
54 | export default inject('stores', 'actions')(observer(SettingsContainer)); | 59 | export default SettingsContainer; |
diff --git a/src/features/quickSwitch/Component.js b/src/features/quickSwitch/Component.tsx index 16da22dce..fb85d61e1 100644 --- a/src/features/quickSwitch/Component.js +++ b/src/features/quickSwitch/Component.tsx | |||
@@ -1,17 +1,16 @@ | |||
1 | import { Component, createRef } from 'react'; | 1 | import { ChangeEvent, Component, createRef, ReactElement } from 'react'; |
2 | import { getCurrentWindow } from '@electron/remote'; | 2 | import { getCurrentWindow } from '@electron/remote'; |
3 | import PropTypes from 'prop-types'; | ||
4 | import { observer, inject } from 'mobx-react'; | 3 | import { observer, inject } from 'mobx-react'; |
5 | import { reaction } from 'mobx'; | 4 | import { reaction } from 'mobx'; |
6 | import injectSheet from 'react-jss'; | 5 | import withStyles, { WithStylesProps } from 'react-jss'; |
7 | import { defineMessages, injectIntl } from 'react-intl'; | 6 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
8 | import { compact, invoke } from 'lodash'; | 7 | import { compact, invoke, noop } from 'lodash'; |
9 | 8 | import { StoresProps } from '../../@types/ferdium-components.types'; | |
9 | import Service from '../../models/Service'; | ||
10 | import Input from '../../components/ui/input/index'; | 10 | import Input from '../../components/ui/input/index'; |
11 | import { H1 } from '../../components/ui/headline'; | 11 | import { H1 } from '../../components/ui/headline'; |
12 | import Modal from '../../components/ui/Modal'; | 12 | import Modal from '../../components/ui/Modal'; |
13 | import { state as ModalState } from './store'; | 13 | import { state as ModalState } from './store'; |
14 | import ServicesStore from '../../stores/ServicesStore'; | ||
15 | 14 | ||
16 | const messages = defineMessages({ | 15 | const messages = defineMessages({ |
17 | title: { | 16 | title: { |
@@ -75,17 +74,20 @@ const styles = theme => ({ | |||
75 | }, | 74 | }, |
76 | }); | 75 | }); |
77 | 76 | ||
78 | class QuickSwitchModal extends Component { | 77 | interface IProps |
79 | static propTypes = { | 78 | extends WithStylesProps<typeof styles>, |
80 | classes: PropTypes.object.isRequired, | 79 | Partial<StoresProps>, |
81 | }; | 80 | WrappedComponentProps {} |
82 | 81 | ||
83 | state = { | 82 | interface IState { |
84 | selected: 0, | 83 | selected: number; |
85 | search: '', | 84 | search: string; |
86 | wasPrevVisible: false, | 85 | wasPrevVisible: boolean; |
87 | }; | 86 | } |
88 | 87 | ||
88 | @inject('stores', 'actions') | ||
89 | @observer | ||
90 | class QuickSwitchModal extends Component<IProps, IState> { | ||
89 | ARROW_DOWN = 40; | 91 | ARROW_DOWN = 40; |
90 | 92 | ||
91 | ARROW_UP = 38; | 93 | ARROW_UP = 38; |
@@ -94,13 +96,19 @@ class QuickSwitchModal extends Component { | |||
94 | 96 | ||
95 | TAB = 9; | 97 | TAB = 9; |
96 | 98 | ||
97 | inputRef = createRef(); | 99 | inputRef = createRef<HTMLDivElement>(); |
98 | 100 | ||
99 | serviceElements = {}; | 101 | serviceElements = {}; |
100 | 102 | ||
101 | constructor(props) { | 103 | constructor(props) { |
102 | super(props); | 104 | super(props); |
103 | 105 | ||
106 | this.state = { | ||
107 | selected: 0, | ||
108 | search: '', | ||
109 | wasPrevVisible: false, | ||
110 | }; | ||
111 | |||
104 | this._handleKeyDown = this._handleKeyDown.bind(this); | 112 | this._handleKeyDown = this._handleKeyDown.bind(this); |
105 | this._handleSearchUpdate = this._handleSearchUpdate.bind(this); | 113 | this._handleSearchUpdate = this._handleSearchUpdate.bind(this); |
106 | this._handleVisibilityChange = this._handleVisibilityChange.bind(this); | 114 | this._handleVisibilityChange = this._handleVisibilityChange.bind(this); |
@@ -115,46 +123,46 @@ class QuickSwitchModal extends Component { | |||
115 | } | 123 | } |
116 | 124 | ||
117 | // Add global keydown listener when component mounts | 125 | // Add global keydown listener when component mounts |
118 | componentDidMount() { | 126 | componentDidMount(): void { |
119 | document.addEventListener('keydown', this._handleKeyDown); | 127 | document.addEventListener('keydown', this._handleKeyDown); |
120 | } | 128 | } |
121 | 129 | ||
122 | // Remove global keydown listener when component unmounts | 130 | // Remove global keydown listener when component unmounts |
123 | componentWillUnmount() { | 131 | componentWillUnmount(): void { |
124 | document.removeEventListener('keydown', this._handleKeyDown); | 132 | document.removeEventListener('keydown', this._handleKeyDown); |
125 | } | 133 | } |
126 | 134 | ||
127 | // Get currently shown services | 135 | // Get currently shown services |
128 | services() { | 136 | services(): Service[] { |
129 | let services = []; | 137 | let services: Service[] = []; |
130 | if ( | 138 | if ( |
131 | this.state.search && | 139 | this.state.search && |
132 | compact(invoke(this.state.search, 'match', /^[\da-z]/i)).length > 0 | 140 | compact(invoke(this.state.search, 'match', /^[\da-z]/i)).length > 0 |
133 | ) { | 141 | ) { |
134 | // Apply simple search algorythm to list of all services | 142 | // Apply simple search algorythm to list of all services |
135 | services = this.props.stores.services.allDisplayed; | 143 | services = this.props.stores!.services.allDisplayed; |
136 | services = services.filter( | 144 | services = services.filter( |
137 | service => | 145 | service => |
138 | service.name.toLowerCase().search(this.state.search.toLowerCase()) !== | 146 | service.name.toLowerCase().search(this.state.search.toLowerCase()) !== |
139 | -1, | 147 | -1, |
140 | ); | 148 | ); |
141 | } else if (this.props.stores.services.allDisplayed.length > 0) { | 149 | } else if (this.props.stores!.services.allDisplayed.length > 0) { |
142 | // Add the currently active service first | 150 | // Add the currently active service first |
143 | const currentService = this.props.stores.services.active; | 151 | const currentService = this.props.stores!.services.active; |
144 | if (currentService) { | 152 | if (currentService) { |
145 | services.push(currentService); | 153 | services.push(currentService); |
146 | } | 154 | } |
147 | 155 | ||
148 | // Add last used services to services array | 156 | // Add last used services to services array |
149 | for (const service of this.props.stores.services.lastUsedServices) { | 157 | for (const service of this.props.stores!.services.lastUsedServices) { |
150 | const tempService = this.props.stores.services.one(service); | 158 | const tempService = this.props.stores!.services.one(service); |
151 | if (tempService && !services.includes(tempService)) { | 159 | if (tempService && !services.includes(tempService)) { |
152 | services.push(tempService); | 160 | services.push(tempService); |
153 | } | 161 | } |
154 | } | 162 | } |
155 | 163 | ||
156 | // Add all other services in the default order | 164 | // Add all other services in the default order |
157 | for (const service of this.props.stores.services.allDisplayed) { | 165 | for (const service of this.props.stores!.services.allDisplayed) { |
158 | if (!services.includes(service)) { | 166 | if (!services.includes(service)) { |
159 | services.push(service); | 167 | services.push(service); |
160 | } | 168 | } |
@@ -164,10 +172,10 @@ class QuickSwitchModal extends Component { | |||
164 | return services; | 172 | return services; |
165 | } | 173 | } |
166 | 174 | ||
167 | openService(index) { | 175 | openService(index): void { |
168 | // Open service | 176 | // Open service |
169 | const service = this.services()[index]; | 177 | const service = this.services()[index]; |
170 | this.props.actions.service.setActive({ serviceId: service.id }); | 178 | this.props.actions!.service.setActive({ serviceId: service.id }); |
171 | 179 | ||
172 | // Reset and close modal | 180 | // Reset and close modal |
173 | this.setState({ | 181 | this.setState({ |
@@ -179,7 +187,7 @@ class QuickSwitchModal extends Component { | |||
179 | 187 | ||
180 | // Change the selected service | 188 | // Change the selected service |
181 | // factor should be -1 or 1 | 189 | // factor should be -1 or 1 |
182 | changeSelected(factor) { | 190 | changeSelected(factor: number): any { |
183 | this.setState(state => { | 191 | this.setState(state => { |
184 | let newSelected = state.selected + factor; | 192 | let newSelected = state.selected + factor; |
185 | const services = this.services().length; | 193 | const services = this.services().length; |
@@ -204,7 +212,7 @@ class QuickSwitchModal extends Component { | |||
204 | } | 212 | } |
205 | 213 | ||
206 | // Handle global key presses to change the selection | 214 | // Handle global key presses to change the selection |
207 | _handleKeyDown(event) { | 215 | _handleKeyDown(event: KeyboardEvent): void { |
208 | if (ModalState.isModalVisible) { | 216 | if (ModalState.isModalVisible) { |
209 | switch (event.keyCode) { | 217 | switch (event.keyCode) { |
210 | case this.ARROW_DOWN: | 218 | case this.ARROW_DOWN: |
@@ -230,13 +238,13 @@ class QuickSwitchModal extends Component { | |||
230 | } | 238 | } |
231 | 239 | ||
232 | // Handle update of the search query | 240 | // Handle update of the search query |
233 | _handleSearchUpdate(evt) { | 241 | _handleSearchUpdate(event: ChangeEvent<HTMLInputElement>): void { |
234 | this.setState({ | 242 | this.setState({ |
235 | search: evt.target.value, | 243 | search: event.target.value, |
236 | }); | 244 | }); |
237 | } | 245 | } |
238 | 246 | ||
239 | _handleVisibilityChange() { | 247 | _handleVisibilityChange(): void { |
240 | const { isModalVisible } = ModalState; | 248 | const { isModalVisible } = ModalState; |
241 | 249 | ||
242 | if (isModalVisible && !this.state.wasPrevVisible) { | 250 | if (isModalVisible && !this.state.wasPrevVisible) { |
@@ -273,21 +281,16 @@ class QuickSwitchModal extends Component { | |||
273 | } | 281 | } |
274 | 282 | ||
275 | // Close this modal | 283 | // Close this modal |
276 | close() { | 284 | close(): void { |
277 | ModalState.isModalVisible = false; | 285 | ModalState.isModalVisible = false; |
278 | } | 286 | } |
279 | 287 | ||
280 | render() { | 288 | render(): ReactElement { |
281 | const { isModalVisible } = ModalState; | 289 | const { isModalVisible } = ModalState; |
282 | |||
283 | const { openService } = this; | 290 | const { openService } = this; |
284 | 291 | const { classes, intl } = this.props; | |
285 | const { classes } = this.props; | ||
286 | |||
287 | const services = this.services(); | 292 | const services = this.services(); |
288 | 293 | ||
289 | const { intl } = this.props; | ||
290 | |||
291 | return ( | 294 | return ( |
292 | <Modal | 295 | <Modal |
293 | isOpen={isModalVisible} | 296 | isOpen={isModalVisible} |
@@ -316,6 +319,7 @@ class QuickSwitchModal extends Component { | |||
316 | : '' | 319 | : '' |
317 | } service`} | 320 | } service`} |
318 | onClick={() => openService(index)} | 321 | onClick={() => openService(index)} |
322 | onKeyDown={noop} | ||
319 | key={service.id} | 323 | key={service.id} |
320 | ref={el => { | 324 | ref={el => { |
321 | this.serviceElements[index] = el; | 325 | this.serviceElements[index] = el; |
@@ -340,18 +344,6 @@ class QuickSwitchModal extends Component { | |||
340 | } | 344 | } |
341 | } | 345 | } |
342 | 346 | ||
343 | QuickSwitchModal.propTypes = { | ||
344 | stores: PropTypes.shape({ | ||
345 | services: PropTypes.instanceOf(ServicesStore).isRequired, | ||
346 | }).isRequired, | ||
347 | actions: PropTypes.shape({ | ||
348 | service: PropTypes.instanceOf(ServicesStore).isRequired, | ||
349 | }).isRequired, | ||
350 | classes: PropTypes.object.isRequired, | ||
351 | }; | ||
352 | |||
353 | export default injectIntl( | 347 | export default injectIntl( |
354 | injectSheet(styles, { injectTheme: true })( | 348 | withStyles(styles, { injectTheme: true })(QuickSwitchModal), |
355 | inject('stores', 'actions')(observer(QuickSwitchModal)), | ||
356 | ), | ||
357 | ); | 349 | ); |
diff --git a/src/models/RecipePreview.ts b/src/models/RecipePreview.ts index fb8cb3e3e..33b5e1432 100644 --- a/src/models/RecipePreview.ts +++ b/src/models/RecipePreview.ts | |||
@@ -4,6 +4,10 @@ interface IRecipePreview { | |||
4 | icon: string; | 4 | icon: string; |
5 | featured: boolean; | 5 | featured: boolean; |
6 | aliases: string[]; | 6 | aliases: string[]; |
7 | isDevRecipe?: boolean; | ||
8 | icons?: { | ||
9 | svg: string; | ||
10 | }; | ||
7 | } | 11 | } |
8 | 12 | ||
9 | export default class RecipePreview { | 13 | export default class RecipePreview { |
@@ -17,6 +21,12 @@ export default class RecipePreview { | |||
17 | 21 | ||
18 | aliases: string[] = []; | 22 | aliases: string[] = []; |
19 | 23 | ||
24 | isDevRecipe?: boolean; | ||
25 | |||
26 | icons?: { | ||
27 | svg: string; | ||
28 | }; | ||
29 | |||
20 | constructor(data: IRecipePreview) { | 30 | constructor(data: IRecipePreview) { |
21 | if (!data) { | 31 | if (!data) { |
22 | throw new Error('RecipePreview config not valid'); | 32 | throw new Error('RecipePreview config not valid'); |