Skip to content

Commit e302ce3

Browse files
committed
fix(conflict): label buttons properly for unsaved local changes
Conflicts with unsaved changes in indexed db work differently. When the file is overwritten on the server while editing the editing session keeps going and the new file version is shown read only. When opening the file and a conflicting version is found in indexed db that local version is shown read only allowing to connect to a new session. Signed-off-by: Max <[email protected]>
1 parent 9e50301 commit e302ce3

File tree

5 files changed

+82
-41
lines changed

5 files changed

+82
-41
lines changed

cypress/e2e/conflict.spec.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ variants.forEach(function ({ fixture, mime }) {
7373
cy.openFile(fileName)
7474
cy.intercept({ method: 'POST', url: '**/session/*/push' }).as('push')
7575
cy.wait('@push')
76-
cy.get('[data-cy="resolveThisVersion"]').click()
76+
cy.get('[data-cy="useEditorVersion"]').click()
7777

7878
getWrapper().should('not.exist')
79-
cy.get('[data-cy="resolveThisVersion"]').should('not.exist')
79+
cy.get('[data-cy="useEditorVersion"]').should('not.exist')
8080
cy.getContent().should('contain', 'cruel conflicting')
8181
},
8282
)
@@ -85,11 +85,11 @@ variants.forEach(function ({ fixture, mime }) {
8585
createConflict(fileName, 'edited-' + fileName, mime)
8686

8787
cy.openFile(fileName)
88-
cy.get('[data-cy="resolveServerVersion"]').click()
88+
cy.get('[data-cy="useReaderVersion"]').click()
8989

9090
getWrapper().should('not.exist')
91-
cy.get('[data-cy="resolveThisVersion"]').should('not.exist')
92-
cy.get('[data-cy="resolveServerVersion"]').should('not.exist')
91+
cy.get('[data-cy="useEditorVersion"]').should('not.exist')
92+
cy.get('[data-cy="useReaderVersion"]').should('not.exist')
9393
cy.getContent().should('contain', 'Hello world')
9494
cy.getContent().should('not.contain', 'cruel conflicting')
9595
})

playwright/e2e/indexed-db.spec.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,32 @@ test('recovering from indexed db', async ({
3737
await expect(editor.saveIndicator).toHaveAttribute('title', /Unsaved changes/)
3838
})
3939

40-
test('conflict when recovering from indexed db', async ({
41-
close,
42-
editor,
43-
file,
44-
reader,
45-
setOffline,
46-
setOnline,
47-
user,
48-
}) => {
49-
await expect(editor.el).toBeVisible()
50-
await setOffline()
51-
await editor.typeHeading('Hello world')
52-
await close()
53-
await setOnline()
54-
await user.uploadFile({ name: file.name, content: '## Good bye' })
55-
await file.open()
56-
await expect(editor.getHeading({ name: 'Good bye' })).toBeVisible()
57-
await expect(reader.getHeading({ name: 'Hello world' })).toBeVisible()
58-
await expect(editor.offlineState).not.toBeVisible()
59-
})
40+
;[
41+
{ source: 'local', buttonName: /Overwrite the file and save /, headingName: 'Hello world' },
42+
{ source: 'server', buttonName: /Discard the changes and edit /, headingName: 'Good bye' },
43+
].forEach(({ source, buttonName, headingName }) => {
44+
test(`resolve conflict with ${source} version`, async ({
45+
close,
46+
container,
47+
editor,
48+
file,
49+
reader,
50+
setOffline,
51+
setOnline,
52+
user,
53+
}) => {
54+
await expect(editor.el).toBeVisible()
55+
await setOffline()
56+
await editor.typeHeading('Hello world')
57+
await close()
58+
await setOnline()
59+
await user.uploadFile({ name: file.name, content: '## Good bye' })
60+
await file.open()
61+
await expect(editor.getHeading({ name: 'Good bye' })).toBeVisible()
62+
await expect(reader.getHeading({ name: 'Hello world' })).toBeVisible()
63+
await container.getButton({ name: buttonName }).click()
64+
await expect(reader.el).not.toBeVisible()
65+
await expect(editor.getHeading({ name: headingName })).toBeVisible()
66+
})
67+
68+
});

playwright/support/fixtures/editor.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
import { test as baseTest } from '@playwright/test'
77
import { EditorSection } from '../sections/EditorSection'
88
import { ReaderSection } from '../sections/ReaderSection'
9+
import { ContainerSection } from '../sections/ContainerSection'
910

1011
interface EditorFixture {
1112
editor: EditorSection
1213
reader: ReaderSection
14+
container: ContainerSection
1315
}
1416

1517
export const test = baseTest.extend<EditorFixture>({
@@ -21,4 +23,8 @@ export const test = baseTest.extend<EditorFixture>({
2123
const reader = new ReaderSection(page)
2224
await use(reader)
2325
},
26+
container: async ({ page }, use) => {
27+
const container = new ContainerSection(page)
28+
await use(container)
29+
},
2430
})

src/components/CollisionResolveDialog.vue

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,16 @@
1212
:wide="true"
1313
size="large"
1414
:disabled="clicked"
15-
data-cy="resolveThisVersion"
16-
@click="resolveThisVersion">
17-
{{ t('text', 'Overwrite the file and save the current changes') }}
15+
data-cy="useEditorVersion"
16+
@click="useEditorVersion">
17+
{{ textForSource[editorSource] }}
1818
</NcButton>
1919
<NcButton
2020
:wide="true"
2121
:disabled="clicked"
22-
data-cy="resolveServerVersion"
23-
@click="resolveServerVersion">
24-
{{
25-
t('text', 'Discard the current changes and load the latest version')
26-
}}
22+
data-cy="useReaderVersion"
23+
@click="useReaderVersion">
24+
{{ textForSource[readerSource] }}
2725
</NcButton>
2826
</div>
2927
</template>
@@ -35,6 +33,7 @@ import { useEditor } from '../composables/useEditor.ts'
3533
import { useEditorMethods } from '../composables/useEditorMethods.ts'
3634
import { useSaveService } from '../composables/useSaveService.ts'
3735
import { useSyncService } from '../composables/useSyncService.ts'
36+
import { logger } from '../helpers/logger'
3837
export default {
3938
name: 'CollisionResolveDialog',
4039
components: {
@@ -45,13 +44,27 @@ export default {
4544
type: String,
4645
required: true,
4746
},
47+
readerSource: {
48+
type: String,
49+
required: true,
50+
},
4851
},
49-
setup() {
52+
setup(props) {
53+
if (!['local', 'server'].includes(props.readerSource)) {
54+
logger.warn('Invalid reader source', props)
55+
}
5056
const { editor } = useEditor()
5157
const { syncService } = useSyncService()
5258
const { saveService } = useSaveService()
5359
const { setContent, setEditable } = useEditorMethods(editor)
60+
const editorSource = props.readerSource === 'local' ? 'server' : 'local'
61+
const textForSource = {
62+
local: t('text', 'Overwrite the file and save the unsaved changes'),
63+
server: t('text', 'Discard the changes and edit the latest version'),
64+
}
5465
return {
66+
editorSource,
67+
textForSource,
5568
setContent,
5669
setEditable,
5770
saveService,
@@ -65,16 +78,22 @@ export default {
6578
}
6679
},
6780
methods: {
68-
resolveThisVersion() {
81+
useEditorVersion() {
6982
this.clicked = true
70-
this.saveService.forceSave().then(() => this.syncService.syncUp())
83+
this.saveService.forceSave().then(() => {
84+
this.syncService.syncUp()
85+
this.$emit('resolved')
86+
})
7187
this.setEditable(!this.readOnly)
7288
},
73-
resolveServerVersion() {
89+
useReaderVersion() {
7490
this.clicked = true
7591
this.setEditable(!this.readOnly)
7692
this.setContent(this.otherVersion)
77-
this.saveService.forceSave().then(() => this.syncService.syncUp())
93+
this.saveService.forceSave().then(() => {
94+
this.syncService.syncUp()
95+
this.$emit('resolved')
96+
})
7897
},
7998
},
8099
}

src/components/Editor.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
:class="{ 'is-mobile': isMobile }"
1313
tabindex="-1">
1414
<SkeletonLoading v-if="showLoadingSkeleton" />
15-
<CollisionResolveDialog v-if="isResolvingConflict" :other-version="otherVersion" />
15+
<CollisionResolveDialog
16+
v-if="isResolvingConflict"
17+
:other-version="otherVersion"
18+
:reader-source="localChange ? 'local' : 'server'"
19+
@resolved="$emit('resolved')" />
1620
<Wrapper
1721
v-if="displayed"
1822
:is-resolving-conflict="isResolvingConflict"
@@ -341,8 +345,11 @@ export default defineComponent({
341345
return this.hasSyncCollission && !this.readOnly
342346
},
343347
hasSyncCollission() {
344-
return Boolean(this.localChange) ||
345-
(this.syncError && this.syncError.type === ERROR_TYPE.SAVE_COLLISSION)
348+
return (
349+
Boolean(this.localChange)
350+
|| (this.syncError
351+
&& this.syncError.type === ERROR_TYPE.SAVE_COLLISSION)
352+
)
346353
},
347354
otherVersion() {
348355
return this.localChange || this.syncError.data.outsideChange

0 commit comments

Comments
 (0)