Merge branch 'main' into 0.5.0-dev

This commit is contained in:
Arnab Chakraborty
2024-10-14 21:20:28 -04:00
33 changed files with 439 additions and 205 deletions

View File

@@ -37,6 +37,7 @@ export default defineConfig(({ mode }) => {
plugins: [
devtoolsPlugin,
process.env.SENTRY_AUTH_TOKEN &&
// All this plugin does is give Sentry access to source maps and release data for errors that users *choose* to report
sentryVitePlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: 'spacedriveapp',

View File

@@ -7,9 +7,8 @@ license.workspace = true
repository.workspace = true
rust-version.workspace = true
[target.'cfg(target_os = "android")'.dependencies]
sd-core = { default-features = false, features = ["mobile"], path = "../../../../../core" }
# Spacedrive Sub-crates
[target.'cfg(target_os = "ios")'.dependencies]
sd-core = { default-features = false, features = [
"ffmpeg",
@@ -17,6 +16,10 @@ sd-core = { default-features = false, features = [
"mobile"
], path = "../../../../../core" }
[target.'cfg(target_os = "android")'.dependencies]
sd-core = { path = "../../../../../core", features = ["mobile"], default-features = false }
[dependencies]
# Workspace dependencies
futures = { workspace = true }
rspc = { workspace = true }

View File

@@ -20,7 +20,7 @@ import { SafeAreaProvider } from 'react-native-safe-area-context';
import { useSnapshot } from 'valtio';
import {
ClientContextProvider,
initPlausible,
configureAnalyticsProperties,
LibraryContextProvider,
P2PContextProvider,
RspcProvider,
@@ -63,7 +63,7 @@ function AppNavigation() {
useEffect(() => {
if (buildInfo?.data) {
initPlausible({ platformType: 'mobile', buildInfo: buildInfo.data });
configureAnalyticsProperties({ platformType: 'mobile', buildInfo: buildInfo.data });
}
}, [buildInfo]);

View File

@@ -37,8 +37,9 @@ export const useContextValue = () => {
};
export const shareTelemetrySchema = z.union([
z.literal('share-telemetry'),
z.literal('minimal-telemetry')
z.literal('full'),
z.literal('minimal'),
z.literal('none')
]);
const schemas = {
@@ -58,7 +59,7 @@ const useFormState = () => {
defaultValues: {
NewLibrary: obStore.data?.['new-library'] ?? undefined,
Privacy: obStore.data?.privacy ?? {
shareTelemetry: 'share-telemetry'
shareTelemetry: 'full'
}
},
onData: (data) => (onboardingStore.data = data)
@@ -81,7 +82,7 @@ const useFormState = () => {
// opted to place this here as users could change their mind before library creation/onboarding finalization
// it feels more fitting to configure it here (once)
telemetryState.shareFullTelemetry = data.Privacy.shareTelemetry === 'share-telemetry';
telemetryState.telemetryLevelPreference = data.Privacy.shareTelemetry;
try {
// show creation screen for a bit for smoothness
@@ -93,7 +94,7 @@ const useFormState = () => {
new Promise((res) => setTimeout(res, 500))
]);
if (telemetryState.shareFullTelemetry) {
if (telemetryState.telemetryLevelPreference === 'full') {
submitPlausibleEvent({ event: { type: 'libraryCreate' } });
}

View File

@@ -59,22 +59,26 @@ const PrivacyScreen = () => {
control={form.control}
render={({ field: { onChange, value } }) => (
<>
<Pressable onPress={() => onChange('share-telemetry')}>
<Pressable onPress={() => onChange('full')}>
<RadioButton
title="Share anonymous usage"
description="Share completely anonymous telemetry data to help the developers improve the app"
isSelected={value === 'share-telemetry'}
title="Share anonymous usage data"
description="This give us a completely anonymous picture of how you use Spacedrive."
isSelected={value === 'full'}
style={tw`mb-3 mt-4`}
/>
</Pressable>
<Pressable
testID="share-minimal"
onPress={() => onChange('minimal-telemetry')}
>
<Pressable testID="share-minimal" onPress={() => onChange('minimal')}>
<RadioButton
title="Share the bare minimum"
description="Only share that I am an active user of Spacedrive and a few technical bits"
isSelected={value === 'minimal-telemetry'}
title="Share minimal data"
description="This just tells us how many people use Spacedrive and device/version details."
isSelected={value === 'minimal'}
/>
</Pressable>
<Pressable testID="share-none" onPress={() => onChange('none')}>
<RadioButton
title="Don't share anything"
description="Sends absolutely no analytics data from the Spacedrive app."
isSelected={value === 'none'}
/>
</Pressable>
</>

View File

@@ -131,12 +131,18 @@ describe('Onboarding', () => {
cy.get('h2').should('contain', 'Your Privacy');
// Check we have all privacy options
cy.get('label').contains("Don't share anything").click();
cy.get('#radiofull').should('have.attr', 'data-state', 'unchecked');
cy.get('#radiominimal').should('have.attr', 'data-state', 'unchecked');
cy.get('#radionone').should('have.attr', 'data-state', 'checked');
cy.get('label').contains('Share the bare minimum').click();
cy.get('#radiominimal-telemetry').should('have.attr', 'data-state', 'checked');
cy.get('#radioshare-telemetry').should('have.attr', 'data-state', 'unchecked');
cy.get('#radiofull').should('have.attr', 'data-state', 'unchecked');
cy.get('#radiominimal').should('have.attr', 'data-state', 'checked');
cy.get('#radionone').should('have.attr', 'data-state', 'unchecked');
cy.get('label').contains('Share anonymous usage').click();
cy.get('#radioshare-telemetry').should('have.attr', 'data-state', 'checked');
cy.get('#radiominimal-telemetry').should('have.attr', 'data-state', 'unchecked');
cy.get('#radiofull').should('have.attr', 'data-state', 'checked');
cy.get('#radiominimal').should('have.attr', 'data-state', 'unchecked');
cy.get('#radionone').should('have.attr', 'data-state', 'unchecked');
// Check More info button exists and point to the valid pravacy policy
cy.get('button').contains('More info').click();

View File

@@ -5,7 +5,7 @@ import {
FallbackProps
} from 'react-error-boundary';
import { useRouteError } from 'react-router';
import { useDebugState } from '@sd/client';
import { useDebugState, useTelemetryState } from '@sd/client';
import { Button, Dialogs } from '@sd/ui';
import { showAlertDialog } from './components';
@@ -68,6 +68,7 @@ export function ErrorPage({
}) {
useTheme();
const debug = useDebugState();
const { telemetryLevelPreference } = useTelemetryState();
const os = useOperatingSystem();
const platform = usePlatform();
const isMacOS = os === 'macOS';
@@ -134,19 +135,21 @@ export function ErrorPage({
{t('reload')}
</Button>
)}
<Button
variant="gray"
className="mt-2"
onClick={() =>
sendReportBtn
? sendReportBtn()
: sentryBrowserLazy.then(({ captureException }) =>
captureException(message)
)
}
>
{t('send_report')}
</Button>
{telemetryLevelPreference !== 'none' && (
<Button
variant="gray"
className="mt-2"
onClick={() =>
sendReportBtn
? sendReportBtn()
: sentryBrowserLazy.then(({ captureException }) =>
captureException(message)
)
}
>
{t('send_report')}
</Button>
)}
{platform.openLogsDir && (
<Button variant="gray" className="mt-2" onClick={platform.openLogsDir}>
{t('open_logs')}

View File

@@ -7,7 +7,7 @@ import {
useRspcLibraryContext,
useSelector
} from '@sd/client';
import { useShortcut } from '~/hooks';
import { useOperatingSystem, useShortcut } from '~/hooks';
import { useTopBarContext } from '../TopBar/Context';
import { useExplorerContext } from './Context';
@@ -45,6 +45,7 @@ export default function Explorer(props: PropsWithChildren<Props>) {
s.showInspector,
s.isTagAssignModeActive
]);
const isWindows = useOperatingSystem() === 'windows';
const showPathBar = explorer.showPathBar && layoutStore.showPathBar;
const rspc = useRspcLibraryContext();
@@ -95,6 +96,8 @@ export default function Explorer(props: PropsWithChildren<Props>) {
className="explorer-scroll explorer-inspector-scroll flex flex-1 flex-col overflow-x-hidden"
style={
{
'--scrollbar-width': isWindows ? '10px' : '6px',
'--scrollbar-height': isWindows ? '10px' : '6px',
'--scrollbar-margin-top': `${topBar.topBarHeight}px`,
'--scrollbar-margin-bottom': `${showPathBar ? PATH_BAR_HEIGHT + (showTagBar ? TAG_BAR_HEIGHT : 0) : 0}px`,
'paddingTop': topBar.topBarHeight,
@@ -133,7 +136,10 @@ export default function Explorer(props: PropsWithChildren<Props>) {
{showInspector && (
<Inspector
className={clsx('no-scrollbar absolute right-1.5 top-0 pb-3 pl-3 pr-1.5')}
className={clsx(
'no-scrollbar absolute top-0 pb-3 pl-3 pr-1.5',
isWindows ? 'right-3' : 'right-1.5'
)}
style={{
paddingTop: topBar.topBarHeight + 12,
bottom: showPathBar

View File

@@ -3,7 +3,7 @@ import { Suspense, useEffect, useMemo, useRef } from 'react';
import { Navigate, Outlet, useNavigate } from 'react-router-dom';
import {
ClientContextProvider,
initPlausible,
configureAnalyticsProperties,
LibraryContextProvider,
useBridgeQuery,
useClientContext,
@@ -144,16 +144,20 @@ function usePlausible() {
const plausibleEvent = usePlausibleEvent();
useEffect(() => {
initPlausible({
configureAnalyticsProperties({
buildInfo,
platformType: platform === 'tauri' ? 'desktop' : 'web'
});
}, [platform, buildInfo]);
useEffect(() => {
const interval = setInterval(() => {
plausibleEvent({ event: { type: 'ping' } });
}, 600 * 1000); // 10 minutes
const interval = setInterval(
() => {
// ping every 10 minutes -- this just tells us that Spacedrive is running and helps us gauge the amount of active users we have.
plausibleEvent({ event: { type: 'ping' } });
},
10 * 60 * 1_000
);
return () => clearInterval(interval);
}, [plausibleEvent]);

View File

@@ -82,7 +82,7 @@ export const SearchOptionSubMenu = (
<SearchOptionItemInternals {...props}>{props.name}</SearchOptionItemInternals>
</ContextMenuDivItem>
}
className={clsx(MENU_STYLES, 'explorer-scroll -mt-1.5 max-h-80', props.className)}
className={clsx(MENU_STYLES, 'default-scroll -mt-1.5 max-h-80', props.className)}
>
{props.children}
</DropdownMenu.SubMenu>
@@ -237,7 +237,7 @@ function AddFilterButton() {
onKeyDown={(e) => e.stopPropagation()}
className={clsx(
MENU_STYLES,
'explorer-scroll max-h-[80vh] min-h-[100px] min-w-[200px] max-w-fit'
'default-scroll max-h-[80vh] min-h-[100px] min-w-[200px] max-w-fit'
)}
trigger={
<Button className="flex flex-row gap-1" size="xs" variant="dotted">

View File

@@ -15,7 +15,7 @@ interface Props {
infoUrl?: string;
}
export default ({ mini, registerName, ...props }: PropsWithChildren<Props>) => {
export default function Setting({ mini, registerName, ...props }: PropsWithChildren<Props>) {
const platform = usePlatform();
if (typeof props.description === 'string')
@@ -38,7 +38,7 @@ export default ({ mini, registerName, ...props }: PropsWithChildren<Props>) => {
</Tooltip>
)}
</div>
<div className="w-[85%]">{props.description}</div>
<div className="text-balance">{props.description}</div>
{!mini && props.children}
</div>
{mini && props.children}
@@ -48,4 +48,4 @@ export default ({ mini, registerName, ...props }: PropsWithChildren<Props>) => {
) : null}
</>
);
};
}

View File

@@ -1,18 +1,26 @@
import { telemetryState, useTelemetryState } from '@sd/client';
import { Switch } from '@sd/ui';
import { TelemetryLevelPreference, telemetryState, useTelemetryState } from '@sd/client';
import { Select, SelectOption } from '@sd/ui';
import i18n from '~/app/I18n';
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
import Setting from '../Setting';
export const Component = () => {
const fullTelemetry = useTelemetryState().shareFullTelemetry;
const telemetryPreferenceOptions = [
{ value: 'full', label: i18n.t('telemetry_share_anonymous_short') },
{ value: 'minimal', label: i18n.t('telemetry_share_minimal_short') },
{ value: 'none', label: i18n.t('telemetry_share_none_short') }
] satisfies { value: TelemetryLevelPreference; label: string }[];
export const Component = () => {
const { t } = useLocale();
const { telemetryLevelPreference } = useTelemetryState();
return (
<>
<Heading title={t('privacy')} description="" />
<Setting
mini
toolTipLabel={t('learn_more_about_telemetry')}
@@ -20,11 +28,20 @@ export const Component = () => {
title={t('telemetry_title')}
description={t('telemetry_description')}
>
<Switch
checked={fullTelemetry}
onClick={() => (telemetryState.shareFullTelemetry = !fullTelemetry)}
size="md"
/>
<Select
value={telemetryLevelPreference}
onChange={(newValue) => {
// add "dateFormat" key to localStorage and set it as default date format
telemetryState.telemetryLevelPreference = newValue;
}}
containerClassName="flex h-[30px] gap-2"
>
{telemetryPreferenceOptions.map((format, index) => (
<SelectOption key={index} value={format.value}>
{format.label}
</SelectOption>
))}
</Select>
</Setting>
</>
);

View File

@@ -38,16 +38,21 @@ export const useContextValue = () => {
};
export const shareTelemetry = RadioGroupField.options([
z.literal('share-telemetry'),
z.literal('minimal-telemetry')
z.literal('full'),
z.literal('minimal'),
z.literal('none')
]).details({
'share-telemetry': {
heading: i18n.t('share_anonymous_usage'),
description: i18n.t('share_anonymous_usage_description')
full: {
heading: i18n.t('telemetry_share_anonymous'),
description: i18n.t('telemetry_share_anonymous_description')
},
'minimal-telemetry': {
heading: i18n.t('share_bare_minimum'),
description: i18n.t('share_bare_minimum_description')
minimal: {
heading: i18n.t('telemetry_share_minimal'),
description: i18n.t('telemetry_share_minimal_description')
},
none: {
heading: i18n.t('telemetry_share_none'),
description: i18n.t('telemetry_share_none_description')
}
});
@@ -104,7 +109,7 @@ const useFormState = () => {
// opted to place this here as users could change their mind before library creation/onboarding finalization
// it feels more fitting to configure it here (once)
telemetryState.shareFullTelemetry = data.privacy.shareTelemetry === 'share-telemetry';
telemetryState.telemetryLevelPreference = data.privacy.shareTelemetry;
try {
// show creation screen for a bit for smoothness
@@ -119,7 +124,7 @@ const useFormState = () => {
if (platform.refreshMenuBar) platform.refreshMenuBar();
if (telemetryState.shareFullTelemetry) {
if (telemetryState.telemetryLevelPreference === 'full') {
submitPlausibleEvent({ event: { type: 'libraryCreate' } });
}

View File

@@ -113,8 +113,8 @@ body {
.explorer-scroll {
&::-webkit-scrollbar {
height: 6px;
width: 6px;
height: var(--scrollbar-height);
width: var(--scrollbar-width);
}
&::-webkit-scrollbar-track {
@apply rounded-[6px] bg-transparent;
@@ -129,13 +129,13 @@ body {
.default-scroll {
&::-webkit-scrollbar {
height: 6px;
width: 8px;
width: 6px;
}
&::-webkit-scrollbar-track {
@apply rounded-[6px] bg-transparent;
}
&::-webkit-scrollbar-thumb {
@apply rounded-[6px] bg-app-box;
@apply rounded-[6px] bg-app-explorerScrollbar;
}
}
.page-scroll {

View File

@@ -629,10 +629,6 @@
"settings": "الإعدادات",
"setup": "إعداد",
"share": "مشاركة",
"share_anonymous_usage": "مشاركة الاستخدام المجهول",
"share_anonymous_usage_description": "مشاركة بيانات التلميح المجهولة تمامًا لمساعدة المطورين في تحسين التطبيق",
"share_bare_minimum": "مشاركة الحد الأدنى",
"share_bare_minimum_description": "مشاركة أنني مستخدم نشط لـ Spacedrive وبعض البيانات الفنية فقط",
"sharing": "المشاركة",
"sharing_description": "إدارة من لديه وصول إلى مكتباتك.",
"show_details": "إظهار التفاصيل",
@@ -705,6 +701,15 @@
"task_two": "tasks",
"task_zero": "task",
"telemetry_description": "قم بتبديل التشغيل لتزويد المطورين ببيانات مفصلة حول الاستخدام وبيانات التلميح لتحسين التطبيق. قم بتبديل التشغيل لإرسال بيانات أساسية فقط: حالة نشاطك ، إصدار التطبيق ، إصدار النواة ، والمنصة (مثل الجوال أو الويب أو سطح المكتب).",
"telemetry_share_anonymous": "مشاركة الاستخدام المجهول",
"telemetry_share_anonymous_description": "مشاركة بيانات التلميح المجهولة تمامًا لمساعدة المطورين في تحسين التطبيق",
"telemetry_share_anonymous_short": "استخدام مجهول",
"telemetry_share_minimal": "مشاركة الحد الأدنى",
"telemetry_share_minimal_description": "مشاركة أنني مستخدم نشط لـ Spacedrive وبعض البيانات الفنية فقط",
"telemetry_share_minimal_short": "الحد الأدنى",
"telemetry_share_none": "لا تشارك",
"telemetry_share_none_description": "لا يرسل أي بيانات تحليلية على الإطلاق من تطبيق Spacedrive.",
"telemetry_share_none_short": "لا أحد",
"telemetry_title": "مشاركة بيانات التلميح والاستخدام الإضافية",
"temperature": "درجة الحرارة",
"text": "Text",

View File

@@ -685,10 +685,6 @@
"settings": "Налады",
"setup": "Наладзіць",
"share": "Падзяліцца",
"share_anonymous_usage": "Падзяляцца ананімнымі дадзенымі пра выкарыстанне",
"share_anonymous_usage_description": "Падзяляйцеся цалкам ананімнымі тэлеметрычнымі дадзенымі, каб дапамагчы распрацоўнікам палепшыць дадатак",
"share_bare_minimum": "Падзяляцца толькі патрэбным",
"share_bare_minimum_description": "Падзяляцца толькі тым, што з'яўляецеся актыўным карыстачом Spacedrive і некалькімі тэхнічнымі момантамі.",
"sharing": "Супольнае выкарыстанне",
"sharing_description": "Кіруйце тым, хто мае доступ да вашых бібліятэк.",
"show_details": "Паказаць падрабязнасці",
@@ -762,6 +758,15 @@
"task_many": "задач",
"task_one": "задача",
"telemetry_description": "Уключыце, каб падаць распрацоўнікам дэталёвыя дадзеныя пра выкарыстанне і тэлеметрыю для паляпшэння дадатку. Выключыце, каб адпраўляць толькі асноўныя дадзеныя: статус актыўнасці, версію дадатку, версію ядра і платформу (прыкладам, мабільную, ўэб- ці настольную).",
"telemetry_share_anonymous": "Падзяляцца ананімнымі дадзенымі пра выкарыстанне",
"telemetry_share_anonymous_description": "Падзяляйцеся цалкам ананімнымі тэлеметрычнымі дадзенымі, каб дапамагчы распрацоўнікам палепшыць дадатак",
"telemetry_share_anonymous_short": "Ананімнае выкарыстанне",
"telemetry_share_minimal": "Падзяляцца толькі патрэбным",
"telemetry_share_minimal_description": "Падзяляцца толькі тым, што з'яўляецеся актыўным карыстачом Spacedrive і некалькімі тэхнічнымі момантамі.",
"telemetry_share_minimal_short": "Мінімальны",
"telemetry_share_none": "Не дзяліцеся",
"telemetry_share_none_description": "Не адпраўляе абсалютна ніякіх аналітычных даных з праграмы Spacedrive.",
"telemetry_share_none_short": "Няма",
"telemetry_title": "Падаванне дадатковай тэлеметрыi і дадзеных пра выкарыстанне",
"temperature": "Тэмпература",
"text": "Тэкст",

View File

@@ -658,10 +658,6 @@
"settings": "Nastavení",
"setup": "Nastavit",
"share": "Sdílet",
"share_anonymous_usage": "Sdílet anonymní používání",
"share_anonymous_usage_description": "Sdílejte zcela anonymní telemetrická data, která pomáhají vývojářům zlepšovat aplikaci",
"share_bare_minimum": "Sdílet pouze minimum",
"share_bare_minimum_description": "Sdílet pouze to, že jsem aktivním uživatelem Spacedrive a několik technických detailů",
"sharing": "Sdílení",
"sharing_description": "Spravujte, kdo má přístup k vašim knihovnám.",
"show_details": "Zobrazit podrobnosti",
@@ -733,6 +729,15 @@
"task_one": "úkol",
"task_other": "úkoly",
"telemetry_description": "Přepněte na ON, aby vývojáři mohli získat podrobné údaje o používání a telemetrii pro vylepšení aplikace. Přepněte na OFF, abyste poslali pouze základní údaje: váš stav aktivity, verzi aplikace, verzi jádra a platformu (např. mobilní, webová nebo desktopová).",
"telemetry_share_anonymous": "Sdílet anonymní používání",
"telemetry_share_anonymous_description": "Sdílejte zcela anonymní telemetrická data, která pomáhají vývojářům zlepšovat aplikaci",
"telemetry_share_anonymous_short": "Anonymní použití",
"telemetry_share_minimal": "Sdílet pouze minimum",
"telemetry_share_minimal_description": "Sdílet pouze to, že jsem aktivním uživatelem Spacedrive a několik technických detailů",
"telemetry_share_minimal_short": "Minimální",
"telemetry_share_none": "Nesdílejte",
"telemetry_share_none_description": "Neposílá absolutně žádná analytická data z aplikace Spacedrive.",
"telemetry_share_none_short": "Žádný",
"telemetry_title": "Sdílet další telemetrické a uživatelské údaje",
"temperature": "Teplota",
"text": "Text",

View File

@@ -611,10 +611,6 @@
"settings": "Einstellungen",
"setup": "Einrichten",
"share": "Teilen",
"share_anonymous_usage": "Anonyme Nutzung teilen",
"share_anonymous_usage_description": "Teile völlig anonyme Telemetriedaten, um den Entwicklern bei der Verbesserung der App zu helfen",
"share_bare_minimum": "Mindestinformationen teilen",
"share_bare_minimum_description": "Nur teilen, dass ich ein aktiver Benutzer von Spacedrive bin und einige technische Details",
"sharing": "Teilen",
"sharing_description": "Verwalte, wer Zugriff auf Deine Bibliotheken hat.",
"show_details": "Details anzeigen",
@@ -679,6 +675,15 @@
"task_one": "task",
"task_other": "tasks",
"telemetry_description": "Schalte EIN, um den Entwicklern detaillierte Nutzung- und Telemetriedaten zur Verfügung zu stellen, die die App verbessern helfen. Schalten AUS, um nur grundlegende Daten zu senden: Deinen Aktivitätsstatus, die App-Version, die Core-Version und die Plattform (z.B. Mobil, Web oder Desktop).",
"telemetry_share_anonymous": "Anonyme Nutzung teilen",
"telemetry_share_anonymous_description": "Teile völlig anonyme Telemetriedaten, um den Entwicklern bei der Verbesserung der App zu helfen",
"telemetry_share_anonymous_short": "Anonyme Nutzung",
"telemetry_share_minimal": "Mindestinformationen teilen",
"telemetry_share_minimal_description": "Nur teilen, dass ich ein aktiver Benutzer von Spacedrive bin und einige technische Details",
"telemetry_share_minimal_short": "Minimal",
"telemetry_share_none": "Nicht teilen",
"telemetry_share_none_description": "Sendet absolut keine Analysedaten von der Spacedrive-App.",
"telemetry_share_none_short": "Keiner",
"telemetry_title": "Zusätzliche Telemetrie- und Nutzungsdaten teilen",
"temperature": "Temperatur",
"text": "Text",

View File

@@ -659,10 +659,6 @@
"settings": "Settings",
"setup": "Set up",
"share": "Share",
"share_anonymous_usage": "Share anonymous usage",
"share_anonymous_usage_description": "Share completely anonymous telemetry data to help the developers improve the app",
"share_bare_minimum": "Share the bare minimum",
"share_bare_minimum_description": "Only share that I am an active user of Spacedrive and a few technical bits",
"sharing": "Sharing",
"sharing_description": "Manage who has access to your libraries.",
"show_details": "Show details",
@@ -733,8 +729,17 @@
"task": "task",
"task_one": "task",
"task_other": "tasks",
"telemetry_description": "Toggle ON to provide developers with detailed usage and telemetry data to enhance the app. Toggle OFF to send only basic data: your activity status, app version, core version, and platform (e.g., mobile, web, or desktop).",
"telemetry_title": "Share Additional Telemetry and Usage Data",
"telemetry_description": "You can choose to provide Spacedrive's developers with anonymous telemetry data to enhance the app. “None” sends no analytics data whatsoever. “Minimal” sends basic usage & version stats. “Anonymous usage” sends strictly anonymized usage details that cannot be linked to you.",
"telemetry_share_anonymous": "Share anonymous usage",
"telemetry_share_anonymous_description": "Shares completely anonymous telemetry data to help our team improve the app.",
"telemetry_share_anonymous_short": "Anonymous usage",
"telemetry_share_minimal": "Share the bare minimum",
"telemetry_share_minimal_description": "Only shares anonymously that Spacedrive is active and a few technical bits.",
"telemetry_share_minimal_short": "Minimal",
"telemetry_share_none": "Don't share anything",
"telemetry_share_none_description": "Sends absolutely no analytics data from the Spacedrive app.",
"telemetry_share_none_short": "None",
"telemetry_title": "Telemetry & usage data sharing",
"temperature": "Temperature",
"text": "Text",
"text_file": "Text File",

View File

@@ -613,10 +613,6 @@
"settings": "Configuraciones",
"setup": "Configurar",
"share": "Compartir",
"share_anonymous_usage": "Compartir uso anónimo",
"share_anonymous_usage_description": "Compartir datos de telemetría completamente anónimos para ayudar a los desarrolladores a mejorar la aplicación",
"share_bare_minimum": "Compartir lo mínimo indispensable",
"share_bare_minimum_description": "Solo compartir que soy un usuario activo de Spacedrive y algunos detalles técnicos",
"sharing": "Compartiendo",
"sharing_description": "Administra quién tiene acceso a tus bibliotecas.",
"show_details": "Mostrar detalles",
@@ -681,6 +677,15 @@
"task_one": "task",
"task_other": "tasks",
"telemetry_description": "Activa para proporcionar a los desarrolladores datos detallados de uso y telemetría para mejorar la aplicación. Desactiva para enviar solo datos básicos: tu estado de actividad, versión de la aplicación, versión central y plataforma (por ejemplo, móvil, web o escritorio).",
"telemetry_share_anonymous": "Compartir uso anónimo",
"telemetry_share_anonymous_description": "Compartir datos de telemetría completamente anónimos para ayudar a los desarrolladores a mejorar la aplicación",
"telemetry_share_anonymous_short": "Uso anónimo",
"telemetry_share_minimal": "Compartir lo mínimo indispensable",
"telemetry_share_minimal_description": "Solo compartir que soy un usuario activo de Spacedrive y algunos detalles técnicos",
"telemetry_share_minimal_short": "Mínimo",
"telemetry_share_none": "No compartir",
"telemetry_share_none_description": "No envía absolutamente ningún dato analítico desde la aplicación Spacedrive.",
"telemetry_share_none_short": "Ninguno",
"telemetry_title": "Compartir datos adicionales de telemetría y uso",
"temperature": "Temperatura",
"text": "Text",

View File

@@ -613,10 +613,6 @@
"settings": "Paramètres",
"setup": "Configuration",
"share": "Partager",
"share_anonymous_usage": "Partager l'utilisation anonyme",
"share_anonymous_usage_description": "Partager des données de télémétrie complètement anonymes pour aider les développeurs à améliorer l'application",
"share_bare_minimum": "Partager le strict minimum",
"share_bare_minimum_description": "Partager uniquement que je suis un utilisateur actif de Spacedrive et quelques détails techniques",
"sharing": "Partage",
"sharing_description": "Gérer qui a accès à vos bibliothèques.",
"show_details": "Afficher les détails",
@@ -680,6 +676,15 @@
"task_one": "task",
"task_other": "tasks",
"telemetry_description": "Activez pour fournir aux développeurs des données détaillées d'utilisation et de télémesure afin d'améliorer l'application. Désactivez pour n'envoyer que les données de base : votre statut d'activité, la version de l'application, la version du noyau et la plateforme (par exemple, mobile, web ou ordinateur de bureau).",
"telemetry_share_anonymous": "Partager l'utilisation anonyme",
"telemetry_share_anonymous_description": "Partager des données de télémétrie complètement anonymes pour aider les développeurs à améliorer l'application",
"telemetry_share_anonymous_short": "Utilisation anonyme",
"telemetry_share_minimal": "Partager le strict minimum",
"telemetry_share_minimal_description": "Partager uniquement que je suis un utilisateur actif de Spacedrive et quelques détails techniques",
"telemetry_share_minimal_short": "Minimal",
"telemetry_share_none": "Ne partagez pas",
"telemetry_share_none_description": "N'envoie absolument aucune donnée analytique depuis l'application Spacedrive.",
"telemetry_share_none_short": "Aucun",
"telemetry_title": "Partager des données de télémesure et d'utilisation supplémentaires",
"temperature": "Température",
"text": "Text",

View File

@@ -613,10 +613,6 @@
"settings": "Impostazioni",
"setup": "Imposta",
"share": "Condividi",
"share_anonymous_usage": "Condividi l'utilizzo in modo anonimo",
"share_anonymous_usage_description": "Condividi dati di telemetria in maniera completamente anonima per aiutare gli sviluppatori a migliorare l'app",
"share_bare_minimum": "Condividi il minimo necessario",
"share_bare_minimum_description": "Condividi solo che sono un utente attivo di Spacedrive e altri dettagli tecnici",
"sharing": "Condivisione",
"sharing_description": "Gestisci chi ha accesso alle tue librerie.",
"show_details": "Mostra dettagli",
@@ -680,6 +676,15 @@
"task_one": "task",
"task_other": "tasks",
"telemetry_description": "Attiva per fornire agli sviluppatori dati dettagliati sull'utilizzo e sulla telemetria per migliorare l'app. Disattiva per inviare solo i dati di base: stato della tua attività, versione dell'app, versione principale e piattaforma (ad esempio mobile, web o desktop).",
"telemetry_share_anonymous": "Condividi l'utilizzo in modo anonimo",
"telemetry_share_anonymous_description": "Condividi dati di telemetria in maniera completamente anonima per aiutare gli sviluppatori a migliorare l'app",
"telemetry_share_anonymous_short": "Utilizzo anonimo",
"telemetry_share_minimal": "Condividi il minimo necessario",
"telemetry_share_minimal_description": "Condividi solo che sono un utente attivo di Spacedrive e altri dettagli tecnici",
"telemetry_share_minimal_short": "Minimo",
"telemetry_share_none": "Non condividere",
"telemetry_share_none_description": "Non invia assolutamente dati analitici dall'app Spacedrive.",
"telemetry_share_none_short": "Nessuno",
"telemetry_title": "Condividi ulteriori dati di telemetria e utilizzo",
"temperature": "Temperatura",
"text": "Text",

View File

@@ -604,10 +604,6 @@
"settings": "設定",
"setup": "セットアップ",
"share": "共有",
"share_anonymous_usage": "利用状況を送信する",
"share_anonymous_usage_description": "アプリの改善のために、完全に匿名のテレメトリデータを送信します",
"share_bare_minimum": "最小限のデータのみを送信する",
"share_bare_minimum_description": "自分がSpacedriveのアクティブユーザーであることと、多少の技術的データのみを送信します",
"sharing": "シェアリング",
"sharing_description": "ライブラリへのアクセス権を管理できます。",
"show_details": "詳細を表示",
@@ -670,6 +666,15 @@
"task": "task",
"task_other": "tasks",
"telemetry_description": "有効にすると、アプリを改善するための詳細なテレメトリ・利用状況データが開発者に提供されます。無効にすると、基本的なデータ(実行状況、アプリバージョン、コアバージョン、プラットフォーム[モバイル/ウェブ/デスクトップなど])のみが送信されます。",
"telemetry_share_anonymous": "利用状況を送信する",
"telemetry_share_anonymous_description": "アプリの改善のために、完全に匿名のテレメトリデータを送信します",
"telemetry_share_anonymous_short": "匿名での使用",
"telemetry_share_minimal": "最小限のデータのみを送信する",
"telemetry_share_minimal_description": "自分がSpacedriveのアクティブユーザーであることと、多少の技術的データのみを送信します",
"telemetry_share_minimal_short": "最小限",
"telemetry_share_none": "共有しないでください",
"telemetry_share_none_description": "Spacedrive アプリから分析データをまったく送信しません。",
"telemetry_share_none_short": "なし",
"telemetry_title": "テレメトリ・利用状況データを送信する",
"temperature": "温度",
"text": "テキスト",

View File

@@ -611,10 +611,6 @@
"settings": "Instellingen",
"setup": "Instellen",
"share": "Delen",
"share_anonymous_usage": "Deel anonieme gebruiksgegevens",
"share_anonymous_usage_description": "Deel volledig anonieme telemetrie gegevens om de ontwikkelaars te helpen de app te verbeteren",
"share_bare_minimum": "Deel het absolute minimale",
"share_bare_minimum_description": "Deel alleen dat ik een actieve gebruiker ben van Spacedrive en een paar technische details",
"sharing": "Delen",
"sharing_description": "Beheer wie toegang heeft tot je bibliotheken.",
"show_details": "Toon details",
@@ -679,6 +675,15 @@
"task_one": "task",
"task_other": "tasks",
"telemetry_description": "Schakel in om de ontwikkelaars te voorzien van gedetailleerde gebruik en telemetrie gegevens om de app te verbeteren. Schakel uit om alleen basisgegevens te verzenden: je status, app-versie, core versie en platform (bijvoorbeeld, mobiel, browser, of desktop).",
"telemetry_share_anonymous": "Deel anonieme gebruiksgegevens",
"telemetry_share_anonymous_description": "Deel volledig anonieme telemetrie gegevens om de ontwikkelaars te helpen de app te verbeteren",
"telemetry_share_anonymous_short": "Anoniem gebruik",
"telemetry_share_minimal": "Deel het absolute minimale",
"telemetry_share_minimal_description": "Deel alleen dat ik een actieve gebruiker ben van Spacedrive en een paar technische details",
"telemetry_share_minimal_short": "Minimaal",
"telemetry_share_none": "Deel niet",
"telemetry_share_none_description": "Verzendt absoluut geen analysegegevens vanuit de Spacedrive-app.",
"telemetry_share_none_short": "Geen",
"telemetry_title": "Deel Aanvullende Telemetrie en Gebruiksgegevens",
"temperature": "Temperatuur",
"text": "Text",

View File

@@ -685,10 +685,6 @@
"settings": "Настройки",
"setup": "Настроить",
"share": "Поделиться",
"share_anonymous_usage": "Делиться анонимными данными об использовании",
"share_anonymous_usage_description": "Делитесь полностью анонимными телеметрическими данными, чтобы помочь разработчикам улучшить приложение",
"share_bare_minimum": "Делиться лишь необходимым",
"share_bare_minimum_description": "Делиться лишь тем, что являетесь активным пользователем Spacedrive и несколькими техническими моментами.",
"sharing": "Совместное использование",
"sharing_description": "Управляйте тем, кто имеет доступ к вашим библиотекам.",
"show_details": "Показать подробности",
@@ -762,6 +758,15 @@
"task_many": "задач",
"task_one": "задача",
"telemetry_description": "Включите, чтобы предоставить разработчикам подробные данные об использовании и телеметрии для улучшения приложения. Выключите, чтобы отправлять только основные данные: статус активности, версию приложения, версию ядра и платформу (например, мобильную, веб- или настольную).",
"telemetry_share_anonymous": "Делиться анонимными данными об использовании",
"telemetry_share_anonymous_description": "Делитесь полностью анонимными телеметрическими данными, чтобы помочь разработчикам улучшить приложение",
"telemetry_share_anonymous_short": "Анонимное использование",
"telemetry_share_minimal": "Делиться лишь необходимым",
"telemetry_share_minimal_description": "Делиться лишь тем, что являетесь активным пользователем Spacedrive и несколькими техническими моментами.",
"telemetry_share_minimal_short": "Минимальный",
"telemetry_share_none": "Не делитесь",
"telemetry_share_none_description": "Не отправляет абсолютно никаких аналитических данных из приложения Spacedrive.",
"telemetry_share_none_short": "Никто",
"telemetry_title": "Предоставление дополнительной телеметрии и данных об использовании",
"temperature": "Температура",
"text": "Текст",

View File

@@ -611,10 +611,6 @@
"settings": "Ayarlar",
"setup": "Kurulum",
"share": "Paylaş",
"share_anonymous_usage": "Anonim kullanımı paylaş",
"share_anonymous_usage_description": "Geliştiricilere uygulamayı iyileştirmelerine yardımcı olmak için tamamen anonim telemetri verilerini paylaşın",
"share_bare_minimum": "Sadece en azını paylaş",
"share_bare_minimum_description": "Yalnızca Spacedrive'ın aktif bir kullanıcısı olduğumu ve birkaç teknik ayrıntıyı paylaşın",
"sharing": "Paylaşım",
"sharing_description": "Kütüphanelerinize kimlerin erişim sağlayabileceğini yönetin.",
"show_details": "Detayları Göster",
@@ -679,6 +675,15 @@
"task_one": "task",
"task_other": "tasks",
"telemetry_description": "Uygulamayı iyileştirmek için geliştiricilere detaylı kullanım ve telemetri verisi sağlamak için AÇIK konumuna getirin. Yalnızca temel verileri göndermek için KAPALI konumuna getirin: faaliyet durumunuz, uygulama sürümü, çekirdek sürümü ve platform (örn., mobil, web veya masaüstü).",
"telemetry_share_anonymous": "Anonim kullanımı paylaş",
"telemetry_share_anonymous_description": "Geliştiricilere uygulamayı iyileştirmelerine yardımcı olmak için tamamen anonim telemetri verilerini paylaşın",
"telemetry_share_anonymous_short": "Anonim kullanım",
"telemetry_share_minimal": "Sadece en azını paylaş",
"telemetry_share_minimal_description": "Yalnızca Spacedrive'ın aktif bir kullanıcısı olduğumu ve birkaç teknik ayrıntıyı paylaşın",
"telemetry_share_minimal_short": "Asgari",
"telemetry_share_none": "Paylaşma",
"telemetry_share_none_description": "Spacedrive uygulamasından kesinlikle hiçbir analiz verisi göndermez.",
"telemetry_share_none_short": "Hiçbiri",
"telemetry_title": "Ek Telemetri ve Kullanım Verisi Paylaş",
"temperature": "Sıcaklık",
"text": "Text",

View File

@@ -604,10 +604,6 @@
"settings": "设置",
"setup": "设置",
"share": "分享",
"share_anonymous_usage": "分享匿名使用情况",
"share_anonymous_usage_description": "分享完全匿名的遥测数据,帮助开发者改进应用程序",
"share_bare_minimum": "分享最基本信息",
"share_bare_minimum_description": "只分享我是 Spacedrive 的活跃用户和一些技术细节",
"sharing": "共享",
"sharing_description": "管理有权访问您的库的人。",
"show_details": "显示详情",
@@ -671,6 +667,15 @@
"task": "任务",
"task_other": "任务",
"telemetry_description": "启用以向开发者提供详细的使用情况和遥测数据来改善应用程序。禁用则将只发送基本数据您的活动状态、应用版本、应用内核版本以及平台例如移动端、web 端或桌面端)。",
"telemetry_share_anonymous": "分享匿名使用情况",
"telemetry_share_anonymous_description": "分享完全匿名的遥测数据,帮助开发者改进应用程序",
"telemetry_share_anonymous_short": "匿名使用",
"telemetry_share_minimal": "分享最基本信息",
"telemetry_share_minimal_description": "只分享我是 Spacedrive 的活跃用户和一些技术细节",
"telemetry_share_minimal_short": "最小",
"telemetry_share_none": "请勿分享",
"telemetry_share_none_description": "绝对不从 Spacedrive 应用程序发送任何分析数据。",
"telemetry_share_none_short": "没有任何",
"telemetry_title": "共享额外的遥测和使用数据",
"temperature": "温度",
"text": "文本",

View File

@@ -604,10 +604,6 @@
"settings": "設置",
"setup": "設定",
"share": "分享",
"share_anonymous_usage": "分享匿名使用情況",
"share_anonymous_usage_description": "分享完全匿名的遙測數據,以幫助開發人員改進應用程序",
"share_bare_minimum": "分享最低限度",
"share_bare_minimum_description": "僅分享我是Spacedrive的活躍用戶和一些技術細節",
"sharing": "共享",
"sharing_description": "管理誰可以訪問您的圖書館。",
"show_details": "顯示詳情",
@@ -671,6 +667,15 @@
"task_one": "task",
"task_other": "tasks",
"telemetry_description": "切換到ON為開發者提供詳細的使用情況和遙測數據以增強應用程序。切換到OFF只發送基本數據您的活動狀態應用版本核心版本以及平台例如移動網絡或桌面。",
"telemetry_share_anonymous": "分享匿名使用情況",
"telemetry_share_anonymous_description": "分享完全匿名的遙測數據,以幫助開發人員改進應用程序",
"telemetry_share_anonymous_short": "匿名使用",
"telemetry_share_minimal": "分享最低限度",
"telemetry_share_minimal_description": "僅分享我是Spacedrive的活躍用戶和一些技術細節",
"telemetry_share_minimal_short": "最小",
"telemetry_share_none": "請勿分享",
"telemetry_share_none_description": "絕對不從 Spacedrive 應用程式發送任何分析資料。",
"telemetry_share_none_short": "沒有任何",
"telemetry_title": "分享額外的遙測和使用情況數據",
"temperature": "溫度",
"text": "Text",

View File

@@ -5,17 +5,10 @@ import { BuildInfo } from '../core';
import { useDebugState } from '../stores/debugState';
import { PlausiblePlatformType, telemetryState, useTelemetryState } from '../stores/telemetryState';
/**
* This should be in sync with the Core's version.
*/
const DOMAIN = 'app.spacedrive.com';
const MOBILE_DOMAIN = 'mobile.spacedrive.com';
const PlausibleProvider = Plausible({
trackLocalhost: true,
domain: DOMAIN
});
let plausibleInstance: ReturnType<typeof Plausible>;
/**
* This defines all possible options that may be provided by events upon submission.
@@ -23,13 +16,10 @@ const PlausibleProvider = Plausible({
* This extends the standard options provided by the `plausible-tracker`
* package, but also offers some additiional options for custom functionality.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
interface PlausibleOptions extends PlausibleTrackerOptions {
/**
* This should **only** be used in contexts where telemetry sharing
* must be allowed/denied via external means. Currently it is not used by anything,
* but probably will be in the future.
*/
telemetryOverride?: boolean;
// the only thing in here before was `telemetryOverride`, but we've removed it
// keeping this interface around should we need it in the future.
}
/**
@@ -145,7 +135,7 @@ interface SubmitEventProps {
* Whether or not full telemetry sharing is enabled for the current client.
*
* It is **crucial** that this is the direct output of `useTelemetryState().shareFullTelemetry`,
* regardless of other conditions that may affect whether we share it (such as event overrides).
* regardless of other conditions that may affect whether we share it.
*/
shareFullTelemetry: boolean;
/**
@@ -173,8 +163,8 @@ interface SubmitEventProps {
* If any of the following conditions are met, this will return and no data will be submitted:
*
* * If the app is in debug/development mode
* * If a telemetry override is present, but it is not true
* * If no telemetry override is present, and telemetry sharing is not true
* * If the user's telemetry preference is not "full", we will only send pings
* * If the user's telemetry preference is "none", we will never send any telemetry
*
* @privateRemarks
* Telemetry sharing settings are never matched to `=== false`, but to `!== true` instead.
@@ -186,27 +176,35 @@ interface SubmitEventProps {
*/
const submitPlausibleEvent = async ({ event, debugState, ...props }: SubmitEventProps) => {
if (props.platformType === 'unknown') return;
// if (debugState.enabled && debugState.shareFullTelemetry !== true) return;
if (
'plausibleOptions' in event && 'telemetryOverride' in event.plausibleOptions
? event.plausibleOptions.telemetryOverride !== true
: props.shareFullTelemetry !== true && event.type !== 'ping'
// if the user's telemetry preference is not "full", we should only send pings
props.shareFullTelemetry !== true &&
event.type !== 'ping'
)
return;
// using a singleton this way instead of instantiating at file eval (first time it's imported)
// because a user having "none" telemetry preference should mean Plausible never even initalizes
plausibleInstance ??= Plausible({
trackLocalhost: true,
domain: props.platformType === 'mobile' ? MOBILE_DOMAIN : DOMAIN
});
const fullEvent: PlausibleTrackerEvent = {
eventName: event.type,
props: {
platform: props.platformType,
fullTelemetry: props.shareFullTelemetry,
coreVersion: props.buildInfo?.version ?? '0.1.0', // TODO(brxken128): clean this up
commitHash: props.buildInfo?.commit ?? '0.1.0',
// we used to fall back to '0.1.0' here, but we should never report an actual version number if we don't know
coreVersion: props.buildInfo?.version ?? 'unknown',
commitHash: props.buildInfo?.commit ?? 'unknown',
debug: debugState.enabled
},
options: {
domain: props.platformType === 'mobile' ? MOBILE_DOMAIN : DOMAIN,
deviceWidth: props.screenWidth ?? window.screen.width,
referrer: '',
// by default do not track current URL, if it's provided in plausibleOptions, that will be sent
url: '',
...('plausibleOptions' in event ? event.plausibleOptions : undefined)
},
callback: debugState.telemetryLogging
@@ -217,7 +215,7 @@ const submitPlausibleEvent = async ({ event, debugState, ...props }: SubmitEvent
: undefined
};
PlausibleProvider.trackEvent(
plausibleInstance.trackEvent(
fullEvent.eventName,
{
props: fullEvent.props,
@@ -242,16 +240,10 @@ interface EventSubmissionCallbackProps {
* The returned callback should only be fired once,
* in order to prevent our analytics from being flooded.
*
* Certain events provide functionality to override the clients's telemetry sharing configuration.
* This is not to ignore the user's choice, but because it should **only** be used in contexts where
* telemetry sharing must be allowed/denied via external means.
*
* @remarks
* If any of the following conditions are met, this will return and no data will be submitted:
*
* * If the app is in debug/development mode
* * If a telemetry override is present, but it is not true
* * If no telemetry override is present, and telemetry sharing is not true
*
* @returns a callback that, once executed, will submit the desired event
*
@@ -271,19 +263,20 @@ interface EventSubmissionCallbackProps {
* });
* ```
*/
export const usePlausibleEvent = () => {
const debugState = useDebugState();
export const usePlausibleEvent = (): ((props: EventSubmissionCallbackProps) => Promise<void>) => {
const telemetryState = useTelemetryState();
const debugState = useDebugState();
const previousEvent = useRef({} as BasePlausibleEvent<string>);
return useCallback(
const sendPlausibleEvent = useCallback(
async (props: EventSubmissionCallbackProps) => {
if (previousEvent.current === props.event) return;
else previousEvent.current = props.event;
submitPlausibleEvent({
debugState,
shareFullTelemetry: telemetryState.shareFullTelemetry,
shareFullTelemetry: telemetryState.telemetryLevelPreference === 'full',
platformType: telemetryState.platform,
buildInfo: telemetryState.buildInfo,
...props
@@ -291,6 +284,10 @@ export const usePlausibleEvent = () => {
},
[debugState, telemetryState]
);
if (telemetryState.telemetryLevelPreference === 'none') return async (...args: any[]) => {};
return sendPlausibleEvent;
};
export interface PlausibleMonitorProps {
@@ -366,7 +363,11 @@ export const usePlausiblePingMonitor = ({ currentPath }: PlausibleMonitorProps)
}, [currentPath, plausibleEvent]);
};
export const initPlausible = ({
/**
* Initializes the `platform` and `buildInfo` properties on `telemetryState` so they can be used
* by Plausible if it's enabled.
*/
export const configureAnalyticsProperties = ({
platformType,
buildInfo
}: {

View File

@@ -4,6 +4,17 @@ import { type StoreNode } from 'solid-js/store';
type CreatePersistedMutableOpts<T> = {
onSave?: (value: T) => T;
/**
* This function is always called after the data object's retrieval from localStorage and it getting assigned to the store.
*
* Originally intended for mutations, but can be used for other things if you have a reason to transform the data.
*
*
* @note This is **not** called on initial load from default values.
* @param value The existing data object from localStorage (or null if doesn't exist)
* @returns The new data object
*/
onLoad?: (value: T | null) => T;
};
// `@solid-primitives/storage`'s `makePersisted` doesn't support `solid-js/store`'s `createMutable` so we roll our own.
@@ -12,12 +23,22 @@ export function createPersistedMutable<T extends StoreNode>(
mutable: T,
opts?: CreatePersistedMutableOpts<T>
) {
try {
parsePersistedValue: try {
const value = localStorage.getItem(key);
if (value) {
const persisted = JSON.parse(value);
Object.assign(mutable, persisted);
if (value === null) {
Object.assign(mutable, opts?.onLoad?.(value) ?? {});
break parsePersistedValue;
}
const persisted = JSON.parse(value);
Object.assign(
mutable,
// if we have a function to use to transform data on load, use its return value
opts?.onLoad?.(persisted) ??
// otherwise just use the data from localStorage as is
persisted
);
} catch (err) {
console.error(`Error loading persisted state from localStorage key '${key}': ${err}`);
}

View File

@@ -2,16 +2,37 @@ import { createMutable } from 'solid-js/store';
import { ExplorerLayout } from '../core';
import { createPersistedMutable, useSolidStore } from '../solid';
import { isTelemetryStateV0 } from './telemetryState';
export const explorerLayout = createPersistedMutable(
interface ExplorerLayoutStore {
showPathBar: boolean;
showTags: boolean;
showImageSlider: boolean;
defaultView: ExplorerLayout;
}
export const explorerLayout = createPersistedMutable<ExplorerLayoutStore>(
'sd-explorer-layout',
createMutable({
createMutable<ExplorerLayoutStore>({
showPathBar: true,
showTags: true,
showImageSlider: true,
// might move this to a store called settings
defaultView: 'grid' as ExplorerLayout
})
}),
{
onLoad(value: unknown): ExplorerLayoutStore {
// we had a bug previously where we saved the telemetry state in the wrong key
// and we need to erase it from the layout store
if (isTelemetryStateV0(value)) {
// remove telemetry state from the layout store
const { buildInfo, platform, shareFullTelemetry, ...rest } = value;
return rest as ExplorerLayoutStore;
}
return value as ExplorerLayoutStore;
}
}
);
export function useExplorerLayoutStore() {

View File

@@ -0,0 +1,102 @@
import { createMutable } from 'solid-js/store';
import { BuildInfo } from '../core';
import { createPersistedMutable, useSolidStore } from '../solid';
/**
* Possible Platform types that can be sourced from `usePlatform().platform` or even hardcoded.
*
* @remarks
* The `tauri` platform is renamed to `desktop` for analytic purposes.
*/
export type PlausiblePlatformType = 'web' | 'mobile' | 'desktop' | 'unknown';
type TelemetryStateV0 = {
shareFullTelemetry: boolean;
platform: PlausiblePlatformType;
buildInfo: BuildInfo | undefined;
};
type TelemetryStateV1 = {
telemetryLevelPreference: TelemetryLevelPreference;
platform: PlausiblePlatformType;
buildInfo: BuildInfo | undefined;
stateVersion: 1;
};
export type TelemetryLevelPreference = 'full' | 'minimal' | 'none';
export const TELEMETRY_LEVEL_PREFERENCES = [
'full',
'minimal',
'none'
] satisfies TelemetryLevelPreference[];
const DEFAULT_TELEMETRY_STATE: TelemetryStateV1 = {
telemetryLevelPreference: 'none',
platform: 'unknown',
buildInfo: undefined,
stateVersion: 1
};
export const telemetryState = createPersistedMutable<TelemetryStateV1>(
'sd-telemetry-state',
createMutable<TelemetryStateV1>(DEFAULT_TELEMETRY_STATE),
{
onLoad(value: unknown): TelemetryStateV1 {
if (value === null) {
// try to migrate from sd-explorer-layout key, was a bug for a while
const oldData = localStorage.getItem('sd-explorer-layout');
if (oldData === null) return DEFAULT_TELEMETRY_STATE;
// assign the old data to the current working value, and will fall through
// to the v0/v1 migration logic
value = JSON.parse(oldData);
}
if (isTelemetryStateV1(value)) {
return value;
}
if (isTelemetryStateV0(value)) {
return migrateV0ToV1(value);
}
// If the value is neither v0 nor v1, return the default state
return DEFAULT_TELEMETRY_STATE;
}
}
);
// exported because we use it in explorerLayout, there was a bug where we saved this into
// the wrong key (sd-explorer-layout) and we need to erase it from the layout store
export function isTelemetryStateV0(value: unknown): value is TelemetryStateV0 {
return (
typeof value === 'object' &&
value !== null &&
'shareFullTelemetry' in value &&
'platform' in value &&
'buildInfo' in value
);
}
function isTelemetryStateV1(value: unknown): value is TelemetryStateV1 {
return (
typeof value === 'object' &&
value !== null &&
'telemetryLevelPreference' in value &&
'platform' in value &&
'buildInfo' in value &&
'stateVersion' in value &&
value.stateVersion === 1
);
}
function migrateV0ToV1(value: TelemetryStateV0): TelemetryStateV1 {
return {
...DEFAULT_TELEMETRY_STATE,
telemetryLevelPreference: value.shareFullTelemetry ? 'full' : 'minimal'
};
}
export function useTelemetryState() {
return useSolidStore(telemetryState);
}

View File

@@ -1,31 +0,0 @@
import { createMutable } from 'solid-js/store';
import { BuildInfo } from '../core';
import { createPersistedMutable, useSolidStore } from '../solid';
/**
* Possible Platform types that can be sourced from `usePlatform().platform` or even hardcoded.
*
* @remarks
* The `tauri` platform is renamed to `desktop` for analytic purposes.
*/
export type PlausiblePlatformType = 'web' | 'mobile' | 'desktop' | 'unknown';
type TelemetryState = {
shareFullTelemetry: boolean;
platform: PlausiblePlatformType;
buildInfo: BuildInfo | undefined;
};
export const telemetryState = createPersistedMutable(
'sd-explorer-layout',
createMutable<TelemetryState>({
shareFullTelemetry: false, // false by default
platform: 'unknown',
buildInfo: undefined
})
);
export function useTelemetryState() {
return useSolidStore(telemetryState);
}