Skip to content

Commit 8abc506

Browse files
committed
refactor: move things around and address feedback
1 parent 3d00b70 commit 8abc506

5 files changed

Lines changed: 317 additions & 164 deletions

File tree

blocks/edit/da-title/da-title.js

Lines changed: 76 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ export default class DaTitle extends LitElement {
3838
previewPrefix: { attribute: false },
3939
livePrefix: { attribute: false },
4040
hasChanges: { attribute: false },
41-
_savingDisabled: { state: true },
41+
configSaveDisabled: { attribute: false },
42+
showConfigStaleDialog: { attribute: false },
4243
_actionsVis: { state: true },
4344
_status: { state: true },
4445
_fixedActions: { state: true },
@@ -48,39 +49,48 @@ export default class DaTitle extends LitElement {
4849
connectedCallback() {
4950
super.connectedCallback();
5051
this.shadowRoot.adoptedStyleSheets = [sheet];
51-
this._actionsVis = this.getInitialActions();
52-
this.hasChanges = false;
53-
this._savingDisabled = false;
54-
this._isStaleIgnored = false;
55-
this._pollSession = 0;
5652
inlinesvg({ parent: this.shadowRoot, paths: ICONS });
57-
this.syncConfigPolling();
58-
59-
if (this.details.view === 'sheet') {
60-
this.collabStatus = window.navigator.onLine
61-
? 'connected'
62-
: 'offline';
63-
64-
window.addEventListener('online', () => { this.collabStatus = 'connected'; });
65-
window.addEventListener('offline', () => { this.collabStatus = 'offline'; });
66-
}
6753
}
6854

6955
disconnectedCallback() {
70-
this.clearConfigPolling();
56+
this.removeCollabListeners();
7157
super.disconnectedCallback();
7258
}
7359

7460
updated(changedProperties) {
7561
super.updated(changedProperties);
76-
if (changedProperties.has('details')) {
77-
this._actionsVis = this.getInitialActions();
78-
this._savingDisabled = false;
79-
this._isStaleIgnored = false;
80-
this.syncConfigPolling();
62+
if (changedProperties.has('details')) this.syncDetailsState();
63+
if (changedProperties.has('showConfigStaleDialog')) {
64+
if (this.showConfigStaleDialog) {
65+
this.openConfigStaleDialog();
66+
} else {
67+
this._dialog = undefined;
68+
}
8169
}
8270
}
8371

72+
async openConfigStaleDialog() {
73+
this._dialog = undefined;
74+
await import('../../shared/da-dialog/da-dialog.js');
75+
this._dialog = {
76+
title: 'Config Updated',
77+
content: html`
78+
<p>The config has been updated. Please refresh to get the latest changes, or ignore to keep your existing edits.</p>
79+
`,
80+
action: {
81+
style: 'accent',
82+
label: 'Refresh',
83+
click: () => this.handleConfigRefreshAction(),
84+
},
85+
ignoreAction: {
86+
style: 'primary outline',
87+
label: 'Ignore',
88+
click: () => this.handleConfigIgnoreAction(),
89+
},
90+
close: () => this.handleConfigIgnoreAction(),
91+
};
92+
}
93+
8494
firstUpdated() {
8595
const observer = new IntersectionObserver((entries) => {
8696
this._fixedActions = !entries[0].isIntersecting;
@@ -115,6 +125,7 @@ export default class DaTitle extends LitElement {
115125
}
116126

117127
async handleAction(action) {
128+
if (!this.details) return;
118129
this.toggleActions();
119130
this._status = null;
120131

@@ -138,7 +149,7 @@ export default class DaTitle extends LitElement {
138149
}
139150

140151
if (this._isConfigView) {
141-
if (this._savingDisabled) {
152+
if (this.configSaveDisabled) {
142153
if (sendBtn) {
143154
sendBtn.classList.remove('is-sending');
144155
}
@@ -155,7 +166,7 @@ export default class DaTitle extends LitElement {
155166
console.log('Saving configuration failed because:', daConfigResp.status, await daConfigResp.text());
156167
} else {
157168
this.hasChanges = false;
158-
await this.cacheConfigData();
169+
await this.onConfigSaved?.();
159170
}
160171
return;
161172
}
@@ -241,7 +252,7 @@ export default class DaTitle extends LitElement {
241252
}
242253

243254
// toggle off if already on
244-
if (this._actionsVis.length > 0) {
255+
if ((this._actionsVis || []).length > 0) {
245256
this._actionsVis = [];
246257
return;
247258
}
@@ -263,18 +274,19 @@ export default class DaTitle extends LitElement {
263274
}
264275

265276
get _isConfigView() {
266-
return this.details.view === 'config';
277+
return this.details?.view === 'config';
267278
}
268279

269280
get _isSaveOnlyView() {
270-
return this._isConfigView || this.details.view === 'sheet';
281+
return this._isConfigView || this.details?.view === 'sheet';
271282
}
272283

273284
get isDotDADoc() {
274-
return this.details.view === 'edit' && this.details.fullpath.includes('/.da/');
285+
return this.details?.view === 'edit' && this.details?.fullpath?.includes('/.da/');
275286
}
276287

277288
getInitialActions() {
289+
if (!this.details) return [];
278290
if (this.isDotDADoc) {
279291
return [];
280292
}
@@ -284,95 +296,57 @@ export default class DaTitle extends LitElement {
284296
return [];
285297
}
286298

287-
clearConfigPolling() {
288-
if (this._pollInterval) {
289-
clearInterval(this._pollInterval);
290-
this._pollInterval = null;
299+
get visibleActions() {
300+
if (this._isSaveOnlyView) {
301+
return ['save'];
291302
}
303+
return this._actionsVis || [];
292304
}
293305

294-
syncConfigPolling() {
295-
this._pollSession = (this._pollSession || 0) + 1;
296-
this.clearConfigPolling();
297-
if (!this._isConfigView || this._isStaleIgnored) {
298-
this._cachedConfigData = null;
306+
syncDetailsState() {
307+
this._actionsVis = [];
308+
this.syncCollabStatus();
309+
}
310+
311+
syncCollabStatus() {
312+
this.removeCollabListeners();
313+
if (this.details?.view !== 'sheet') {
314+
this.collabStatus = undefined;
299315
return;
300316
}
301-
this.startConfigPolling(this._pollSession);
302-
}
303317

304-
async cacheConfigData() {
305-
const resp = await daFetch(this.details.sourceUrl);
306-
if (!resp.ok) return;
307-
this._cachedConfigData = JSON.stringify(await resp.json());
308-
}
318+
this.collabStatus = window.navigator.onLine
319+
? 'connected'
320+
: 'offline';
309321

310-
async startConfigPolling(pollSession) {
311-
await this.cacheConfigData();
312-
if (pollSession !== this._pollSession || this._isStaleIgnored) return;
313-
this.clearConfigPolling();
314-
this._pollInterval = setInterval(() => this.checkConfigChanges(), 30000);
322+
this._handleOnline = () => { this.collabStatus = 'connected'; };
323+
this._handleOffline = () => { this.collabStatus = 'offline'; };
324+
window.addEventListener('online', this._handleOnline);
325+
window.addEventListener('offline', this._handleOffline);
315326
}
316327

317-
async checkConfigChanges() {
318-
if (this._isStaleIgnored) return;
319-
const resp = await daFetch(this.details.sourceUrl);
320-
if (!resp.ok) return;
321-
const latestConfigData = JSON.stringify(await resp.json());
322-
if (!this._cachedConfigData) {
323-
this._cachedConfigData = latestConfigData;
324-
return;
328+
removeCollabListeners() {
329+
if (this._handleOnline) {
330+
window.removeEventListener('online', this._handleOnline);
331+
this._handleOnline = null;
325332
}
326-
if (latestConfigData !== this._cachedConfigData) {
327-
this.clearConfigPolling();
328-
this.showConfigStaleDialog();
333+
if (this._handleOffline) {
334+
window.removeEventListener('offline', this._handleOffline);
335+
this._handleOffline = null;
329336
}
330337
}
331338

332-
async showConfigStaleDialog() {
333-
await import('../../shared/da-dialog/da-dialog.js');
334-
this._dialog = {
335-
title: 'Config Updated',
336-
content: html`
337-
<p>The config has been updated. Please refresh to get the latest changes, or ignore to keep your existing edits.</p>
338-
`,
339-
action: {
340-
style: 'accent',
341-
label: 'Refresh',
342-
click: async () => this.handleConfigRefresh(),
343-
},
344-
ignoreAction: {
345-
style: 'primary outline',
346-
label: 'Ignore',
347-
click: () => this.handleConfigIgnore(),
348-
},
349-
close: () => this.handleConfigIgnore(),
350-
};
351-
}
352-
353-
handleConfigIgnore() {
354-
this._dialog = undefined;
355-
this._savingDisabled = true;
356-
this._isStaleIgnored = true;
357-
this.clearConfigPolling();
339+
async handleConfigIgnoreAction() {
340+
await this.onConfigIgnore?.();
358341
}
359342

360-
async handleConfigRefresh() {
361-
this._dialog = undefined;
362-
this._savingDisabled = false;
363-
this._isStaleIgnored = false;
364-
this.hasChanges = false;
365-
const daSheet = document.querySelector('.da-sheet');
366-
if (!daSheet) return;
367-
const { default: initSheet, getData } = await import('../../sheet/utils/index.js');
368-
const freshData = await getData(this.details.sourceUrl);
369-
this.sheet = await initSheet(daSheet, freshData);
370-
this.syncConfigPolling();
343+
async handleConfigRefreshAction() {
344+
await this.onConfigRefresh?.();
371345
}
372346

373347
renderActions() {
374-
const saveDisabled = this._isSaveOnlyView && (!this.hasChanges || this._savingDisabled);
375-
return html`${this._actionsVis.map((action) => html`
348+
const saveDisabled = this._isSaveOnlyView && (!this.hasChanges || this.configSaveDisabled);
349+
return html`${this.visibleActions.map((action) => html`
376350
<button
377351
@click=${() => this.handleAction(action)}
378352
class="con-button da-title-action ${saveDisabled ? '' : 'blue'}"
@@ -449,6 +423,8 @@ export default class DaTitle extends LitElement {
449423
}
450424

451425
render() {
426+
if (!this.details) return nothing;
427+
452428
return html`
453429
<div class="da-title-inner ${this._readOnly ? 'is-read-only' : ''}">
454430
<div class="da-title-name">
@@ -475,7 +451,7 @@ export default class DaTitle extends LitElement {
475451
`}
476452
</div>
477453
</div>
478-
${this._isConfigView && this._savingDisabled
454+
${this._isConfigView && this.configSaveDisabled
479455
? html`<p class="da-title-save-disabled-msg">Saving is disabled until the config has been refreshed. If you have unsaved changes that you want to preserve, you can copy them and merge them after refreshing the config.</p>`
480456
: nothing}
481457
${this._dialog ? this.renderDialog() : nothing}

blocks/sheet/sheet.js

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import getPathDetails from '../shared/pathDetails.js';
33
import { getNx } from '../../scripts/utils.js';
44
import '../edit/da-title/da-title.js';
55
import { getData } from './utils/index.js';
6+
import { createConfigStaleMonitor, fetchConfigSnapshot } from './utils/config-stale.js';
67

78
const { default: getStyle } = await import(`${getNx()}/utils/styles.js`);
89

@@ -108,13 +109,68 @@ class DaSheetPanes extends LitElement {
108109
customElements.define('da-sheet-panes', DaSheetPanes);
109110

110111
let initSheet;
112+
let configStaleMonitor;
113+
114+
function clearConfigStaleState(daTitle) {
115+
daTitle.showConfigStaleDialog = false;
116+
daTitle.configSaveDisabled = false;
117+
daTitle.onConfigIgnore = undefined;
118+
daTitle.onConfigRefresh = undefined;
119+
daTitle.onConfigSaved = undefined;
120+
}
121+
122+
function stopConfigStaleMonitor() {
123+
configStaleMonitor?.stop();
124+
configStaleMonitor = undefined;
125+
}
126+
127+
async function refreshConfigSheet(details, daTitle, daSheet) {
128+
const freshData = await getData(details.sourceUrl);
129+
daTitle.sheet = await initSheet(daSheet, freshData);
130+
}
131+
132+
async function configureConfigStaleMonitor(details, daTitle, daSheet) {
133+
stopConfigStaleMonitor();
134+
clearConfigStaleState(daTitle);
135+
136+
if (details.view !== 'config') return;
137+
138+
const getSnapshot = () => fetchConfigSnapshot(details.sourceUrl);
139+
configStaleMonitor = createConfigStaleMonitor({
140+
getSnapshot,
141+
onStale: () => {
142+
daTitle.showConfigStaleDialog = true;
143+
},
144+
});
145+
146+
daTitle.onConfigIgnore = () => {
147+
daTitle.showConfigStaleDialog = false;
148+
daTitle.configSaveDisabled = true;
149+
configStaleMonitor?.ignore();
150+
};
151+
152+
daTitle.onConfigRefresh = async () => {
153+
daTitle.showConfigStaleDialog = false;
154+
daTitle.configSaveDisabled = false;
155+
daTitle.hasChanges = false;
156+
await refreshConfigSheet(details, daTitle, daSheet);
157+
await configStaleMonitor?.refresh();
158+
};
159+
160+
daTitle.onConfigSaved = async () => {
161+
await configStaleMonitor?.syncBaseline();
162+
};
163+
164+
await configStaleMonitor.start();
165+
}
111166

112167
async function setSheet(details, daTitle, daSheet) {
113168
daTitle.details = details;
114169
daSheet.details = details;
115170

116171
if (!initSheet) initSheet = (await import('./utils/index.js')).default;
117172
daTitle.sheet = await initSheet(daSheet);
173+
await configureConfigStaleMonitor(details, daTitle, daSheet);
118174
}
119175

120176
export default async function init(el) {
@@ -146,12 +202,14 @@ export default async function init(el) {
146202
versionWrapper.append(wrapper, daSheetPanes);
147203

148204
// Set data against the title & sheet
149-
setSheet(details, daTitle, daSheet);
205+
await setSheet(details, daTitle, daSheet);
150206

151207
el.append(daTitle, versionWrapper);
152208

153209
window.addEventListener('hashchange', async () => {
210+
stopConfigStaleMonitor();
154211
details = getPathDetails();
155-
setSheet(details, daTitle, daSheet);
212+
if (!details) return;
213+
await setSheet(details, daTitle, daSheet);
156214
});
157215
}

0 commit comments

Comments
 (0)