Skip to content

Commit

Permalink
fix: better error handling when state could not be loaded
Browse files Browse the repository at this point in the history
  • Loading branch information
dulnan committed Jan 15, 2025
1 parent 21d9059 commit 5602a74
Show file tree
Hide file tree
Showing 22 changed files with 168 additions and 15 deletions.
12 changes: 12 additions & 0 deletions css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,18 @@ html.bk-hide-non-editable {
right: var(--bk-root-offset-right);
}

.bk-state-unavailable {
@apply bg-red-normal p-20 text-white;

h2 {
@apply text-2xl font-bold;
}

p {
@apply text-lg;
}
}

.bk {
.bk-beta-indicator {
@apply bg-accent-700 text-white text-xs uppercase rounded-full font-semibold px-[7px] py-[1px];
Expand Down
12 changes: 12 additions & 0 deletions i18n/de.po
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,18 @@ msgctxt "singleComment"
msgid "comment"
msgstr "Kommentar"

msgctxt "stateUnavailableText"
msgid ""
"This could be due to missing permissions or a temporary problem. Please try "
"again later."
msgstr ""
"Dies könnte auf fehlende Berechtigungen oder ein vorübergehendes Problem "
"zurückzuführen sein. Bitte versuchen Sie es später nochmal."

msgctxt "stateUnavailableTitle"
msgid "The edit state could not be loaded."
msgstr "Der Bearbeitungszustand konnte nicht geladen werden."

msgctxt "structureToolbarLabel"
msgid "Structure"
msgstr "Struktur"
Expand Down
10 changes: 10 additions & 0 deletions i18n/fr.po
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,16 @@ msgctxt "singleComment"
msgid "comment"
msgstr ""

msgctxt "stateUnavailableText"
msgid ""
"This could be due to missing permissions or a temporary problem. Please try "
"again later."
msgstr ""

msgctxt "stateUnavailableTitle"
msgid "The edit state could not be loaded."
msgstr ""

msgctxt "structureToolbarLabel"
msgid "Structure"
msgstr "Structure"
Expand Down
10 changes: 10 additions & 0 deletions i18n/gsw_CH.po
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,16 @@ msgctxt "singleComment"
msgid "comment"
msgstr "Kommentar"

msgctxt "stateUnavailableText"
msgid ""
"This could be due to missing permissions or a temporary problem. Please try "
"again later."
msgstr ""

msgctxt "stateUnavailableTitle"
msgid "The edit state could not be loaded."
msgstr ""

msgctxt "structureToolbarLabel"
msgid "Structure"
msgstr "Struktur"
Expand Down
10 changes: 10 additions & 0 deletions i18n/it.po
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,16 @@ msgctxt "singleComment"
msgid "comment"
msgstr ""

msgctxt "stateUnavailableText"
msgid ""
"This could be due to missing permissions or a temporary problem. Please try "
"again later."
msgstr ""

msgctxt "stateUnavailableTitle"
msgid "The edit state could not be loaded."
msgstr ""

msgctxt "structureToolbarLabel"
msgid "Structure"
msgstr "Struttura"
Expand Down
1 change: 1 addition & 0 deletions playground/app/blokkli.editAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export default defineBlokkliEditAdapter((ctx) => {
const adapter: BlokkliAdapter<MutatedState> = {
mediaLibraryGetResults,
loadState() {
// throw new Error('Failed to load state')
const page = entityStorageManager.getContent(ctx.value.entityUuid)
if (!page) {
throw new Error(
Expand Down
11 changes: 9 additions & 2 deletions src/runtime/adapter/drupal/graphqlMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,18 @@ export default defineBlokkliEditAdapter<ParagraphsBlokkliEditStateFragment>(
return Promise.resolve(config.allTypes)
}

const loadState: DrupalAdapter['loadState'] = () =>
useGraphqlQuery('pbEditState', {
const loadState: DrupalAdapter['loadState'] = async () => {
const state = await useGraphqlQuery('pbEditState', {
...ctx.value,
}).then((v) => v?.data.state)

if (!state) {
throw new Error('Failed to load state.')
}

return state
}

const loadStateAtIndex: DrupalAdapter['loadStateAtIndex'] = (
historyIndex,
) =>
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/blokkliPlugins/Sidebar/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const props = withDefaults(
icon: BlokkliIcon
weight?: string | number
renderAlways?: boolean
disabled?: boolean
region?: 'left' | 'right'
minWidth?: number
minHeight?: number
Expand Down Expand Up @@ -149,7 +150,7 @@ const detachedKey = computed(() => 'sidebar:detached:' + props.id)
const storageKey = computed(() => 'sidebar:active:' + props.region)
const isDetached = storage.use(detachedKey, false)
const isDisabled = computed(
() => props.editOnly && state.editMode.value !== 'editing',
() => props.editOnly && state.editMode.value !== 'editing' && !props.disabled,
)
const activeSidebar = storage.use(storageKey, '')
Expand Down
10 changes: 8 additions & 2 deletions src/runtime/components/BlokkliField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const isGlobalProxyMode = inject<ComputedRef<boolean> | null>(
null,
)
const isInReusable = inject(INJECT_IS_IN_REUSABLE, false)
const isPreview = inject<boolean>(INJECT_IS_PREVIEW, false)
const isPreview = inject<ComputedRef<boolean> | null>(INJECT_IS_PREVIEW, null)
const isNested = inject(INJECT_IS_NESTED, false)
const nestingLevel = inject<number>(INJECT_NESTING_LEVEL, 0)
const mutatedFields = inject<Record<string, MutatedField> | null>(
Expand Down Expand Up @@ -179,7 +179,13 @@ function filterVisible(item?: FieldListItemTyped | FieldListItem): boolean {
}
const filteredList = computed<FieldListItemTyped[]>(() => {
if (mutatedFields && !isInReusable && editContext && fieldKey.value) {
if (
mutatedFields &&
!isInReusable &&
editContext &&
fieldKey.value &&
(isPreview?.value || isEditing)
) {
return ((mutatedFields[fieldKey.value] || {}).list || [])
.map((v) => {
const mutatedOptions = editContext.mutatedOptions[v.uuid] || {}
Expand Down
15 changes: 15 additions & 0 deletions src/runtime/components/Edit/EditProvider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@

<div id="bk-banner-container">
<Messages />
<div v-if="!state.stateAvailable.value" class="bk-state-unavailable">
<h2>
{{
$t('stateUnavailableTitle', 'The edit state could not be loaded.')
}}
</h2>
<p>
{{
$t(
'stateUnavailableText',
'This could be due to missing permissions or a temporary problem. Please try again later.',
)
}}
</p>
</div>
</div>
</Teleport>
<Actions v-if="!isInitializing" />
Expand Down
6 changes: 5 additions & 1 deletion src/runtime/components/Edit/Features/EditableField/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ type Editable = {
value?: string
}
const { selection, adapter, types, $t, dom, runtimeConfig } = useBlokkli()
const { selection, adapter, types, $t, dom, runtimeConfig, state } =
useBlokkli()
const editable = ref<Editable | null>(null)
const hasTransition = ref(false)
Expand Down Expand Up @@ -117,6 +118,9 @@ const buildEditable = (
}
onBlokkliEvent('editable:focus', (e) => {
if (!state.canEdit.value) {
return
}
hasTransition.value = !editable.value
editable.value = buildEditable(e.fieldName, e.uuid) || null
if (editable.value) {
Expand Down
1 change: 1 addition & 0 deletions src/runtime/components/Edit/Features/EntityTitle/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<button
ref="buttonEl"
class="bk-toolbar-button"
:disabled="!state.canEdit.value"
@click="eventBus.emit('editEntity')"
>
<div class="bk-toolbar-title">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ onMounted(() => {
if (
isEmpty.value &&
!state.mutations.value.length &&
settings.value.showDialogWhenEmpty
settings.value.showDialogWhenEmpty &&
state.canEdit.value
) {
showModal.value = true
}
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Edit/Features/Preview/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
:title="$t('previewNewWindow', 'Preview (new window)')"
region="after-menu"
icon="open_in_new"
:disabled="!state.canEdit.value"
:tour-text="
$t(
'previewNewWindowTourText',
Expand All @@ -25,7 +26,7 @@ defineBlokkliFeature({
description: 'Provides a button to open a preview in a new window.',
})
const { $t } = useBlokkli()
const { $t, state } = useBlokkli()
const route = useRoute()
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Edit/Features/PreviewGrant/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
v-if="!ui.isMobile.value"
id="preview_with_smartphone"
:title="$t('previewWithSmartphone', 'Preview (with smartphone)')"
:disabled="!state.canEdit.value"
:tour-text="
$t(
'previewWithSmartphoneTourText',
Expand Down Expand Up @@ -54,7 +55,7 @@ const { adapter } = defineBlokkliFeature({
viewports: ['desktop'],
})
const { $t, ui } = useBlokkli()
const { $t, ui, state } = useBlokkli()
const qrCodeVisible = ref(false)
const previewGrantUrl = ref<string | undefined | null>('')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
v-slot="{ width, height, isDetached }"
:title="$t('responsivePreviewTitle', 'Responsive Preview')"
:tour-text="tourText"
:disabled="!state.canEdit.value"
:min-width="375"
:min-height="375"
:size="size"
Expand Down Expand Up @@ -83,7 +84,7 @@ defineBlokkliFeature({
'Provides a responsive preview of the current edit state in an iframe.',
})
const { $t, storage } = useBlokkli()
const { $t, storage, state } = useBlokkli()
const selectedViewportId = storage.use('mobile-preview:viewport', 'iphone-se')
const isRotated = storage.use('mobile-preview:rotated', false)
Expand Down
11 changes: 9 additions & 2 deletions src/runtime/components/Edit/PreviewProvider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const mutatedEntity = computed(
() => mutatedEntityFromState.value || props.entity,
)
const { data, refresh } = await useAsyncData(() =>
const { data, refresh, error } = await useAsyncData(() =>
adapter.loadState().then((v) => adapter.mapState(v)),
)
Expand Down Expand Up @@ -94,8 +94,10 @@ const updateState = () => {
updateState()
const isPreview = computed(() => !error.value)
provide(INJECT_MUTATED_FIELDS_MAP, mutatedFieldsMap)
provide(INJECT_IS_PREVIEW, true)
provide(INJECT_IS_PREVIEW, isPreview)
provide<ItemEditContext>(INJECT_EDIT_CONTEXT, {
mutatedOptions,
eventBus,
Expand Down Expand Up @@ -127,6 +129,11 @@ const onMouseDown = () => {
* update.
*/
function checkChangedDate() {
// State loading has failed, so we can return here; no need to poll.
if (!isPreview.value) {
return
}
clearTimeout(timeout)
const delay = adapter.getLastChanged ? 1000 : 5000
Expand Down
27 changes: 24 additions & 3 deletions src/runtime/helpers/stateProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type StateProvider = {
editMode: Readonly<Ref<EditMode>>
mutatedEntity: Readonly<Ref<any>>
canEdit: ComputedRef<boolean>
stateAvailable: ComputedRef<boolean>
isLoading: Readonly<Ref<boolean>>
getFieldBlockCount: (key: string) => number
getBlockBundleCount: (bundle: string) => number
Expand All @@ -67,6 +68,8 @@ export default async function (
context: ComputedRef<AdapterContext>,
$t: TextProvider,
): Promise<StateProvider> {
const stateLoaded = ref(false)
const stateLoadError = ref(false)
const owner = ref<BlokkliOwner | null>(null)
const refreshKey = ref('')
const mutatedFields = ref<MutatedField[]>([])
Expand Down Expand Up @@ -291,13 +294,26 @@ export default async function (
}

async function loadState() {
const state = await adapter.loadState()
if (state) {
try {
const state = await adapter.loadState()
if (!state) {
throw new Error('Missing state.')
}
setContext(adapter.mapState(state))
stateLoadError.value = false
stateLoaded.value = true
} catch {
stateLoadError.value = true
stateLoaded.value = false
}
}

const canEdit = computed(() => !!owner.value?.currentUserIsOwner)
const canEdit = computed(
() =>
stateLoaded.value &&
!!owner.value?.currentUserIsOwner &&
!stateLoadError.value,
)
const isTranslation = computed(
() =>
context.value.language !== translation.value.sourceLanguage &&
Expand Down Expand Up @@ -331,7 +347,12 @@ export default async function (

await loadState()

const stateAvailable = computed(
() => stateLoaded.value && !stateLoadError.value,
)

return {
stateAvailable,
refreshKey,
owner: readonly(owner),
mutatedFields,
Expand Down
8 changes: 8 additions & 0 deletions src/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,14 @@
"source": "comment",
"translation": "Kommentar"
},
"stateUnavailableText": {
"source": "This could be due to missing permissions or a temporary problem. Please try again later.",
"translation": "Dies könnte auf fehlende Berechtigungen oder ein vorübergehendes Problem zurückzuführen sein. Bitte versuchen Sie es später nochmal."
},
"stateUnavailableTitle": {
"source": "The edit state could not be loaded.",
"translation": "Der Bearbeitungszustand konnte nicht geladen werden."
},
"structureToolbarLabel": {
"source": "Structure",
"translation": "Struktur"
Expand Down
8 changes: 8 additions & 0 deletions src/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,14 @@
"source": "comment",
"translation": ""
},
"stateUnavailableText": {
"source": "This could be due to missing permissions or a temporary problem. Please try again later.",
"translation": ""
},
"stateUnavailableTitle": {
"source": "The edit state could not be loaded.",
"translation": ""
},
"structureToolbarLabel": {
"source": "Structure",
"translation": "Structure"
Expand Down
Loading

0 comments on commit 5602a74

Please sign in to comment.