CnSupportDialog
A first-open "support this project" modal every Conduction app can mount from @conduction/nextcloud-vue. Personal note in the Conduction voice, signed by the founder in a handwritten font, with four CTAs in a deliberate priority order:
- Suggest a feature — primary. Framed as the single most valuable contribution.
- Review on App Store — secondary. Helps other people find the app.
- Donate — tertiary. Defaults to ConductionNL GitHub Sponsors.
- Get support — tertiary. Routes organisations to a Conduction support partner (Host / Service / Certified). Conduction does not sell direct support — the apps stay free and support runs through partners. Defaults to
https://www.conduction.nl/support.
The dialog is paired with useSupportDialog — a composable that persists the dismissed state per appSlug in localStorage so users only see the note the first time they open an app.
The same dialog is also wired up automatically as the fourth container of CnFeaturesAndRoadmapSidebar ("Support this project" → "Show support note"), so users who dismissed the first-open prompt can always get back to it from the roadmap surface every Conduction app already mounts.
Try it
<template>
<div>
<NcButton @click="show = true">Show support note</NcButton>
<CnSupportDialog
v-if="show"
app-name="Decidiq"
app-slug="decidesk"
app-store-url="https://apps.nextcloud.com/apps/decidesk"
feature-request-url="https://github.com/ConductionNL/decidesk/issues/new"
@close="show = false" />
</div>
</template>
<script>
import { CnSupportDialog } from '@conduction/nextcloud-vue'
export default {
components: { CnSupportDialog },
data() { return { show: false } },
}
</script>
Fleet auto-mount via CnAppRoot (default)
Apps that mount CnAppRoot get the first-open note automatically on a lib bump — no per-app wiring. CnAppRoot derives the slug/name/App-Store/feature-request URLs from appId and persists the "seen" flag per-user (server preferences endpoint, localStorage fallback). Opt out with :support-dialog="false", or override copy/URLs with an object:
<CnAppRoot :app-id="appId" :manifest="manifest" /> <!-- auto-shows -->
<CnAppRoot :app-id="appId" :manifest="manifest" :support-dialog="false" /> <!-- opt out -->
<CnAppRoot :app-id="appId" :manifest="manifest" :support-dialog="{ appName: 'Pipelinq', donateUrl: '…' }" />
Server persistence requires the host app to expose the generic preferences endpoint (GET/PUT /apps/{appId}/api/preferences/{key}, backed by IConfig user values). The nextcloud-app-template ships the canonical controller; if it's missing, the dialog falls back to per-browser localStorage.
First-open adoption (manual)
For apps not using CnAppRoot, pair useSupportDialog with a manual mount for the same "show once per user, then never again" UX:
<template>
<CnSupportDialog
v-if="visible"
app-name="Decidiq"
app-slug="decidesk"
app-store-url="https://apps.nextcloud.com/apps/decidesk"
feature-request-url="https://github.com/ConductionNL/decidesk/issues/new"
@close="hide" />
</template>
<script>
import { CnSupportDialog, useSupportDialog } from '@conduction/nextcloud-vue'
export default {
components: { CnSupportDialog },
setup() {
return useSupportDialog('decidesk')
},
}
</script>
useSupportDialog reads localStorage["cn-support-dialog-shown:decidesk"]:
- if absent,
visible.valuestartstrue, - after the user dismisses,
hide()writes"1"and setsvisible.value = false, - subsequent app opens stay quiet.
Use reset() from the same composable to re-enable the dialog (tests, admin "show again" UI, etc).
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
appName | String (required) | — | Interpolated into the body copy and the dialog title. |
appSlug | String (required) | — | Kebab-case app id; pairs with the useSupportDialog localStorage namespace. |
appStoreUrl | String (required) | — | URL the "Review on App Store" CTA opens. |
featureRequestUrl | String (required) |