Skip to content

Commit 4245007

Browse files
Merge pull request #1303 from CleverCloud/cc-ssh-key-list/migrate-to-new-state
refactor(cc-ssh-key-list)!: rework properties to avoid impossible states
2 parents 4cb8fac + 9d81a3e commit 4245007

File tree

4 files changed

+271
-161
lines changed

4 files changed

+271
-161
lines changed

src/components/cc-ssh-key-list/cc-ssh-key-list.js

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const SSH_KEY_DOCUMENTATION = 'https://developers.clever-cloud.com/doc/account/s
3434
*/
3535
const SKELETON_KEYS = [
3636
{
37-
state: 'idle',
37+
type: 'idle',
3838
name: fakeString(15),
3939
fingerprint: fakeString(32),
4040
},
@@ -55,7 +55,7 @@ class SshPublicKeyValidator {
5555
}
5656

5757
/**
58-
* @typedef {import('./cc-ssh-key-list.types.js').KeyDataState} KeyDataState
58+
* @typedef {import('./cc-ssh-key-list.types.js').SshKeyListState} SshKeyListState
5959
* @typedef {import('./cc-ssh-key-list.types.js').SshKeyState} SshKeyState
6060
* @typedef {import('./cc-ssh-key-list.types.js').CreateSshKeyFormState} CreateSshKeyFormState
6161
* @typedef {import('./cc-ssh-key-list.types.js').NewKey} NewKey
@@ -86,7 +86,7 @@ export class CcSshKeyList extends LitElement {
8686
static get properties() {
8787
return {
8888
createKeyFormState: { type: Object, attribute: false },
89-
keyData: { type: Object, attribute: 'key-data' },
89+
keyListState: { type: Object, attribute: false },
9090
};
9191
}
9292

@@ -96,8 +96,8 @@ export class CcSshKeyList extends LitElement {
9696
/** @type {CreateSshKeyFormState} create key form state. */
9797
this.createKeyFormState = { type: 'idle' };
9898

99-
/** @type {KeyDataState} personal and GitHub lists of registered SSH keys. */
100-
this.keyData = { state: 'loading' };
99+
/** @type {SshKeyListState} personal and GitHub lists of registered SSH keys. */
100+
this.keyListState = { type: 'loading' };
101101

102102
/** @type {HTMLFormElementRef} */
103103
this._createFormRef = createRef();
@@ -141,14 +141,14 @@ export class CcSshKeyList extends LitElement {
141141
/** @param {SshKeyState} sshKeyState */
142142
_onDeleteKey(sshKeyState) {
143143
// removing state property that belongs to internal component implementation
144-
const { state, ...sshKey } = sshKeyState;
144+
const { type: state, ...sshKey } = sshKeyState;
145145
dispatchCustomEvent(this, 'delete', sshKey);
146146
}
147147

148148
/** @param {SshKeyState} sshKeyState */
149149
_onImportKey(sshKeyState) {
150150
// removing state property that belongs to internal component implementation
151-
const { state, ...sshKey } = sshKeyState;
151+
const { type: state, ...sshKey } = sshKeyState;
152152
dispatchCustomEvent(this, 'import', sshKey);
153153
}
154154

@@ -169,26 +169,26 @@ export class CcSshKeyList extends LitElement {
169169
<cc-block-section slot="content-body">
170170
<div slot="title">
171171
<span>${i18n('cc-ssh-key-list.personal.title')}</span>
172-
${this.keyData.state === 'loaded' && this.keyData.personalKeys.length > 2
173-
? html` <cc-badge circle>${this.keyData.personalKeys.length}</cc-badge> `
172+
${this.keyListState.type === 'loaded' && this.keyListState.personalKeys.length > 2
173+
? html` <cc-badge circle>${this.keyListState.personalKeys.length}</cc-badge> `
174174
: ''}
175175
</div>
176176
<div slot="info">${i18n('cc-ssh-key-list.personal.info')}</div>
177177
178-
${this.keyData.state === 'loading' ? html` ${this._renderKeyList('skeleton', SKELETON_KEYS)} ` : ''}
179-
${this.keyData.state === 'loaded'
178+
${this.keyListState.type === 'loading' ? html` ${this._renderKeyList('skeleton', SKELETON_KEYS)} ` : ''}
179+
${this.keyListState.type === 'loaded'
180180
? html`
181-
${this.keyData.personalKeys.length === 0
181+
${this.keyListState.personalKeys.length === 0
182182
? html`
183183
<p class="info-msg" id="personal-keys-empty-msg" tabindex="-1">
184184
${i18n('cc-ssh-key-list.personal.empty')}
185185
</p>
186186
`
187187
: ''}
188-
${this._renderKeyList('personal', this.keyData.personalKeys)}
188+
${this._renderKeyList('personal', this.keyListState.personalKeys)}
189189
`
190190
: ''}
191-
${this.keyData.state === 'error'
191+
${this.keyListState.type === 'error'
192192
? html` <cc-notice intent="warning" message="${i18n('cc-ssh-key-list.error.loading')}"></cc-notice> `
193193
: ''}
194194
</cc-block-section>
@@ -197,29 +197,31 @@ export class CcSshKeyList extends LitElement {
197197
<cc-block-section slot="content-body">
198198
<div slot="title">
199199
<span>${i18n('cc-ssh-key-list.github.title')}</span>
200-
${this.keyData.state === 'loaded' && this.keyData.isGithubLinked && this.keyData.githubKeys.length > 2
201-
? html` <cc-badge circle>${this.keyData.githubKeys.length}</cc-badge> `
200+
${this.keyListState.type === 'loaded' &&
201+
this.keyListState.isGithubLinked &&
202+
this.keyListState.githubKeys.length > 2
203+
? html` <cc-badge circle>${this.keyListState.githubKeys.length}</cc-badge> `
202204
: ''}
203205
</div>
204206
<div slot="info">${i18n('cc-ssh-key-list.github.info')}</div>
205207
206-
${this.keyData.state === 'loading' ? html` ${this._renderKeyList('skeleton', SKELETON_KEYS)} ` : ''}
207-
${this.keyData.state === 'loaded' && !this.keyData.isGithubLinked
208+
${this.keyListState.type === 'loading' ? html` ${this._renderKeyList('skeleton', SKELETON_KEYS)} ` : ''}
209+
${this.keyListState.type === 'loaded' && !this.keyListState.isGithubLinked
208210
? html` <p class="info-msg">${i18n('cc-ssh-key-list.github.unlinked')}</p> `
209211
: ''}
210-
${this.keyData.state === 'loaded' && this.keyData.isGithubLinked
212+
${this.keyListState.type === 'loaded' && this.keyListState.isGithubLinked
211213
? html`
212-
${this.keyData.githubKeys.length === 0
214+
${this.keyListState.githubKeys.length === 0
213215
? html`
214216
<p class="info-msg" id="github-keys-empty-msg" tabindex="-1">
215217
${i18n('cc-ssh-key-list.github.empty')}
216218
</p>
217219
`
218220
: ''}
219-
${this._renderKeyList('github', this.keyData.githubKeys)}
221+
${this._renderKeyList('github', this.keyListState.githubKeys)}
220222
`
221223
: ''}
222-
${this.keyData.state === 'error'
224+
${this.keyListState.type === 'error'
223225
? html` <cc-notice intent="warning" message="${i18n('cc-ssh-key-list.error.loading')}"></cc-notice> `
224226
: ''}
225227
</cc-block-section>
@@ -285,7 +287,7 @@ export class CcSshKeyList extends LitElement {
285287
(key) => key.name,
286288
(key) => {
287289
const name = key.name;
288-
const isWaiting = !skeleton && key.state !== 'idle';
290+
const isWaiting = !skeleton && key.type !== 'idle';
289291
const classes = {
290292
'key--personal': type === 'personal',
291293
'key--github': type === 'github',

src/components/cc-ssh-key-list/cc-ssh-key-list.smart.js

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import './cc-ssh-key-list.js';
1818
* @typedef {import('./cc-ssh-key-list.js').CcSshKeyList} CcSshKeyList
1919
* @typedef {import('./cc-ssh-key-list.types.js').SshKey} SshKey
2020
* @typedef {import('./cc-ssh-key-list.types.js').CreateSshKeyFormState} CreateSshKeyFormState
21-
* @typedef {import('./cc-ssh-key-list.types.js').KeyDataStateLoadedAndUnlinked} KeyDataStateLoadedAndUnlinked
22-
* @typedef {import('./cc-ssh-key-list.types.js').KeyDataStateLoadedAndLinked} KeyDataStateLoadedAndLinked
21+
* @typedef {import('./cc-ssh-key-list.types.js').SshKeyListStateLoadedAndLinked} SshKeyListStateLoadedAndLinked
22+
* @typedef {import('./cc-ssh-key-list.types.js').SshKeyListStateLoadedAndUnlinked} SshKeyListStateLoadedAndUnlinked
2323
* @typedef {import('../../lib/send-to-api.types.js').ApiConfig} ApiConfig
2424
* @typedef {import('../../lib/smart/smart-component.types.js').OnContextUpdateArgs<CcSshKeyList>} OnContextUpdateArgs
2525
*/
@@ -38,22 +38,22 @@ defineSmartComponent({
3838
// Retrieving SSH keys is done in two steps, hidden in the `fetchAllKeys()` implementation:
3939
// - first, we retrieve the current user information to check if their GitHub account is linked to their main account;
4040
// - then, we fetch the personal SSH keys and the GitHub keys if needed.
41-
// Note: we intentionally show `loading` state only on initial load and not on further actions, to keep a responsive UI.
41+
// Note: we intentionally show `loading` type only on initial load and not on further actions, to keep a responsive UI.
4242
function refreshList() {
4343
return fetchAllKeys({ apiConfig, signal, cacheDelay: 0 })
4444
.then(({ isGithubLinked, personalKeys, githubKeys }) => {
45-
updateComponent('keyData', {
46-
state: 'loaded',
47-
// linked (or unlinked) GitHub account state passed to the component
45+
updateComponent('keyListState', {
46+
type: 'loaded',
47+
// linked (or unlinked) GitHub account type passed to the component
4848
isGithubLinked,
4949
// internal key states initialization (to `idle`) after API fetch, to separate fetched data from UI infos
50-
personalKeys: personalKeys.map((key) => ({ ...key, state: 'idle' })),
51-
githubKeys: githubKeys?.map((key) => ({ ...key, state: 'idle' })),
50+
personalKeys: personalKeys.map((key) => ({ ...key, type: 'idle' })),
51+
githubKeys: githubKeys?.map((key) => ({ ...key, type: 'idle' })),
5252
});
5353
})
5454
.catch((error) => {
5555
console.error(error);
56-
updateComponent('keyData', { state: 'error' });
56+
updateComponent('keyListState', { type: 'error' });
5757
});
5858
}
5959

@@ -79,11 +79,11 @@ defineSmartComponent({
7979

8080
onEvent('cc-ssh-key-list:delete', ({ name }) => {
8181
updateComponent(
82-
'keyData',
83-
/** @param {KeyDataStateLoadedAndUnlinked|KeyDataStateLoadedAndLinked} keyData */
84-
(keyData) => {
85-
const key = keyData.personalKeys.find((key) => key.name === name);
86-
key.state = 'deleting';
82+
'keyListState',
83+
/** @param {SshKeyListStateLoadedAndLinked|SshKeyListStateLoadedAndUnlinked} keyListState */
84+
(keyListState) => {
85+
const key = keyListState.personalKeys.find((key) => key.name === name);
86+
key.type = 'deleting';
8787
},
8888
);
8989

@@ -96,53 +96,53 @@ defineSmartComponent({
9696
console.error(error);
9797
notifyError(error, i18n('cc-ssh-key-list.error.delete', { name }));
9898
updateComponent(
99-
'keyData',
100-
/** @param {KeyDataStateLoadedAndUnlinked|KeyDataStateLoadedAndLinked} keyData */
101-
(keyData) => {
102-
const key = keyData.personalKeys.find((key) => key.name === name);
103-
key.state = 'idle';
99+
'keyListState',
100+
/** @param {SshKeyListStateLoadedAndLinked|SshKeyListStateLoadedAndUnlinked} keyListState */
101+
(keyListState) => {
102+
const key = keyListState.personalKeys.find((key) => key.name === name);
103+
key.type = 'idle';
104104
},
105105
);
106106
});
107107
});
108108

109109
onEvent('cc-ssh-key-list:import', ({ name, key, fingerprint }) => {
110110
updateComponent(
111-
'keyData',
112-
/** @param {KeyDataStateLoadedAndLinked} keyData */
113-
(keyData) => {
114-
const key = keyData.githubKeys.find((key) => key.name === name);
115-
key.state = 'importing';
111+
'keyListState',
112+
/** @param {SshKeyListStateLoadedAndLinked} keyListState */
113+
(keyListState) => {
114+
const key = keyListState.githubKeys.find((key) => key.name === name);
115+
key.type = 'importing';
116116
},
117117
);
118118

119119
importKey({ apiConfig, key: { name, key } })
120120
.then(() => {
121121
notifySuccess(i18n('cc-ssh-key-list.success.import', { name }));
122122
updateComponent(
123-
'keyData',
124-
/** @param {KeyDataStateLoadedAndLinked} keyData */
125-
(keyData) => {
126-
keyData.personalKeys.push({ state: 'idle', name, fingerprint });
127-
keyData.githubKeys = keyData.githubKeys.filter((k) => k.name !== name);
123+
'keyListState',
124+
/** @param {SshKeyListStateLoadedAndLinked} keyListState */
125+
(keyListState) => {
126+
keyListState.personalKeys.push({ type: 'idle', name, fingerprint });
127+
keyListState.githubKeys = keyListState.githubKeys.filter((k) => k.name !== name);
128128
},
129129
);
130130
})
131131
.catch((error) => {
132132
console.error(error);
133133
notifyError(error, i18n('cc-ssh-key-list.error.import', { name }));
134134
updateComponent(
135-
'keyData',
136-
/** @param {KeyDataStateLoadedAndLinked} keyData */
137-
(keyData) => {
138-
const key = keyData.githubKeys.find((key) => key.name === name);
139-
key.state = 'idle';
135+
'keyListState',
136+
/** @param {SshKeyListStateLoadedAndLinked} keyListState */
137+
(keyListState) => {
138+
const key = keyListState.githubKeys.find((key) => key.name === name);
139+
key.type = 'idle';
140140
},
141141
);
142142
});
143143
});
144144

145-
updateComponent('keyData', { state: 'loading' });
145+
updateComponent('keyListState', { type: 'loading' });
146146
component.createKeyFormState = { type: 'idle' };
147147
component.resetCreateKeyForm();
148148

0 commit comments

Comments
 (0)