Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 61 additions & 2 deletions src/Forms.vue
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,53 @@ export default {
loading.value = false
}

/**
* Clean up stale localStorage entries for forms that are no longer available.
* Removes localStorage keys matching the pattern `nextcloud_forms_*_activeResponseView`
* where the form hash no longer exists in the current forms list.
*/
const cleanupStaleLocalStorageEntries = () => {
try {
// Get all current form hashes
const currentFormHashes = new Set(
[...forms.value, ...allSharedForms.value].map(
(form) => form.hash,
),
)

// Iterate through all localStorage keys
const keysToRemove = []
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i)
if (
key
&& key.startsWith('nextcloud_forms_')
&& key.endsWith('_activeResponseView')
) {
// Extract hash from key: nextcloud_forms_<hash>_activeResponseView
const hash = key.substring(
'nextcloud_forms_'.length,
key.length - '_activeResponseView'.length,
)
// If form hash is not in current forms, mark for removal
if (!currentFormHashes.has(hash)) {
keysToRemove.push(key)
}
}
}

// Remove stale entries
keysToRemove.forEach((key) => {
localStorage.removeItem(key)
logger.debug(`Removed stale localStorage entry: ${key}`)
})
} catch (err) {
logger.debug('Error cleaning up stale localStorage entries', {
error: err,
})
}
}

/**
* Fetch a partial form by its hash after initial load completes.
*
Expand Down Expand Up @@ -447,6 +494,17 @@ export default {
forms.value.splice(formIndex, 1)
deletedFormHash.value = deletedHash

// Remove localStorage entry for this form's active response view
try {
localStorage.removeItem(
`nextcloud_forms_${deletedHash}_activeResponseView`,
)
} catch (err) {
logger.debug('Error removing localStorage entry for deleted form', {
error: err,
})
}

if (deletedHash === routeHash.value && route.name !== 'root') {
// Navigate to root without triggering route guards
router.replace({ name: 'root' })
Expand Down Expand Up @@ -477,8 +535,9 @@ export default {
}
}

onMounted(() => {
loadForms()
onMounted(async () => {
await loadForms()
cleanupStaleLocalStorageEntries()
subscribe('forms:last-updated:set', onLastUpdatedByEventBus)
subscribe('forms:ownership-transfered', onDeleteForm)
})
Expand Down
49 changes: 49 additions & 0 deletions src/views/Results.vue
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ export default {
// Reload results when form changes
async hash() {
await this.fetchFullForm(this.form.id)
this.loadActiveResponseViewFromLocalStorage()
this.loadFormResults()
SetWindowTitle(this.formTitle)
},
Expand All @@ -521,15 +522,63 @@ export default {
})
this.loadFormResults()
}, INPUT_DEBOUNCE_MS),

// Persist active response view to localStorage when it changes
activeResponseView(newView) {
this.saveActiveResponseViewToLocalStorage(newView.id)
},
},

async beforeMount() {
await this.fetchFullForm(this.form.id)
this.loadActiveResponseViewFromLocalStorage()
this.loadFormResults()
SetWindowTitle(this.formTitle)
},

methods: {
/**
* Load the active response view preference from localStorage for the current form.
* Applies stored value if available and otherwise resets to default (summary)
*/
loadActiveResponseViewFromLocalStorage() {
try {
const storedViewId = localStorage.getItem(
`nextcloud_forms_${this.form.hash}_activeResponseView`,
)
if (storedViewId) {
const view = responseViews.find((v) => v.id === storedViewId)
if (view) {
this.activeResponseView = view
}
} else {
this.activeResponseView = responseViews[0]
}
} catch (err) {
logger.debug('Error loading activeResponseView from localStorage', {
error: err,
})
}
},

/**
* Save the active response view preference to localStorage for the current form.
*
* @param {string} viewId - The ID of the view ('summary' or 'responses')
*/
saveActiveResponseViewToLocalStorage(viewId) {
try {
localStorage.setItem(
`nextcloud_forms_${this.form.hash}_activeResponseView`,
viewId,
)
} catch (err) {
logger.debug('Error saving activeResponseView to localStorage', {
error: err,
})
}
},

async onUnlinkFile() {
await axios.patch(
generateOcsUrl('apps/forms/api/v3/forms/{formId}', {
Expand Down
Loading