Skip to content

Commit ce81fbc

Browse files
committed
components: add data sharing config modal
Add a small two-card modal that lets the user choose whether to share detailed hardware specifications on top of the always-on basic baseline. The modal explains the basic tier (always shared, cannot be turned off) and exposes a single switch for the detailed hardware specifications. A "View documentation" action links to the public privacy documentation for the full data field breakdown. The flag is persisted directly via VueUse `useStorage` against `localStorage`, intentionally bypassing the BlueOS-synced settings manager: its 3-second debounce and remote-sync echoes can otherwise revert the switch back to its previous value while the user is still toggling it. Mount the modal once at the root in `App.vue` so any settings entry point can open it through the new `isDataPrivacyModalVisible` flag on the app interface store.
1 parent 9fdf84a commit ce81fbc

3 files changed

Lines changed: 114 additions & 0 deletions

File tree

src/App.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
</v-main>
8282
</v-app>
8383
<About v-if="showAboutDialog" @update:show-about-dialog="showAboutDialog = $event" />
84+
<DataPrivacyModal />
8485
<Tutorial v-if="interfaceStore.isTutorialVisible" />
8586
<VideoLibraryModal v-if="interfaceStore.isVideoLibraryVisible" />
8687
<VehicleDiscoveryDialog v-model="showDiscoveryDialog" show-auto-search-option />
@@ -108,6 +109,7 @@ import { computed, onBeforeMount, onBeforeUnmount, onMounted, ref, watch } from
108109
109110
import ArchitectureWarning from '@/components/ArchitectureWarning.vue'
110111
import CameraReplacementDialog from '@/components/CameraReplacementDialog.vue'
112+
import DataPrivacyModal from '@/components/DataPrivacyModal.vue'
111113
import ExternalFeaturesDiscoveryModal from '@/components/ExternalFeaturesDiscoveryModal.vue'
112114
import GlassModal from '@/components/GlassModal.vue'
113115
import SkullAnimation from '@/components/SkullAnimation.vue'
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<template>
2+
<teleport to="body">
3+
<InteractionDialog v-model:show-dialog="isVisible" max-width="560" variant="text-only">
4+
<template #title>
5+
<div class="relative flex items-center w-full justify-center">
6+
<span>Shared Data</span>
7+
<v-btn
8+
icon="mdi-close"
9+
size="small"
10+
variant="text"
11+
class="absolute -right-3 mt-1"
12+
aria-label="Close shared data dialog"
13+
@click="close"
14+
/>
15+
</div>
16+
</template>
17+
<template #content>
18+
<div class="flex flex-col w-full -mt-6 gap-4 mb-2">
19+
<p class="text-sm text-white/85">
20+
To help us understand how Cockpit is being used and prioritise the development effort, the application
21+
shares a small set of anonymous information with the Blue Robotics team.
22+
</p>
23+
24+
<div
25+
role="switch"
26+
tabindex="0"
27+
:aria-checked="shareHardwareDetails"
28+
class="rounded-md bg-white/[0.04] border border-white/10 px-3 py-3 cursor-pointer hover:bg-white/[0.06] transition-colors duration-150"
29+
@click="shareHardwareDetails = !shareHardwareDetails"
30+
@keydown.enter.prevent="shareHardwareDetails = !shareHardwareDetails"
31+
@keydown.space.prevent="shareHardwareDetails = !shareHardwareDetails"
32+
>
33+
<div class="flex items-center gap-3">
34+
<span class="text-[14px] font-semibold text-white">Detailed hardware specifications</span>
35+
<v-switch
36+
v-model="shareHardwareDetails"
37+
hide-details
38+
density="compact"
39+
color="#4fa483"
40+
class="ml-auto -my-1 scale-75"
41+
inset
42+
aria-label="Share detailed hardware specifications"
43+
@click.stop
44+
/>
45+
</div>
46+
<p class="text-[12px] text-white/70 leading-snug mt-1">
47+
Adds the device manufacturer / model, CPU and GPU details, total memory and storage, and display size
48+
information. Helps us know which hardware we should target and test for.
49+
</p>
50+
</div>
51+
52+
<div class="rounded-md bg-white/[0.04] border border-white/10 px-3 py-3">
53+
<div class="flex items-center gap-2">
54+
<v-icon size="18" color="#7ad1aa">mdi-information-outline</v-icon>
55+
<span class="text-[14px] font-semibold text-white">Basic information</span>
56+
<span class="text-[11px] text-white/55 ml-auto">Always shared</span>
57+
</div>
58+
<p class="text-[12px] text-white/70 leading-snug mt-1">
59+
Cockpit version, runtime, system context (operating system, system language, window size, touch
60+
capability) and connected vehicle type / firmware. This baseline cannot be turned off.
61+
</p>
62+
</div>
63+
</div>
64+
</template>
65+
<template #actions>
66+
<div class="flex w-full justify-between items-center px-1">
67+
<v-btn
68+
variant="text"
69+
size="small"
70+
prepend-icon="mdi-open-in-new"
71+
:href="telemetryDataPrivacyDocsUrl"
72+
target="_blank"
73+
rel="noopener"
74+
>
75+
View documentation
76+
</v-btn>
77+
<v-btn variant="flat" size="small" class="bg-[#FFFFFF33] text-white" @click="close">Close</v-btn>
78+
</div>
79+
</template>
80+
</InteractionDialog>
81+
</teleport>
82+
</template>
83+
84+
<script setup lang="ts">
85+
import { computed, toRef } from 'vue'
86+
87+
import InteractionDialog from '@/components/InteractionDialog.vue'
88+
import { telemetryDataPrivacyDocsUrl } from '@/libs/external-telemetry/event-tracking'
89+
import { useAppInterfaceStore } from '@/stores/appInterface'
90+
import { useDevelopmentStore } from '@/stores/development'
91+
92+
const interfaceStore = useAppInterfaceStore()
93+
const developmentStore = useDevelopmentStore()
94+
95+
const isVisible = computed({
96+
get: () => interfaceStore.isDataPrivacyModalVisible,
97+
set: (v: boolean) => {
98+
interfaceStore.isDataPrivacyModalVisible = v
99+
},
100+
})
101+
102+
const shareHardwareDetails = toRef(developmentStore, 'shareHardwareDetails')
103+
104+
/**
105+
* Hide the modal.
106+
* @returns {void}
107+
*/
108+
const close = (): void => {
109+
isVisible.value = false
110+
}
111+
</script>

src/stores/appInterface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const useAppInterfaceStore = defineStore('responsive', {
6060
isGlassModalAlwaysOnTop: false,
6161
isTutorialVisible: false,
6262
isExternalFeaturesModalVisible: false,
63+
isDataPrivacyModalVisible: false,
6364
userHasSeenTutorial: useBlueOsStorage('cockpit-has-seen-tutorial', false),
6465
configPanelVisible: false,
6566
showSplashScreen: true,

0 commit comments

Comments
 (0)