diff options
author | muhamedsalih-tw <104364298+muhamedsalih-tw@users.noreply.github.com> | 2022-11-17 05:45:39 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-17 00:15:39 +0000 |
commit | d9502c7516bc2d4ae467c6ea8a2e4816b0885f37 (patch) | |
tree | b339c587a5529ac26d52cfc12d9972a8a00255e6 | |
parent | Transform JSX components to TSX (#755) (diff) | |
download | ferdium-app-d9502c7516bc2d4ae467c6ea8a2e4816b0885f37.tar.gz ferdium-app-d9502c7516bc2d4ae467c6ea8a2e4816b0885f37.tar.zst ferdium-app-d9502c7516bc2d4ae467c6ea8a2e4816b0885f37.zip |
Transfrom workspace components to ts (#775)
19 files changed, 270 insertions, 260 deletions
diff --git a/src/@types/stores.types.ts b/src/@types/stores.types.ts index bbb37cff9..edea41ea9 100644 --- a/src/@types/stores.types.ts +++ b/src/@types/stores.types.ts | |||
@@ -2,8 +2,8 @@ import Workspace from '../features/workspaces/models/Workspace'; | |||
2 | import Recipe from '../models/Recipe'; | 2 | import Recipe from '../models/Recipe'; |
3 | import Service from '../models/Service'; | 3 | import Service from '../models/Service'; |
4 | import User from '../models/User'; | 4 | import User from '../models/User'; |
5 | import { Request } from '../stores/lib/Request'; | 5 | import Request from '../stores/lib/Request'; |
6 | import { CachedRequest } from '../stores/lib/CachedRequest'; | 6 | import CachedRequest from '../stores/lib/CachedRequest'; |
7 | import Reaction from '../stores/lib/Reaction'; | 7 | import Reaction from '../stores/lib/Reaction'; |
8 | 8 | ||
9 | // TODO: This file will be removed in the future when all stores are | 9 | // TODO: This file will be removed in the future when all stores are |
diff --git a/src/components/auth/AuthLayout.tsx b/src/components/auth/AuthLayout.tsx index 527c2bb74..eeb93b83b 100644 --- a/src/components/auth/AuthLayout.tsx +++ b/src/components/auth/AuthLayout.tsx | |||
@@ -8,7 +8,7 @@ import { observer } from 'mobx-react'; | |||
8 | import { TitleBar } from 'electron-react-titlebar/renderer'; | 8 | import { TitleBar } from 'electron-react-titlebar/renderer'; |
9 | import { injectIntl, WrappedComponentProps } from 'react-intl'; | 9 | import { injectIntl, WrappedComponentProps } from 'react-intl'; |
10 | import { mdiFlash } from '@mdi/js'; | 10 | import { mdiFlash } from '@mdi/js'; |
11 | import { GlobalError } from '../../@types/ferdium-components.types'; | 11 | import { Response } from 'electron'; |
12 | import Link from '../ui/Link'; | 12 | import Link from '../ui/Link'; |
13 | import InfoBar from '../ui/InfoBar'; | 13 | import InfoBar from '../ui/InfoBar'; |
14 | import { Component as PublishDebugInfo } from '../../features/publishDebugInfo'; | 14 | import { Component as PublishDebugInfo } from '../../features/publishDebugInfo'; |
@@ -22,7 +22,7 @@ import { serverName } from '../../api/apiBase'; | |||
22 | 22 | ||
23 | export interface IProps extends WrappedComponentProps { | 23 | export interface IProps extends WrappedComponentProps { |
24 | children: ReactElement; | 24 | children: ReactElement; |
25 | error: GlobalError; | 25 | error: Response; |
26 | isOnline: boolean; | 26 | isOnline: boolean; |
27 | isAPIHealthy: boolean; | 27 | isAPIHealthy: boolean; |
28 | retryHealthCheck: MouseEventHandler<HTMLButtonElement>; | 28 | retryHealthCheck: MouseEventHandler<HTMLButtonElement>; |
@@ -106,9 +106,7 @@ class AuthLayout extends Component<IProps, IState> { | |||
106 | )} | 106 | )} |
107 | <div className="auth__layout"> | 107 | <div className="auth__layout"> |
108 | {/* Inject globalError into children */} | 108 | {/* Inject globalError into children */} |
109 | {cloneElement(children, { | 109 | {cloneElement(children, { error })} |
110 | error, | ||
111 | })} | ||
112 | </div> | 110 | </div> |
113 | {/* </div> */} | 111 | {/* </div> */} |
114 | <Link | 112 | <Link |
diff --git a/src/features/workspaces/components/CreateWorkspaceForm.js b/src/features/workspaces/components/CreateWorkspaceForm.tsx index fac84bed0..eafe9f36a 100644 --- a/src/features/workspaces/components/CreateWorkspaceForm.js +++ b/src/features/workspaces/components/CreateWorkspaceForm.tsx | |||
@@ -1,9 +1,7 @@ | |||
1 | import { Component } from 'react'; | 1 | import { Component, ReactElement } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 3 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
5 | import injectSheet from 'react-jss'; | 4 | import withStyles, { WithStylesProps } from 'react-jss'; |
6 | |||
7 | import Input from '../../../components/ui/input/index'; | 5 | import Input from '../../../components/ui/input/index'; |
8 | import Button from '../../../components/ui/button'; | 6 | import Button from '../../../components/ui/button'; |
9 | import Form from '../../../lib/Form'; | 7 | import Form from '../../../lib/Form'; |
@@ -34,47 +32,49 @@ const styles = { | |||
34 | }, | 32 | }, |
35 | }; | 33 | }; |
36 | 34 | ||
37 | class CreateWorkspaceForm extends Component { | 35 | interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps { |
38 | static propTypes = { | 36 | isSubmitting: boolean; |
39 | classes: PropTypes.object.isRequired, | 37 | onSubmit: (...args: any[]) => void; |
40 | isSubmitting: PropTypes.bool.isRequired, | 38 | } |
41 | onSubmit: PropTypes.func.isRequired, | 39 | |
42 | }; | 40 | @observer |
41 | class CreateWorkspaceForm extends Component<IProps> { | ||
42 | form: Form; | ||
43 | 43 | ||
44 | form = (() => { | 44 | constructor(props: IProps) { |
45 | const { intl } = this.props; | 45 | super(props); |
46 | return new Form({ | 46 | |
47 | this.form = new Form({ | ||
47 | fields: { | 48 | fields: { |
48 | name: { | 49 | name: { |
49 | label: intl.formatMessage(messages.name), | 50 | label: this.props.intl.formatMessage(messages.name), |
50 | placeholder: intl.formatMessage(messages.name), | 51 | placeholder: this.props.intl.formatMessage(messages.name), |
51 | value: '', | 52 | value: '', |
52 | validators: [required], | 53 | validators: [required], |
53 | }, | 54 | }, |
54 | }, | 55 | }, |
55 | }); | 56 | }); |
56 | })(); | 57 | } |
57 | 58 | ||
58 | submitForm() { | 59 | submitForm(): void { |
59 | const { form } = this; | 60 | this.form.submit({ |
60 | form.submit({ | 61 | onSuccess: async form => { |
61 | onSuccess: async f => { | ||
62 | const { onSubmit } = this.props; | 62 | const { onSubmit } = this.props; |
63 | const values = f.values(); | 63 | const values = form.values(); |
64 | onSubmit(values); | 64 | onSubmit(values); |
65 | }, | 65 | }, |
66 | }); | 66 | }); |
67 | } | 67 | } |
68 | 68 | ||
69 | render() { | 69 | render(): ReactElement { |
70 | const { intl } = this.props; | 70 | const { classes, isSubmitting, intl } = this.props; |
71 | const { classes, isSubmitting } = this.props; | ||
72 | const { form } = this; | 71 | const { form } = this; |
72 | |||
73 | return ( | 73 | return ( |
74 | <div className={classes.form}> | 74 | <div className={classes.form}> |
75 | <Input | 75 | <Input |
76 | className={classes.input} | ||
77 | {...form.$('name').bind()} | 76 | {...form.$('name').bind()} |
77 | className={classes.input} | ||
78 | showLabel={false} | 78 | showLabel={false} |
79 | onEnterKey={this.submitForm.bind(this, form)} | 79 | onEnterKey={this.submitForm.bind(this, form)} |
80 | focus={workspaceStore.isUserAllowedToUseFeature} | 80 | focus={workspaceStore.isUserAllowedToUseFeature} |
@@ -93,5 +93,5 @@ class CreateWorkspaceForm extends Component { | |||
93 | } | 93 | } |
94 | 94 | ||
95 | export default injectIntl( | 95 | export default injectIntl( |
96 | injectSheet(styles, { injectTheme: true })(observer(CreateWorkspaceForm)), | 96 | withStyles(styles, { injectTheme: true })(CreateWorkspaceForm), |
97 | ); | 97 | ); |
diff --git a/src/features/workspaces/components/EditWorkspaceForm.js b/src/features/workspaces/components/EditWorkspaceForm.tsx index ff4e71260..a860ac2e8 100644 --- a/src/features/workspaces/components/EditWorkspaceForm.js +++ b/src/features/workspaces/components/EditWorkspaceForm.tsx | |||
@@ -1,11 +1,10 @@ | |||
1 | import { Component } from 'react'; | 1 | import { Component, ReactElement } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 3 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
5 | import { Link } from 'react-router-dom'; | 4 | import { Link } from 'react-router-dom'; |
6 | import injectSheet from 'react-jss'; | 5 | import withStyles, { WithStylesProps } from 'react-jss'; |
7 | import Infobox from '../../../components/ui/infobox/index'; | 6 | import Infobox from '../../../components/ui/infobox/index'; |
8 | import Input from '../../../components/ui/input/index'; | 7 | import Input from '../../../components/ui/input'; |
9 | import Button from '../../../components/ui/button'; | 8 | import Button from '../../../components/ui/button'; |
10 | import Workspace from '../models/Workspace'; | 9 | import Workspace from '../models/Workspace'; |
11 | import Service from '../../../models/Service'; | 10 | import Service from '../../../models/Service'; |
@@ -69,30 +68,36 @@ const styles = { | |||
69 | }, | 68 | }, |
70 | }; | 69 | }; |
71 | 70 | ||
72 | class EditWorkspaceForm extends Component { | 71 | interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps { |
73 | static propTypes = { | 72 | onDelete: () => void; |
74 | classes: PropTypes.object.isRequired, | 73 | onSave: (...args: any[]) => void; |
75 | onDelete: PropTypes.func.isRequired, | 74 | services: Service[]; |
76 | onSave: PropTypes.func.isRequired, | 75 | workspace: Workspace; |
77 | services: PropTypes.arrayOf(PropTypes.instanceOf(Service)).isRequired, | 76 | updateWorkspaceRequest: Request; |
78 | workspace: PropTypes.instanceOf(Workspace).isRequired, | 77 | deleteWorkspaceRequest: Request; |
79 | updateWorkspaceRequest: PropTypes.instanceOf(Request).isRequired, | 78 | } |
80 | deleteWorkspaceRequest: PropTypes.instanceOf(Request).isRequired, | 79 | |
81 | }; | 80 | @observer |
81 | class EditWorkspaceForm extends Component<IProps> { | ||
82 | form: Form; | ||
83 | |||
84 | constructor(props: IProps) { | ||
85 | super(props); | ||
82 | 86 | ||
83 | form = this.prepareWorkspaceForm(this.props.workspace); | 87 | this.form = this.prepareWorkspaceForm(this.props.workspace); |
88 | } | ||
84 | 89 | ||
85 | // eslint-disable-next-line react/no-deprecated | 90 | UNSAFE_componentWillReceiveProps(nextProps): void { |
86 | componentWillReceiveProps(nextProps) { | ||
87 | const { workspace } = this.props; | 91 | const { workspace } = this.props; |
88 | if (workspace.id !== nextProps.workspace.id) { | 92 | if (workspace.id !== nextProps.workspace.id) { |
89 | this.form = this.prepareWorkspaceForm(nextProps.workspace); | 93 | this.form = this.prepareWorkspaceForm(nextProps.workspace); |
90 | } | 94 | } |
91 | } | 95 | } |
92 | 96 | ||
93 | prepareWorkspaceForm(workspace) { | 97 | prepareWorkspaceForm(workspace: Workspace): Form { |
94 | const { intl, updateWorkspaceRequest } = this.props; | 98 | const { intl, updateWorkspaceRequest } = this.props; |
95 | updateWorkspaceRequest.reset(); | 99 | updateWorkspaceRequest.reset(); |
100 | |||
96 | return new Form({ | 101 | return new Form({ |
97 | fields: { | 102 | fields: { |
98 | name: { | 103 | name: { |
@@ -105,6 +110,7 @@ class EditWorkspaceForm extends Component { | |||
105 | label: intl.formatMessage(messages.keepLoaded), | 110 | label: intl.formatMessage(messages.keepLoaded), |
106 | value: workspace.services.includes(KEEP_WS_LOADED_USID), | 111 | value: workspace.services.includes(KEEP_WS_LOADED_USID), |
107 | default: false, | 112 | default: false, |
113 | type: 'checkbox', | ||
108 | }, | 114 | }, |
109 | services: { | 115 | services: { |
110 | value: [...workspace.services], | 116 | value: [...workspace.services], |
@@ -113,7 +119,7 @@ class EditWorkspaceForm extends Component { | |||
113 | }); | 119 | }); |
114 | } | 120 | } |
115 | 121 | ||
116 | save(form) { | 122 | save(form): void { |
117 | this.props.updateWorkspaceRequest.reset(); | 123 | this.props.updateWorkspaceRequest.reset(); |
118 | form.submit({ | 124 | form.submit({ |
119 | onSuccess: async f => { | 125 | onSuccess: async f => { |
@@ -125,12 +131,12 @@ class EditWorkspaceForm extends Component { | |||
125 | }); | 131 | }); |
126 | } | 132 | } |
127 | 133 | ||
128 | delete() { | 134 | delete(): void { |
129 | const { onDelete } = this.props; | 135 | const { onDelete } = this.props; |
130 | onDelete(); | 136 | onDelete(); |
131 | } | 137 | } |
132 | 138 | ||
133 | toggleService(service) { | 139 | toggleService(service: Service): void { |
134 | const servicesField = this.form.$('services'); | 140 | const servicesField = this.form.$('services'); |
135 | const serviceIds = servicesField.value; | 141 | const serviceIds = servicesField.value; |
136 | if (serviceIds.includes(service.id)) { | 142 | if (serviceIds.includes(service.id)) { |
@@ -141,19 +147,20 @@ class EditWorkspaceForm extends Component { | |||
141 | servicesField.set(serviceIds); | 147 | servicesField.set(serviceIds); |
142 | } | 148 | } |
143 | 149 | ||
144 | render() { | 150 | render(): ReactElement { |
145 | const { intl } = this.props; | ||
146 | const { | 151 | const { |
147 | classes, | 152 | classes, |
148 | workspace, | 153 | workspace, |
149 | services, | 154 | services, |
150 | deleteWorkspaceRequest, | 155 | deleteWorkspaceRequest, |
151 | updateWorkspaceRequest, | 156 | updateWorkspaceRequest, |
157 | intl, | ||
152 | } = this.props; | 158 | } = this.props; |
153 | const { form } = this; | 159 | const { form } = this; |
154 | const workspaceServices = form.$('services').value; | 160 | const workspaceServices = form.$('services').value; |
155 | const isDeleting = deleteWorkspaceRequest.isExecuting; | 161 | const isDeleting = deleteWorkspaceRequest.isExecuting; |
156 | const isSaving = updateWorkspaceRequest.isExecuting; | 162 | const isSaving = updateWorkspaceRequest.isExecuting; |
163 | |||
157 | return ( | 164 | return ( |
158 | <div className="settings__main"> | 165 | <div className="settings__main"> |
159 | <div className="settings__header"> | 166 | <div className="settings__header"> |
@@ -195,12 +202,12 @@ class EditWorkspaceForm extends Component { | |||
195 | </div> | 202 | </div> |
196 | ) : ( | 203 | ) : ( |
197 | <> | 204 | <> |
198 | {services.map(s => ( | 205 | {services.map(service => ( |
199 | <WorkspaceServiceListItem | 206 | <WorkspaceServiceListItem |
200 | key={s.id} | 207 | key={service.id} |
201 | service={s} | 208 | service={service} |
202 | isInWorkspace={workspaceServices.includes(s.id)} | 209 | isInWorkspace={workspaceServices.includes(service.id)} |
203 | onToggle={() => this.toggleService(s)} | 210 | onToggle={() => this.toggleService(service)} |
204 | /> | 211 | /> |
205 | ))} | 212 | ))} |
206 | </> | 213 | </> |
@@ -236,5 +243,5 @@ class EditWorkspaceForm extends Component { | |||
236 | } | 243 | } |
237 | 244 | ||
238 | export default injectIntl( | 245 | export default injectIntl( |
239 | injectSheet(styles, { injectTheme: true })(observer(EditWorkspaceForm)), | 246 | withStyles(styles, { injectTheme: true })(EditWorkspaceForm), |
240 | ); | 247 | ); |
diff --git a/src/features/workspaces/components/WorkspaceDrawer.tsx b/src/features/workspaces/components/WorkspaceDrawer.tsx index bdbebdb0a..61284d81a 100644 --- a/src/features/workspaces/components/WorkspaceDrawer.tsx +++ b/src/features/workspaces/components/WorkspaceDrawer.tsx | |||
@@ -11,7 +11,6 @@ import WorkspaceDrawerItem from './WorkspaceDrawerItem'; | |||
11 | import workspaceActions from '../actions'; | 11 | import workspaceActions from '../actions'; |
12 | import { workspaceStore } from '../index'; | 12 | import { workspaceStore } from '../index'; |
13 | import { getUserWorkspacesRequest } from '../api'; | 13 | import { getUserWorkspacesRequest } from '../api'; |
14 | import Service from '../../../models/Service'; | ||
15 | import Workspace from '../models/Workspace'; | 14 | import Workspace from '../models/Workspace'; |
16 | 15 | ||
17 | const messages = defineMessages({ | 16 | const messages = defineMessages({ |
@@ -90,7 +89,7 @@ const styles = theme => ({ | |||
90 | }); | 89 | }); |
91 | 90 | ||
92 | interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps { | 91 | interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps { |
93 | getServicesForWorkspace: (workspace: Workspace | null) => Service[]; | 92 | getServicesForWorkspace: (workspace: Workspace | null) => string[]; |
94 | } | 93 | } |
95 | 94 | ||
96 | @observer | 95 | @observer |
@@ -150,7 +149,9 @@ class WorkspaceDrawer extends Component<IProps> { | |||
150 | name={workspace.name} | 149 | name={workspace.name} |
151 | isActive={actualWorkspace === workspace} | 150 | isActive={actualWorkspace === workspace} |
152 | onClick={() => { | 151 | onClick={() => { |
153 | if (actualWorkspace === workspace) return; | 152 | if (actualWorkspace === workspace) { |
153 | return; | ||
154 | } | ||
154 | workspaceActions.activate({ workspace }); | 155 | workspaceActions.activate({ workspace }); |
155 | workspaceActions.toggleWorkspaceDrawer(); | 156 | workspaceActions.toggleWorkspaceDrawer(); |
156 | }} | 157 | }} |
diff --git a/src/features/workspaces/components/WorkspaceDrawerItem.js b/src/features/workspaces/components/WorkspaceDrawerItem.tsx index 22c0a39d9..0ad56d1ae 100644 --- a/src/features/workspaces/components/WorkspaceDrawerItem.js +++ b/src/features/workspaces/components/WorkspaceDrawerItem.tsx | |||
@@ -1,10 +1,11 @@ | |||
1 | import { Menu } from '@electron/remote'; | 1 | import { Component, MouseEventHandler, ReactElement } from 'react'; |
2 | import { Component } from 'react'; | ||
3 | import PropTypes from 'prop-types'; | ||
4 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
5 | import injectSheet from 'react-jss'; | 3 | import withStyles, { WithStylesProps } from 'react-jss'; |
6 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
7 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
6 | import { noop } from 'lodash'; | ||
7 | import { Menu } from '@electron/remote'; | ||
8 | import { MenuItemConstructorOptions } from 'electron'; | ||
8 | import { altKey, cmdOrCtrlShortcutKey } from '../../../environment'; | 9 | import { altKey, cmdOrCtrlShortcutKey } from '../../../environment'; |
9 | 10 | ||
10 | const messages = defineMessages({ | 11 | const messages = defineMessages({ |
@@ -18,11 +19,10 @@ const messages = defineMessages({ | |||
18 | }, | 19 | }, |
19 | }); | 20 | }); |
20 | 21 | ||
21 | let itemTransition = 'none'; | 22 | const itemTransition = |
22 | 23 | window && window.matchMedia('(prefers-reduced-motion: no-preference)') | |
23 | if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | 24 | ? 'background-color 300ms ease-out' |
24 | itemTransition = 'background-color 300ms ease-out'; | 25 | : 'none'; |
25 | } | ||
26 | 26 | ||
27 | const styles = theme => ({ | 27 | const styles = theme => ({ |
28 | item: { | 28 | item: { |
@@ -65,35 +65,30 @@ const styles = theme => ({ | |||
65 | }, | 65 | }, |
66 | }); | 66 | }); |
67 | 67 | ||
68 | class WorkspaceDrawerItem extends Component { | 68 | interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps { |
69 | static propTypes = { | 69 | isActive: boolean; |
70 | classes: PropTypes.object.isRequired, | 70 | name: string; |
71 | isActive: PropTypes.bool.isRequired, | 71 | onClick: MouseEventHandler<HTMLInputElement>; |
72 | name: PropTypes.string.isRequired, | 72 | services: string[]; |
73 | onClick: PropTypes.func.isRequired, | 73 | onContextMenuEditClick?: () => void | null; |
74 | services: PropTypes.arrayOf(PropTypes.string).isRequired, | 74 | shortcutIndex: number; |
75 | onContextMenuEditClick: PropTypes.func, | 75 | } |
76 | shortcutIndex: PropTypes.number.isRequired, | ||
77 | }; | ||
78 | |||
79 | static defaultProps = { | ||
80 | onContextMenuEditClick: null, | ||
81 | }; | ||
82 | 76 | ||
83 | render() { | 77 | @observer |
78 | class WorkspaceDrawerItem extends Component<IProps> { | ||
79 | render(): ReactElement { | ||
84 | const { | 80 | const { |
85 | classes, | 81 | classes, |
86 | isActive, | 82 | isActive, |
87 | name, | 83 | name, |
88 | onClick, | 84 | onClick, |
89 | onContextMenuEditClick, | 85 | onContextMenuEditClick = null, |
90 | services, | 86 | services, |
91 | shortcutIndex, | 87 | shortcutIndex, |
88 | intl, | ||
92 | } = this.props; | 89 | } = this.props; |
93 | 90 | ||
94 | const { intl } = this.props; | 91 | const contextMenuTemplate: MenuItemConstructorOptions[] = [ |
95 | |||
96 | const contextMenuTemplate = [ | ||
97 | { | 92 | { |
98 | label: name, | 93 | label: name, |
99 | enabled: false, | 94 | enabled: false, |
@@ -103,7 +98,7 @@ class WorkspaceDrawerItem extends Component { | |||
103 | }, | 98 | }, |
104 | { | 99 | { |
105 | label: intl.formatMessage(messages.contextMenuEdit), | 100 | label: intl.formatMessage(messages.contextMenuEdit), |
106 | click: onContextMenuEditClick, | 101 | click: onContextMenuEditClick || noop, |
107 | }, | 102 | }, |
108 | ]; | 103 | ]; |
109 | 104 | ||
@@ -116,7 +111,12 @@ class WorkspaceDrawerItem extends Component { | |||
116 | isActive ? classes.isActiveItem : null, | 111 | isActive ? classes.isActiveItem : null, |
117 | ])} | 112 | ])} |
118 | onClick={onClick} | 113 | onClick={onClick} |
119 | onContextMenu={() => onContextMenuEditClick && contextMenu.popup()} | 114 | onContextMenu={() => { |
115 | if (onContextMenuEditClick) { | ||
116 | contextMenu.popup(); | ||
117 | } | ||
118 | }} | ||
119 | onKeyDown={noop} | ||
120 | data-tip={`${ | 120 | data-tip={`${ |
121 | shortcutIndex <= 9 | 121 | shortcutIndex <= 9 |
122 | ? `(${cmdOrCtrlShortcutKey(false)}+${altKey( | 122 | ? `(${cmdOrCtrlShortcutKey(false)}+${altKey( |
@@ -149,5 +149,5 @@ class WorkspaceDrawerItem extends Component { | |||
149 | } | 149 | } |
150 | 150 | ||
151 | export default injectIntl( | 151 | export default injectIntl( |
152 | injectSheet(styles, { injectTheme: true })(observer(WorkspaceDrawerItem)), | 152 | withStyles(styles, { injectTheme: true })(WorkspaceDrawerItem), |
153 | ); | 153 | ); |
diff --git a/src/features/workspaces/components/WorkspaceItem.tsx b/src/features/workspaces/components/WorkspaceItem.tsx index eb33a0376..b097a8298 100644 --- a/src/features/workspaces/components/WorkspaceItem.tsx +++ b/src/features/workspaces/components/WorkspaceItem.tsx | |||
@@ -1,6 +1,8 @@ | |||
1 | import { Component } from 'react'; | 1 | /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ // TODO - [TS DEBT] Need to check and remove it |
2 | import { Component, ReactElement } from 'react'; | ||
2 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
3 | import injectSheet from 'react-jss'; | 4 | import withStyles, { WithStylesProps } from 'react-jss'; |
5 | import { noop } from 'lodash'; | ||
4 | import Workspace from '../models/Workspace'; | 6 | import Workspace from '../models/Workspace'; |
5 | 7 | ||
6 | const styles = theme => ({ | 8 | const styles = theme => ({ |
@@ -14,24 +16,24 @@ const styles = theme => ({ | |||
14 | columnName: {}, | 16 | columnName: {}, |
15 | }); | 17 | }); |
16 | 18 | ||
17 | type Props = { | 19 | interface IProps extends WithStylesProps<typeof styles> { |
18 | classes: any; | 20 | workspace: Workspace; |
19 | workspace: typeof Workspace; | 21 | onItemClick: (workspace: Workspace) => void; |
20 | onItemClick: (workspace) => void; | 22 | } |
21 | }; | ||
22 | 23 | ||
23 | class WorkspaceItem extends Component<Props> { | 24 | @observer |
24 | render() { | 25 | class WorkspaceItem extends Component<IProps> { |
26 | render(): ReactElement { | ||
25 | const { classes, workspace, onItemClick } = this.props; | 27 | const { classes, workspace, onItemClick } = this.props; |
26 | 28 | ||
27 | return ( | 29 | return ( |
28 | <tr className={classes.row}> | 30 | <tr className={classes.row}> |
29 | <td onClick={() => onItemClick(workspace)}>{workspace.name}</td> | 31 | <td onClick={() => onItemClick(workspace)} onKeyDown={noop}> |
32 | {workspace.name} | ||
33 | </td> | ||
30 | </tr> | 34 | </tr> |
31 | ); | 35 | ); |
32 | } | 36 | } |
33 | } | 37 | } |
34 | 38 | ||
35 | export default injectSheet(styles, { injectTheme: true })( | 39 | export default withStyles(styles, { injectTheme: true })(WorkspaceItem); |
36 | observer(WorkspaceItem), | ||
37 | ); | ||
diff --git a/src/features/workspaces/components/WorkspaceServiceListItem.tsx b/src/features/workspaces/components/WorkspaceServiceListItem.tsx index e708d5cdf..9034be37c 100644 --- a/src/features/workspaces/components/WorkspaceServiceListItem.tsx +++ b/src/features/workspaces/components/WorkspaceServiceListItem.tsx | |||
@@ -2,6 +2,7 @@ import { Component, ReactElement } from 'react'; | |||
2 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
3 | import withStyles, { WithStylesProps } from 'react-jss'; | 3 | import withStyles, { WithStylesProps } from 'react-jss'; |
4 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
5 | import { noop } from 'lodash'; | ||
5 | import Toggle from '../../../components/ui/toggle'; | 6 | import Toggle from '../../../components/ui/toggle'; |
6 | import ServiceIcon from '../../../components/ui/ServiceIcon'; | 7 | import ServiceIcon from '../../../components/ui/ServiceIcon'; |
7 | import Service from '../../../models/Service'; | 8 | import Service from '../../../models/Service'; |
@@ -39,9 +40,9 @@ interface IProps extends WithStylesProps<typeof styles> { | |||
39 | class WorkspaceServiceListItem extends Component<IProps> { | 40 | class WorkspaceServiceListItem extends Component<IProps> { |
40 | render(): ReactElement { | 41 | render(): ReactElement { |
41 | const { classes, isInWorkspace, onToggle, service } = this.props; | 42 | const { classes, isInWorkspace, onToggle, service } = this.props; |
42 | |||
43 | return ( | 43 | return ( |
44 | <div className={classes.listItem}> | 44 | // onclick in below div used to fix bug raised under toggle duplicate component removal |
45 | <div className={classes.listItem} onClick={onToggle} onKeyDown={noop}> | ||
45 | <ServiceIcon className={classes.serviceIcon} service={service} /> | 46 | <ServiceIcon className={classes.serviceIcon} service={service} /> |
46 | <span | 47 | <span |
47 | className={classnames([ | 48 | className={classnames([ |
diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.tsx index 87ba06a2d..60fc7a0ce 100644 --- a/src/features/workspaces/components/WorkspacesDashboard.js +++ b/src/features/workspaces/components/WorkspacesDashboard.tsx | |||
@@ -1,18 +1,16 @@ | |||
1 | /* eslint-disable react/jsx-no-useless-fragment */ | 1 | /* eslint-disable react/jsx-no-useless-fragment */ |
2 | import { Component } from 'react'; | 2 | import { Component, ReactElement } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import { observer } from 'mobx-react'; |
4 | import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; | 4 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import withStyles, { WithStylesProps } from 'react-jss'; |
6 | import injectSheet from 'react-jss'; | ||
7 | |||
8 | import Infobox from '../../../components/ui/infobox/index'; | 6 | import Infobox from '../../../components/ui/infobox/index'; |
9 | import Loader from '../../../components/ui/Loader'; | 7 | import Loader from '../../../components/ui/Loader'; |
10 | import WorkspaceItem from './WorkspaceItem'; | 8 | import WorkspaceItem from './WorkspaceItem'; |
11 | import CreateWorkspaceForm from './CreateWorkspaceForm'; | 9 | import CreateWorkspaceForm from './CreateWorkspaceForm'; |
12 | import Request from '../../../stores/lib/Request'; | 10 | import Request from '../../../stores/lib/Request'; |
13 | import Appear from '../../../components/ui/effects/Appear'; | 11 | import Appear from '../../../components/ui/effects/Appear'; |
14 | import UIStore from '../../../stores/UIStore'; | ||
15 | import { H1 } from '../../../components/ui/headline'; | 12 | import { H1 } from '../../../components/ui/headline'; |
13 | import Workspace from '../models/Workspace'; | ||
16 | 14 | ||
17 | const messages = defineMessages({ | 15 | const messages = defineMessages({ |
18 | headline: { | 16 | headline: { |
@@ -70,19 +68,19 @@ const styles = { | |||
70 | }, | 68 | }, |
71 | }; | 69 | }; |
72 | 70 | ||
73 | class WorkspacesDashboard extends Component { | 71 | interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps { |
74 | static propTypes = { | 72 | getUserWorkspacesRequest: Request; |
75 | classes: PropTypes.object.isRequired, | 73 | createWorkspaceRequest: Request; |
76 | getUserWorkspacesRequest: PropTypes.instanceOf(Request).isRequired, | 74 | deleteWorkspaceRequest: Request; |
77 | createWorkspaceRequest: PropTypes.instanceOf(Request).isRequired, | 75 | updateWorkspaceRequest: Request; |
78 | deleteWorkspaceRequest: PropTypes.instanceOf(Request).isRequired, | 76 | onCreateWorkspaceSubmit: (workspace: Workspace) => void; |
79 | updateWorkspaceRequest: PropTypes.instanceOf(Request).isRequired, | 77 | onWorkspaceClick: (workspace: Workspace) => void; |
80 | onCreateWorkspaceSubmit: PropTypes.func.isRequired, | 78 | workspaces: Workspace[]; |
81 | onWorkspaceClick: PropTypes.func.isRequired, | 79 | } |
82 | workspaces: MobxPropTypes.arrayOrObservableArray.isRequired, | ||
83 | }; | ||
84 | 80 | ||
85 | render() { | 81 | @observer |
82 | class WorkspacesDashboard extends Component<IProps> { | ||
83 | render(): ReactElement { | ||
86 | const { | 84 | const { |
87 | classes, | 85 | classes, |
88 | getUserWorkspacesRequest, | 86 | getUserWorkspacesRequest, |
@@ -108,7 +106,7 @@ class WorkspacesDashboard extends Component { | |||
108 | <Infobox | 106 | <Infobox |
109 | type="success" | 107 | type="success" |
110 | icon="checkbox-marked-circle-outline" | 108 | icon="checkbox-marked-circle-outline" |
111 | dismissable | 109 | dismissible |
112 | onUnmount={updateWorkspaceRequest.reset} | 110 | onUnmount={updateWorkspaceRequest.reset} |
113 | > | 111 | > |
114 | {intl.formatMessage(messages.updatedInfo)} | 112 | {intl.formatMessage(messages.updatedInfo)} |
@@ -122,7 +120,7 @@ class WorkspacesDashboard extends Component { | |||
122 | <Infobox | 120 | <Infobox |
123 | type="success" | 121 | type="success" |
124 | icon="checkbox-marked-circle-outline" | 122 | icon="checkbox-marked-circle-outline" |
125 | dismissable | 123 | dismissible |
126 | onUnmount={deleteWorkspaceRequest.reset} | 124 | onUnmount={deleteWorkspaceRequest.reset} |
127 | > | 125 | > |
128 | {intl.formatMessage(messages.deletedInfo)} | 126 | {intl.formatMessage(messages.deletedInfo)} |
@@ -147,7 +145,7 @@ class WorkspacesDashboard extends Component { | |||
147 | icon="alert" | 145 | icon="alert" |
148 | type="danger" | 146 | type="danger" |
149 | ctaLabel={intl.formatMessage(messages.tryReloadWorkspaces)} | 147 | ctaLabel={intl.formatMessage(messages.tryReloadWorkspaces)} |
150 | ctaLoading={getUserWorkspacesRequest.isExecuting} | 148 | // ctaLoading={getUserWorkspacesRequest.isExecuting} // TODO - [TECH DEBT][PROP NOT USED IN COMPONENT] need to check and update |
151 | ctaOnClick={getUserWorkspacesRequest.retry} | 149 | ctaOnClick={getUserWorkspacesRequest.retry} |
152 | > | 150 | > |
153 | {intl.formatMessage(messages.workspacesRequestFailed)} | 151 | {intl.formatMessage(messages.workspacesRequestFailed)} |
@@ -165,7 +163,7 @@ class WorkspacesDashboard extends Component { | |||
165 | </p> | 163 | </p> |
166 | </div> | 164 | </div> |
167 | ) : ( | 165 | ) : ( |
168 | <table className={classes.table}> | 166 | <table className={classes.table} role="grid"> |
169 | {/* ===== Workspaces list ===== */} | 167 | {/* ===== Workspaces list ===== */} |
170 | <tbody> | 168 | <tbody> |
171 | {workspaces.map(workspace => ( | 169 | {workspaces.map(workspace => ( |
@@ -189,13 +187,5 @@ class WorkspacesDashboard extends Component { | |||
189 | } | 187 | } |
190 | 188 | ||
191 | export default injectIntl( | 189 | export default injectIntl( |
192 | inject('stores')( | 190 | withStyles(styles, { injectTheme: true })(WorkspacesDashboard), |
193 | injectSheet(styles, { injectTheme: true })(observer(WorkspacesDashboard)), | ||
194 | ), | ||
195 | ); | 191 | ); |
196 | |||
197 | WorkspacesDashboard.propTypes = { | ||
198 | stores: PropTypes.shape({ | ||
199 | ui: PropTypes.instanceOf(UIStore).isRequired, | ||
200 | }).isRequired, | ||
201 | }; | ||
diff --git a/src/features/workspaces/containers/WorkspacesScreen.tsx b/src/features/workspaces/containers/WorkspacesScreen.tsx index d43dc5efa..39f19935f 100644 --- a/src/features/workspaces/containers/WorkspacesScreen.tsx +++ b/src/features/workspaces/containers/WorkspacesScreen.tsx | |||
@@ -10,8 +10,13 @@ import { | |||
10 | getUserWorkspacesRequest, | 10 | getUserWorkspacesRequest, |
11 | updateWorkspaceRequest, | 11 | updateWorkspaceRequest, |
12 | } from '../api'; | 12 | } from '../api'; |
13 | import Workspace from '../models/Workspace'; | ||
13 | 14 | ||
14 | class WorkspacesScreen extends Component<StoresProps> { | 15 | interface IProps extends StoresProps {} |
16 | |||
17 | @inject('stores', 'actions') | ||
18 | @observer | ||
19 | class WorkspacesScreen extends Component<IProps> { | ||
15 | render() { | 20 | render() { |
16 | const { actions } = this.props; | 21 | const { actions } = this.props; |
17 | return ( | 22 | return ( |
@@ -23,11 +28,13 @@ class WorkspacesScreen extends Component<StoresProps> { | |||
23 | deleteWorkspaceRequest={deleteWorkspaceRequest} | 28 | deleteWorkspaceRequest={deleteWorkspaceRequest} |
24 | updateWorkspaceRequest={updateWorkspaceRequest} | 29 | updateWorkspaceRequest={updateWorkspaceRequest} |
25 | onCreateWorkspaceSubmit={data => actions.workspaces.create(data)} | 30 | onCreateWorkspaceSubmit={data => actions.workspaces.create(data)} |
26 | onWorkspaceClick={w => actions.workspaces.edit({ workspace: w })} | 31 | onWorkspaceClick={(workspace: Workspace) => |
32 | actions.workspaces.edit({ workspace }) | ||
33 | } | ||
27 | /> | 34 | /> |
28 | </ErrorBoundary> | 35 | </ErrorBoundary> |
29 | ); | 36 | ); |
30 | } | 37 | } |
31 | } | 38 | } |
32 | 39 | ||
33 | export default inject('stores', 'actions')(observer(WorkspacesScreen)); | 40 | export default WorkspacesScreen; |
diff --git a/src/stores/AppStore.ts b/src/stores/AppStore.ts index aab279e59..2db90bfa0 100644 --- a/src/stores/AppStore.ts +++ b/src/stores/AppStore.ts | |||
@@ -494,7 +494,7 @@ export default class AppStore extends TypedStore { | |||
494 | ), | 494 | ), |
495 | ); | 495 | ); |
496 | 496 | ||
497 | await clearAppCache._promise; | 497 | await clearAppCache.promise; |
498 | 498 | ||
499 | await sleep(ms('1s')); | 499 | await sleep(ms('1s')); |
500 | 500 | ||
diff --git a/src/stores/FeaturesStore.ts b/src/stores/FeaturesStore.ts index ed0c6c17b..5f43ccf84 100644 --- a/src/stores/FeaturesStore.ts +++ b/src/stores/FeaturesStore.ts | |||
@@ -45,7 +45,7 @@ export default class FeaturesStore extends TypedStore { | |||
45 | this._monitorLoginStatus.bind(this), | 45 | this._monitorLoginStatus.bind(this), |
46 | ]); | 46 | ]); |
47 | 47 | ||
48 | await this.featuresRequest._promise; | 48 | await this.featuresRequest.promise; |
49 | setTimeout(this._setupFeatures.bind(this), 1); | 49 | setTimeout(this._setupFeatures.bind(this), 1); |
50 | } | 50 | } |
51 | 51 | ||
diff --git a/src/stores/GlobalErrorStore.ts b/src/stores/GlobalErrorStore.ts index c42e9a4af..be86563d0 100644 --- a/src/stores/GlobalErrorStore.ts +++ b/src/stores/GlobalErrorStore.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { observable, action, makeObservable } from 'mobx'; | 1 | import { observable, action, makeObservable } from 'mobx'; |
2 | import { Response } from 'electron'; | ||
2 | import { Actions } from '../actions/lib/actions'; | 3 | import { Actions } from '../actions/lib/actions'; |
3 | import { ApiInterface } from '../api'; | 4 | import { ApiInterface } from '../api'; |
4 | import { Stores } from '../@types/stores.types'; | 5 | import { Stores } from '../@types/stores.types'; |
@@ -11,12 +12,8 @@ interface Message { | |||
11 | message?: string; | 12 | message?: string; |
12 | status?: number; | 13 | status?: number; |
13 | }; | 14 | }; |
14 | request?: { | 15 | request?: Request; |
15 | result: any; | 16 | response?: Response; |
16 | wasExecuted: any; | ||
17 | method: any; | ||
18 | }; | ||
19 | response?: any; | ||
20 | server?: any; | 17 | server?: any; |
21 | info?: any; | 18 | info?: any; |
22 | url?: string; | 19 | url?: string; |
@@ -28,7 +25,7 @@ export default class GlobalErrorStore extends TypedStore { | |||
28 | 25 | ||
29 | @observable messages: Message[] = []; | 26 | @observable messages: Message[] = []; |
30 | 27 | ||
31 | @observable response: object = {}; | 28 | @observable response: Response = {} as Response; |
32 | 29 | ||
33 | // TODO: Get rid of the @ts-ignores in this function. | 30 | // TODO: Get rid of the @ts-ignores in this function. |
34 | constructor(stores: Stores, api: ApiInterface, actions: Actions) { | 31 | constructor(stores: Stores, api: ApiInterface, actions: Actions) { |
@@ -85,21 +82,15 @@ export default class GlobalErrorStore extends TypedStore { | |||
85 | } | 82 | } |
86 | } | 83 | } |
87 | 84 | ||
88 | @action _handleRequests = async (request: { | 85 | @action _handleRequests = async (request: Request): Promise<void> => { |
89 | isError: any; | ||
90 | error: { json: () => object | PromiseLike<object> }; | ||
91 | result: any; | ||
92 | wasExecuted: any; | ||
93 | _method: any; | ||
94 | }): Promise<void> => { | ||
95 | if (request.isError) { | 86 | if (request.isError) { |
96 | this.error = request.error; | 87 | this.error = request.error; |
97 | 88 | ||
98 | if (request.error.json) { | 89 | if (request.error && request.error.json) { |
99 | try { | 90 | try { |
100 | this.response = await request.error.json(); | 91 | this.response = await request.error.json(); |
101 | } catch { | 92 | } catch { |
102 | this.response = {}; | 93 | this.response = {} as Response; |
103 | } | 94 | } |
104 | if (this.error?.status === 401) { | 95 | if (this.error?.status === 401) { |
105 | window['ferdium'].stores.app.authRequestFailed = true; | 96 | window['ferdium'].stores.app.authRequestFailed = true; |
@@ -111,8 +102,8 @@ export default class GlobalErrorStore extends TypedStore { | |||
111 | request: { | 102 | request: { |
112 | result: request.result, | 103 | result: request.result, |
113 | wasExecuted: request.wasExecuted, | 104 | wasExecuted: request.wasExecuted, |
114 | method: request._method, | 105 | method: request.method, |
115 | }, | 106 | } as Request, |
116 | error: this.error, | 107 | error: this.error, |
117 | response: this.response, | 108 | response: this.response, |
118 | server: window['ferdium'].stores.settings.app.server, | 109 | server: window['ferdium'].stores.settings.app.server, |
diff --git a/src/stores/RecipesStore.ts b/src/stores/RecipesStore.ts index 25304e97c..07f1343f8 100644 --- a/src/stores/RecipesStore.ts +++ b/src/stores/RecipesStore.ts | |||
@@ -74,8 +74,8 @@ export default class RecipesStore extends TypedStore { | |||
74 | 74 | ||
75 | // Actions | 75 | // Actions |
76 | async _install({ recipeId }): Promise<Recipe> { | 76 | async _install({ recipeId }): Promise<Recipe> { |
77 | const recipe = await this.installRecipeRequest.execute(recipeId)._promise; | 77 | const recipe = await this.installRecipeRequest.execute(recipeId).promise; |
78 | await this.allRecipesRequest.invalidate({ immediately: true })._promise; | 78 | await this.allRecipesRequest.invalidate({ immediately: true }).promise; |
79 | 79 | ||
80 | return recipe; | 80 | return recipe; |
81 | } | 81 | } |
@@ -128,7 +128,7 @@ export default class RecipesStore extends TypedStore { | |||
128 | const update = updates[i]; | 128 | const update = updates[i]; |
129 | 129 | ||
130 | this.actions.recipe.install({ recipeId: update }); | 130 | this.actions.recipe.install({ recipeId: update }); |
131 | await this.installRecipeRequest._promise; | 131 | await this.installRecipeRequest.promise; |
132 | 132 | ||
133 | this.installRecipeRequest.reset(); | 133 | this.installRecipeRequest.reset(); |
134 | 134 | ||
@@ -158,10 +158,10 @@ export default class RecipesStore extends TypedStore { | |||
158 | debug(`Recipe ${recipeId} is not installed, trying to install it`); | 158 | debug(`Recipe ${recipeId} is not installed, trying to install it`); |
159 | 159 | ||
160 | const recipe = await this.installRecipeRequest.execute(recipeId) | 160 | const recipe = await this.installRecipeRequest.execute(recipeId) |
161 | ._promise; | 161 | .promise; |
162 | if (recipe) { | 162 | if (recipe) { |
163 | await this.allRecipesRequest.invalidate({ immediately: true }) | 163 | await this.allRecipesRequest.invalidate({ immediately: true }) |
164 | ._promise; | 164 | .promise; |
165 | router.push(`/settings/services/add/${recipeId}`); | 165 | router.push(`/settings/services/add/${recipeId}`); |
166 | } else { | 166 | } else { |
167 | router.push('/settings/recipes'); | 167 | router.push('/settings/recipes'); |
diff --git a/src/stores/RequestStore.ts b/src/stores/RequestStore.ts index 279615e50..807f2d126 100644 --- a/src/stores/RequestStore.ts +++ b/src/stores/RequestStore.ts | |||
@@ -37,6 +37,9 @@ export default class RequestStore extends TypedStore { | |||
37 | ); | 37 | ); |
38 | 38 | ||
39 | this.registerReactions([this._autoRetry.bind(this)]); | 39 | this.registerReactions([this._autoRetry.bind(this)]); |
40 | |||
41 | this.userInfoRequest = {} as CachedRequest; | ||
42 | this.servicesRequest = {} as CachedRequest; | ||
40 | } | 43 | } |
41 | 44 | ||
42 | async setup(): Promise<void> { | 45 | async setup(): Promise<void> { |
diff --git a/src/stores/ServicesStore.ts b/src/stores/ServicesStore.ts index 74810b81f..4fdd9d5ad 100644 --- a/src/stores/ServicesStore.ts +++ b/src/stores/ServicesStore.ts | |||
@@ -477,7 +477,7 @@ export default class ServicesStore extends TypedStore { | |||
477 | : this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData); | 477 | : this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData); |
478 | 478 | ||
479 | const response = await this.createServiceRequest.execute(recipeId, data) | 479 | const response = await this.createServiceRequest.execute(recipeId, data) |
480 | ._promise; | 480 | .promise; |
481 | 481 | ||
482 | this.allServicesRequest.patch(result => { | 482 | this.allServicesRequest.patch(result => { |
483 | if (!result) return; | 483 | if (!result) return; |
@@ -536,7 +536,7 @@ export default class ServicesStore extends TypedStore { | |||
536 | 536 | ||
537 | const newData = serviceData; | 537 | const newData = serviceData; |
538 | if (serviceData.iconFile) { | 538 | if (serviceData.iconFile) { |
539 | await request._promise; | 539 | await request.promise; |
540 | 540 | ||
541 | newData.iconUrl = request.result.data.iconUrl; | 541 | newData.iconUrl = request.result.data.iconUrl; |
542 | newData.hasCustomUploadedIcon = true; | 542 | newData.hasCustomUploadedIcon = true; |
@@ -562,7 +562,7 @@ export default class ServicesStore extends TypedStore { | |||
562 | ); | 562 | ); |
563 | }); | 563 | }); |
564 | 564 | ||
565 | await request._promise; | 565 | await request.promise; |
566 | this.actionStatus = request.result.status; | 566 | this.actionStatus = request.result.status; |
567 | 567 | ||
568 | if (service.isEnabled) { | 568 | if (service.isEnabled) { |
@@ -596,7 +596,7 @@ export default class ServicesStore extends TypedStore { | |||
596 | remove(result, (c: Service) => c.id === serviceId); | 596 | remove(result, (c: Service) => c.id === serviceId); |
597 | }); | 597 | }); |
598 | 598 | ||
599 | await request._promise; | 599 | await request.promise; |
600 | this.actionStatus = request.result.status; | 600 | this.actionStatus = request.result.status; |
601 | } | 601 | } |
602 | 602 | ||
@@ -637,7 +637,7 @@ export default class ServicesStore extends TypedStore { | |||
637 | @action async _clearCache({ serviceId }) { | 637 | @action async _clearCache({ serviceId }) { |
638 | this.clearCacheRequest.reset(); | 638 | this.clearCacheRequest.reset(); |
639 | const request = this.clearCacheRequest.execute(serviceId); | 639 | const request = this.clearCacheRequest.execute(serviceId); |
640 | await request._promise; | 640 | await request.promise; |
641 | } | 641 | } |
642 | 642 | ||
643 | @action _setIsActive(service: Service, state: boolean): void { | 643 | @action _setIsActive(service: Service, state: boolean): void { |
diff --git a/src/stores/UserStore.ts b/src/stores/UserStore.ts index c5e67c966..6c8f8f20b 100644 --- a/src/stores/UserStore.ts +++ b/src/stores/UserStore.ts | |||
@@ -187,7 +187,7 @@ export default class UserStore extends TypedStore { | |||
187 | 187 | ||
188 | // Actions | 188 | // Actions |
189 | @action async _login({ email, password }): Promise<void> { | 189 | @action async _login({ email, password }): Promise<void> { |
190 | const authToken = await this.loginRequest.execute(email, password)._promise; | 190 | const authToken = await this.loginRequest.execute(email, password).promise; |
191 | this._setUserData(authToken); | 191 | this._setUserData(authToken); |
192 | 192 | ||
193 | this.stores.router.push('/'); | 193 | this.stores.router.push('/'); |
@@ -209,6 +209,8 @@ export default class UserStore extends TypedStore { | |||
209 | plan, | 209 | plan, |
210 | currency, | 210 | currency, |
211 | }): Promise<void> { | 211 | }): Promise<void> { |
212 | // TODO - [TS DEBT] Need to find a way proper to implement promise's then and catch in request class | ||
213 | // @ts-ignore | ||
212 | const authToken = await this.signupRequest.execute({ | 214 | const authToken = await this.signupRequest.execute({ |
213 | firstname, | 215 | firstname, |
214 | lastname, | 216 | lastname, |
@@ -231,14 +233,14 @@ export default class UserStore extends TypedStore { | |||
231 | @action async _retrievePassword({ email }): Promise<void> { | 233 | @action async _retrievePassword({ email }): Promise<void> { |
232 | const request = this.passwordRequest.execute(email); | 234 | const request = this.passwordRequest.execute(email); |
233 | 235 | ||
234 | await request._promise; | 236 | await request.promise; |
235 | this.actionStatus = request.result.status || []; | 237 | this.actionStatus = request.result.status || []; |
236 | } | 238 | } |
237 | 239 | ||
238 | @action async _invite({ invites }): Promise<void> { | 240 | @action async _invite({ invites }): Promise<void> { |
239 | const data = invites.filter(invite => invite.email !== ''); | 241 | const data = invites.filter(invite => invite.email !== ''); |
240 | 242 | ||
241 | const response = await this.inviteRequest.execute(data)._promise; | 243 | const response = await this.inviteRequest.execute(data).promise; |
242 | 244 | ||
243 | this.actionStatus = response.status || []; | 245 | this.actionStatus = response.status || []; |
244 | 246 | ||
@@ -251,8 +253,7 @@ export default class UserStore extends TypedStore { | |||
251 | @action async _update({ userData }): Promise<void> { | 253 | @action async _update({ userData }): Promise<void> { |
252 | if (!this.isLoggedIn) return; | 254 | if (!this.isLoggedIn) return; |
253 | 255 | ||
254 | const response = await this.updateUserInfoRequest.execute(userData) | 256 | const response = await this.updateUserInfoRequest.execute(userData).promise; |
255 | ._promise; | ||
256 | 257 | ||
257 | this.getUserInfoRequest.patch(() => response.data); | 258 | this.getUserInfoRequest.patch(() => response.data); |
258 | this.actionStatus = response.status || []; | 259 | this.actionStatus = response.status || []; |
@@ -299,7 +300,7 @@ export default class UserStore extends TypedStore { | |||
299 | data: service, | 300 | data: service, |
300 | }); | 301 | }); |
301 | // eslint-disable-next-line no-await-in-loop | 302 | // eslint-disable-next-line no-await-in-loop |
302 | await this.stores.services.createServiceRequest._promise; | 303 | await this.stores.services.createServiceRequest.promise; |
303 | } | 304 | } |
304 | 305 | ||
305 | this.isImportLegacyServicesExecuting = false; | 306 | this.isImportLegacyServicesExecuting = false; |
@@ -349,7 +350,7 @@ export default class UserStore extends TypedStore { | |||
349 | if (this.isLoggedIn) { | 350 | if (this.isLoggedIn) { |
350 | let data; | 351 | let data; |
351 | try { | 352 | try { |
352 | data = await this.getUserInfoRequest.execute()._promise; | 353 | data = await this.getUserInfoRequest.execute().promise; |
353 | } catch { | 354 | } catch { |
354 | return; | 355 | return; |
355 | } | 356 | } |
@@ -406,7 +407,7 @@ export default class UserStore extends TypedStore { | |||
406 | 407 | ||
407 | async _migrateUserLocale(): Promise<void> { | 408 | async _migrateUserLocale(): Promise<void> { |
408 | try { | 409 | try { |
409 | await this.getUserInfoRequest._promise; | 410 | await this.getUserInfoRequest.promise; |
410 | } catch { | 411 | } catch { |
411 | return; | 412 | return; |
412 | } | 413 | } |
diff --git a/src/stores/lib/CachedRequest.js b/src/stores/lib/CachedRequest.ts index a6dd47f7d..25cc365e2 100644 --- a/src/stores/lib/CachedRequest.js +++ b/src/stores/lib/CachedRequest.ts | |||
@@ -3,29 +3,33 @@ import { isEqual, remove } from 'lodash'; | |||
3 | import Request from './Request'; | 3 | import Request from './Request'; |
4 | 4 | ||
5 | export default class CachedRequest extends Request { | 5 | export default class CachedRequest extends Request { |
6 | _apiCalls = []; | 6 | _apiCalls: any[] = []; |
7 | 7 | ||
8 | _isInvalidated = true; | 8 | _isInvalidated = true; |
9 | 9 | ||
10 | execute(...callArgs) { | 10 | execute(...callArgs): this { |
11 | // Do not continue if this request is already loading | 11 | // Do not continue if this request is already loading |
12 | if (this._isWaitingForResponse) return this; | 12 | if (this.isWaitingForResponse) { |
13 | return this; | ||
14 | } | ||
13 | 15 | ||
14 | // Very simple caching strategy -> only continue if the call / args changed | 16 | // Very simple caching strategy -> only continue if the call / args changed |
15 | // or the request was invalidated manually from outside | 17 | // or the request was invalidated manually from outside |
16 | const existingApiCall = this._findApiCall(callArgs); | 18 | const existingApiCall = this._findApiCall(callArgs); |
17 | 19 | ||
18 | // Invalidate if new or different api call will be done | 20 | // Invalidate if new or different api call will be done |
19 | if (existingApiCall && existingApiCall !== this._currentApiCall) { | 21 | if (existingApiCall && existingApiCall !== this.currentApiCall) { |
20 | this._isInvalidated = true; | 22 | this._isInvalidated = true; |
21 | this._currentApiCall = existingApiCall; | 23 | this.currentApiCall = existingApiCall; |
22 | } else if (!existingApiCall) { | 24 | } else if (!existingApiCall) { |
23 | this._isInvalidated = true; | 25 | this._isInvalidated = true; |
24 | this._currentApiCall = this._addApiCall(callArgs); | 26 | this.currentApiCall = this._addApiCall(callArgs); |
25 | } | 27 | } |
26 | 28 | ||
27 | // Do not continue if this request is not invalidated (see above) | 29 | // Do not continue if this request is not invalidated (see above) |
28 | if (!this._isInvalidated) return this; | 30 | if (!this._isInvalidated) { |
31 | return this; | ||
32 | } | ||
29 | 33 | ||
30 | // This timeout is necessary to avoid warnings from mobx | 34 | // This timeout is necessary to avoid warnings from mobx |
31 | // regarding triggering actions as side-effect of getters | 35 | // regarding triggering actions as side-effect of getters |
@@ -41,18 +45,18 @@ export default class CachedRequest extends Request { | |||
41 | ); | 45 | ); |
42 | 46 | ||
43 | // Issue api call & save it as promise that is handled to update the results of the operation | 47 | // Issue api call & save it as promise that is handled to update the results of the operation |
44 | this._promise = new Promise(resolve => { | 48 | this.promise = new Promise(resolve => { |
45 | this._api[this._method](...callArgs) | 49 | this.api[this.method](...callArgs) |
46 | .then(result => { | 50 | .then(result => { |
47 | setTimeout( | 51 | setTimeout( |
48 | action(() => { | 52 | action(() => { |
49 | this.result = result; | 53 | this.result = result; |
50 | if (this._currentApiCall) this._currentApiCall.result = result; | 54 | if (this.currentApiCall) this.currentApiCall.result = result; |
51 | this.isExecuting = false; | 55 | this.isExecuting = false; |
52 | this.isError = false; | 56 | this.isError = false; |
53 | this.wasExecuted = true; | 57 | this.wasExecuted = true; |
54 | this._isInvalidated = false; | 58 | this._isInvalidated = false; |
55 | this._isWaitingForResponse = false; | 59 | this.isWaitingForResponse = false; |
56 | this._triggerHooks(); | 60 | this._triggerHooks(); |
57 | resolve(result); | 61 | resolve(result); |
58 | }), | 62 | }), |
@@ -68,7 +72,7 @@ export default class CachedRequest extends Request { | |||
68 | this.isExecuting = false; | 72 | this.isExecuting = false; |
69 | this.isError = true; | 73 | this.isError = true; |
70 | this.wasExecuted = true; | 74 | this.wasExecuted = true; |
71 | this._isWaitingForResponse = false; | 75 | this.isWaitingForResponse = false; |
72 | this._triggerHooks(); | 76 | this._triggerHooks(); |
73 | // reject(error); | 77 | // reject(error); |
74 | }), | 78 | }), |
@@ -78,26 +82,27 @@ export default class CachedRequest extends Request { | |||
78 | ); | 82 | ); |
79 | }); | 83 | }); |
80 | 84 | ||
81 | this._isWaitingForResponse = true; | 85 | this.isWaitingForResponse = true; |
82 | return this; | 86 | return this; |
83 | } | 87 | } |
84 | 88 | ||
85 | // eslint-disable-next-line unicorn/no-object-as-default-parameter | 89 | static defaultOptions = { immediately: false }; |
86 | invalidate(options = { immediately: false }) { | 90 | |
91 | invalidate(options = CachedRequest.defaultOptions): this { | ||
87 | this._isInvalidated = true; | 92 | this._isInvalidated = true; |
88 | if (options.immediately && this._currentApiCall) { | 93 | if (options.immediately && this.currentApiCall) { |
89 | return this.execute(...this._currentApiCall.args); | 94 | return this.execute(...this.currentApiCall.args); |
90 | } | 95 | } |
91 | return this; | 96 | return this; |
92 | } | 97 | } |
93 | 98 | ||
94 | patch(modify) { | 99 | patch(modify): Promise<this> { |
95 | return new Promise(resolve => { | 100 | return new Promise(resolve => { |
96 | setTimeout( | 101 | setTimeout( |
97 | action(() => { | 102 | action(() => { |
98 | const override = modify(this.result); | 103 | const override = modify(this.result); |
99 | if (override !== undefined) this.result = override; | 104 | if (override !== undefined) this.result = override; |
100 | if (this._currentApiCall) this._currentApiCall.result = this.result; | 105 | if (this.currentApiCall) this.currentApiCall.result = this.result; |
101 | resolve(this); | 106 | resolve(this); |
102 | }), | 107 | }), |
103 | 0, | 108 | 0, |
@@ -105,17 +110,17 @@ export default class CachedRequest extends Request { | |||
105 | }); | 110 | }); |
106 | } | 111 | } |
107 | 112 | ||
108 | removeCacheForCallWith(...args) { | 113 | removeCacheForCallWith(...args: any): void { |
109 | remove(this._apiCalls, c => isEqual(c.args, args)); | 114 | remove(this._apiCalls, c => isEqual(c.args, args)); |
110 | } | 115 | } |
111 | 116 | ||
112 | _addApiCall(args) { | 117 | _addApiCall(args: any) { |
113 | const newCall = { args, result: null }; | 118 | const newCall = { args, result: null }; |
114 | this._apiCalls.push(newCall); | 119 | this._apiCalls.push(newCall); |
115 | return newCall; | 120 | return newCall; |
116 | } | 121 | } |
117 | 122 | ||
118 | _findApiCall(args) { | 123 | _findApiCall(args: any) { |
119 | return this._apiCalls.find(c => isEqual(c.args, args)); | 124 | return this._apiCalls.find(c => isEqual(c.args, args)); |
120 | } | 125 | } |
121 | } | 126 | } |
diff --git a/src/stores/lib/Request.js b/src/stores/lib/Request.ts index 60c943a42..f9424ac99 100644 --- a/src/stores/lib/Request.js +++ b/src/stores/lib/Request.ts | |||
@@ -1,16 +1,18 @@ | |||
1 | import { observable, action, computed, makeObservable } from 'mobx'; | 1 | import { observable, action, computed, makeObservable } from 'mobx'; |
2 | import { isEqual } from 'lodash/fp'; | 2 | import { isEqual } from 'lodash/fp'; |
3 | 3 | ||
4 | type Hook = (request: Request) => void; | ||
5 | |||
4 | export default class Request { | 6 | export default class Request { |
5 | static _hooks = []; | 7 | static _hooks: Hook[] = []; |
6 | 8 | ||
7 | static registerHook(hook) { | 9 | static registerHook(hook: Hook) { |
8 | Request._hooks.push(hook); | 10 | Request._hooks.push(hook); |
9 | } | 11 | } |
10 | 12 | ||
11 | @observable result = null; | 13 | @observable result: any = null; |
12 | 14 | ||
13 | @observable error = null; | 15 | @observable error: any = null; |
14 | 16 | ||
15 | @observable isExecuting = false; | 17 | @observable isExecuting = false; |
16 | 18 | ||
@@ -18,43 +20,47 @@ export default class Request { | |||
18 | 20 | ||
19 | @observable wasExecuted = false; | 21 | @observable wasExecuted = false; |
20 | 22 | ||
21 | @action _reset() { | 23 | promise: any = Promise; |
22 | this.error = null; | ||
23 | this.result = null; | ||
24 | this.isExecuting = false; | ||
25 | this.isError = false; | ||
26 | this.wasExecuted = false; | ||
27 | this._isWaitingForResponse = false; | ||
28 | this._promise = Promise; | ||
29 | 24 | ||
30 | return this; | 25 | protected api: any = {}; |
31 | } | ||
32 | 26 | ||
33 | _promise = Promise; | 27 | method = ''; |
34 | 28 | ||
35 | _api = {}; | 29 | protected isWaitingForResponse = false; |
36 | 30 | ||
37 | _method = ''; | 31 | protected currentApiCall: any = null; |
38 | 32 | ||
39 | _isWaitingForResponse = false; | 33 | retry = () => this.reload(); |
40 | 34 | ||
41 | _currentApiCall = null; | 35 | reset = () => this._reset(); |
42 | 36 | ||
43 | constructor(api, method) { | 37 | constructor(api, method) { |
44 | makeObservable(this); | 38 | makeObservable(this); |
45 | 39 | ||
46 | this._api = api; | 40 | this.api = api; |
47 | this._method = method; | 41 | this.method = method; |
42 | } | ||
43 | |||
44 | @action _reset(): this { | ||
45 | this.error = null; | ||
46 | this.result = null; | ||
47 | this.isExecuting = false; | ||
48 | this.isError = false; | ||
49 | this.wasExecuted = false; | ||
50 | this.isWaitingForResponse = false; | ||
51 | this.promise = Promise; | ||
52 | |||
53 | return this; | ||
48 | } | 54 | } |
49 | 55 | ||
50 | execute(...callArgs) { | 56 | execute(...callArgs: any[]): this { |
51 | // Do not continue if this request is already loading | 57 | // Do not continue if this request is already loading |
52 | if (this._isWaitingForResponse) return this; | 58 | if (this.isWaitingForResponse) return this; |
53 | 59 | ||
54 | if (!this._api[this._method]) { | 60 | if (!this.api[this.method]) { |
55 | throw new Error( | 61 | throw new Error( |
56 | `Missing method <${this._method}> on api object:`, | 62 | `Missing method <${this.method}> on api object:`, |
57 | this._api, | 63 | this.api, |
58 | ); | 64 | ); |
59 | } | 65 | } |
60 | 66 | ||
@@ -68,18 +74,18 @@ export default class Request { | |||
68 | ); | 74 | ); |
69 | 75 | ||
70 | // Issue api call & save it as promise that is handled to update the results of the operation | 76 | // Issue api call & save it as promise that is handled to update the results of the operation |
71 | this._promise = new Promise((resolve, reject) => { | 77 | this.promise = new Promise((resolve, reject) => { |
72 | this._api[this._method](...callArgs) | 78 | this.api[this.method](...callArgs) |
73 | .then(result => { | 79 | .then(result => { |
74 | setTimeout( | 80 | setTimeout( |
75 | action(() => { | 81 | action(() => { |
76 | this.error = null; | 82 | this.error = null; |
77 | this.result = result; | 83 | this.result = result; |
78 | if (this._currentApiCall) this._currentApiCall.result = result; | 84 | if (this.currentApiCall) this.currentApiCall.result = result; |
79 | this.isExecuting = false; | 85 | this.isExecuting = false; |
80 | this.isError = false; | 86 | this.isError = false; |
81 | this.wasExecuted = true; | 87 | this.wasExecuted = true; |
82 | this._isWaitingForResponse = false; | 88 | this.isWaitingForResponse = false; |
83 | this._triggerHooks(); | 89 | this._triggerHooks(); |
84 | resolve(result); | 90 | resolve(result); |
85 | }), | 91 | }), |
@@ -95,7 +101,7 @@ export default class Request { | |||
95 | this.isExecuting = false; | 101 | this.isExecuting = false; |
96 | this.isError = true; | 102 | this.isError = true; |
97 | this.wasExecuted = true; | 103 | this.wasExecuted = true; |
98 | this._isWaitingForResponse = false; | 104 | this.isWaitingForResponse = false; |
99 | this._triggerHooks(); | 105 | this._triggerHooks(); |
100 | reject(error); | 106 | reject(error); |
101 | }), | 107 | }), |
@@ -105,51 +111,49 @@ export default class Request { | |||
105 | ); | 111 | ); |
106 | }); | 112 | }); |
107 | 113 | ||
108 | this._isWaitingForResponse = true; | 114 | this.isWaitingForResponse = true; |
109 | this._currentApiCall = { args: callArgs, result: null }; | 115 | this.currentApiCall = { args: callArgs, result: null }; |
110 | return this; | 116 | return this; |
111 | } | 117 | } |
112 | 118 | ||
113 | reload() { | 119 | reload(): this { |
114 | const args = this._currentApiCall ? this._currentApiCall.args : []; | 120 | const args = this.currentApiCall ? this.currentApiCall.args : []; |
115 | this.error = null; | 121 | this.error = null; |
116 | return this.execute(...args); | 122 | return this.execute(...args); |
117 | } | 123 | } |
118 | 124 | ||
119 | retry = () => this.reload(); | 125 | isExecutingWithArgs(...args: any[]): boolean { |
120 | |||
121 | isExecutingWithArgs(...args) { | ||
122 | return ( | 126 | return ( |
123 | this.isExecuting && | 127 | this.isExecuting && |
124 | this._currentApiCall && | 128 | this.currentApiCall && |
125 | isEqual(this._currentApiCall.args, args) | 129 | isEqual(this.currentApiCall.args, args) |
126 | ); | 130 | ); |
127 | } | 131 | } |
128 | 132 | ||
129 | @computed get isExecutingFirstTime() { | 133 | @computed get isExecutingFirstTime(): boolean { |
130 | return !this.wasExecuted && this.isExecuting; | 134 | return !this.wasExecuted && this.isExecuting; |
131 | } | 135 | } |
132 | 136 | ||
133 | /* eslint-disable unicorn/no-thenable */ | 137 | /* eslint-disable unicorn/no-thenable */ |
134 | then(...args) { | 138 | then(...args: any[]) { |
135 | if (!this._promise) | 139 | if (!this.promise) |
136 | throw new Error( | 140 | throw new Error( |
137 | 'You have to call Request::execute before you can access it as promise', | 141 | 'You have to call Request::execute before you can access it as promise', |
138 | ); | 142 | ); |
139 | return this._promise.then(...args); | 143 | return this.promise.then(...args); |
140 | } | 144 | } |
141 | 145 | ||
142 | catch(...args) { | 146 | catch(...args: any[]) { |
143 | if (!this._promise) | 147 | if (!this.promise) |
144 | throw new Error( | 148 | throw new Error( |
145 | 'You have to call Request::execute before you can access it as promise', | 149 | 'You have to call Request::execute before you can access it as promise', |
146 | ); | 150 | ); |
147 | return this._promise.catch(...args); | 151 | return this.promise.catch(...args); |
148 | } | 152 | } |
149 | 153 | ||
150 | _triggerHooks() { | 154 | _triggerHooks(): void { |
151 | for (const hook of Request._hooks) hook(this); | 155 | for (const hook of Request._hooks) { |
156 | hook(this); | ||
157 | } | ||
152 | } | 158 | } |
153 | |||
154 | reset = () => this._reset(); | ||
155 | } | 159 | } |