feat: optional telemetry (#3131)

This commit is contained in:
pochoclin
2025-10-02 15:50:54 -04:00
committed by GitHub
parent 5e575c74a6
commit 108e30a11c
51 changed files with 1087 additions and 191 deletions

8
Cargo.lock generated
View File

@@ -3731,6 +3731,13 @@ dependencies = [
[[package]]
name = "popcorntime-settings"
version = "0.0.1"
dependencies = [
"anyhow",
"serde",
"specta",
"tokio",
"toml 0.9.7",
]
[[package]]
name = "popcorntime-tauri"
@@ -3744,6 +3751,7 @@ dependencies = [
"popcorntime-error",
"popcorntime-graphql-client",
"popcorntime-session",
"popcorntime-settings",
"popcorntime-tauri-splash",
"serde",
"serde_json",

View File

@@ -25,6 +25,7 @@
"i18next-resources-to-backend": "catalog:",
"immer": "catalog:",
"lucide-react": "catalog:",
"posthog-js": "^1.266.0",
"react": "catalog:",
"react-dom": "catalog:",
"react-fast-compare": "catalog:",

View File

@@ -9,13 +9,11 @@ import {
} from "@popcorntime/ui/components/menubar";
import { Tooltip, TooltipContent, TooltipTrigger } from "@popcorntime/ui/components/tooltip";
import { cn } from "@popcorntime/ui/lib/utils";
import { appLogDir } from "@tauri-apps/api/path";
import { openPath } from "@tauri-apps/plugin-opener";
import {
Bug,
CircleUserIcon,
Clapperboard,
FileText,
Cog,
Film,
Globe,
Headphones,
@@ -36,6 +34,7 @@ export function Header() {
const preferFavorites = useGlobalStore(state => state.browse.preferFavorites);
const togglePreferFavorites = useGlobalStore(state => state.togglePreferFavorites);
const togglePreferences = useGlobalStore(state => state.togglePreferences);
const toggleSettings = useGlobalStore(state => state.toggleSettings);
const toggleWatchPreferences = useGlobalStore(state => state.toggleWatchPreferences);
const sessionCleared = useGlobalStore(state => state.sessionCleared);
const direction = useGlobalStore(state => state.i18n.direction);
@@ -47,11 +46,6 @@ export function Header() {
return (searchParams.get("kind") || "MOVIE") as MediaKind;
}, [searchParams]);
const openLogsDir = useCallback(async () => {
const appLogDirPath = await appLogDir();
openPath(appLogDirPath);
}, []);
const logout = useCallback(() => {
api.logout().then(sessionCleared);
}, [api.logout, sessionCleared]);
@@ -153,16 +147,10 @@ export function Header() {
<Clapperboard className="size-4 shrink-0" />
<span>{t("menu.watchPreferences")}</span>
</MenubarItem>
<MenubarItem asChild>
<Link to="/onboarding/manifest?next=/" className="flex gap-2">
<FileText className="size-4 shrink-0" />
<span>{t("menu.manifest")}</span>
</Link>
</MenubarItem>
<MenubarSeparator />
<MenubarItem onClick={openLogsDir} className="flex gap-2">
<Bug className="size-4 shrink-0" />
<span>{t("menu.logs")}</span>
<MenubarItem onClick={toggleSettings} className="flex gap-2">
<Cog className="size-4 shrink-0" />
<span>{t("menu.settings")}</span>
</MenubarItem>
<MenubarItem onClick={logout} className="flex gap-2">
<LogOut className="size-4 shrink-0" />

View File

@@ -16,7 +16,6 @@ import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router";
import { toast } from "sonner";
import { z } from "zod";
import { useShallow } from "zustand/shallow";
import { CountryPopover } from "@/components/popover/country";
import { LanguagePopover } from "@/components/popover/language";
import { useCountry } from "@/hooks/useCountry";
@@ -35,8 +34,7 @@ export function PreferencesDialog() {
const shouldOpen = useGlobalStore(state => state.dialogs.preferences.isOpen);
const togglePreferences = useGlobalStore(state => state.togglePreferences);
const preferences = useGlobalStore(useShallow(state => state.preferences));
const sessionStatus = useGlobalStore(state => state.session.status);
const preferences = useGlobalStore(state => state.preferences);
const [submitted, setSubmitted] = useState(false);
const { api } = useTauri();
const { t } = useTranslation();
@@ -47,8 +45,8 @@ export function PreferencesDialog() {
const form = useForm<AccountFormValues>({
resolver: zodResolver(accountFormSchema),
defaultValues: {
country: i18n.defaultCountry,
language: i18n.defaultLocale,
country: preferences.country ?? i18n.defaultCountry,
language: preferences.language ?? i18n.defaultLocale,
},
});
@@ -61,17 +59,6 @@ export function PreferencesDialog() {
};
}, [shouldOpen, hide]);
useEffect(() => {
if (sessionStatus === "ready") {
if (preferences.country) {
form.setValue("country", preferences.country);
}
if (preferences.language) {
form.setValue("language", preferences.language);
}
}
}, [form, preferences, sessionStatus]);
const onSubmit = useCallback(
(values: AccountFormValues) => {
if (submitted) {
@@ -106,7 +93,7 @@ export function PreferencesDialog() {
[submitted, api.updateUserPreferences, t, country, navigate, togglePreferences]
);
if (!sessionStatus || !shouldOpen) {
if (preferences.status !== "ready" || !shouldOpen) {
return null;
}

View File

@@ -0,0 +1,144 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@popcorntime/ui/components/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@popcorntime/ui/components/dialog";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
} from "@popcorntime/ui/components/form";
import { Switch } from "@popcorntime/ui/components/switch";
import { appLogDir } from "@tauri-apps/api/path";
import { openPath } from "@tauri-apps/plugin-opener";
import { useCallback, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import { z } from "zod";
import { useTauri } from "@/hooks/useTauri";
import { useUpdater } from "@/hooks/useUpdater";
import { useGlobalStore } from "@/stores/global";
const settingsFormSchema = z.object({
enableAnalytics: z.boolean(),
});
type SettingsFormValues = z.infer<typeof settingsFormSchema>;
export function SettingsDialog() {
const shouldOpen = useGlobalStore(state => state.dialogs.settings.isOpen);
const toggleSettings = useGlobalStore(state => state.toggleSettings);
const settings = useGlobalStore(state => state.settings);
const [submitted, setSubmitted] = useState(false);
const { api } = useTauri();
const { t } = useTranslation();
const { hide } = useUpdater();
const openLogsDir = useCallback(async () => {
const appLogDirPath = await appLogDir();
openPath(appLogDirPath);
}, []);
const form = useForm<SettingsFormValues>({
resolver: zodResolver(settingsFormSchema),
defaultValues: {
enableAnalytics: settings.enableAnalytics ?? false,
},
});
useEffect(() => {
const withUpdateAvailable = hide(shouldOpen);
return () => {
if (withUpdateAvailable) {
hide(false);
}
};
}, [shouldOpen, hide]);
const onSubmit = useCallback(
(values: SettingsFormValues) => {
if (submitted) {
return;
}
const { settingsSucceeded, settingsFailed } = useGlobalStore.getState();
setSubmitted(true);
settingsSucceeded(values);
api
.updateSettings(values)
.then(() =>
toast.success(t("settings.toast"), {
closeButton: true,
dismissible: true,
})
)
.catch(settingsFailed)
.finally(() => {
setSubmitted(false);
toggleSettings();
});
},
[submitted, api.updateSettings, t, toggleSettings]
);
if (settings.status !== "ready" || !shouldOpen) {
return null;
}
return (
<Dialog open={shouldOpen} onOpenChange={toggleSettings}>
<DialogContent className="max-w-md">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<DialogHeader>
<DialogTitle>{t("settings.settings")}</DialogTitle>
<DialogDescription>{t("settings.description")}</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<FormField
control={form.control}
name="enableAnalytics"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>{t("settings.analytics")}</FormLabel>
<FormDescription>{t("settings.analytics-description")}</FormDescription>
</div>
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
</FormItem>
)}
/>
<div className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<div>{t("settings.logs")}</div>
<p className="text-muted-foreground text-sm">{t("settings.logs-description")}</p>
</div>
<div>
<Button variant="accent" onClick={openLogsDir}>
{t("settings.open-logs")}
</Button>
</div>
</div>
</div>
<DialogFooter>
<Button type="submit">{t("settings.update")}</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View File

@@ -37,14 +37,12 @@ export function OnboardingTimeline() {
const startBrowsing = useCallback(() => {
setIsWorking(true);
api.setOnboarded().then(() => {
api.updateSettings({ onboardingComplete: true }).then(settings => {
setIsWorking(false);
settingsSucceeded({
onboarded: true,
});
settingsSucceeded(settings);
navigate("/");
});
}, [api.setOnboarded, navigate, settingsSucceeded]);
}, [api.updateSettings, navigate, settingsSucceeded]);
return (
<div className="flex min-h-svh w-full flex-col justify-center gap-12">

View File

@@ -5,13 +5,13 @@ import { Header } from "@/components/header";
import { MediaDialog } from "@/components/modal/media";
import { PreferencesDialog } from "@/components/modal/preferences";
import { WatchPreferencesDialog } from "@/components/modal/watch-preferences";
import { SettingsDialog } from "./components/modal/settings";
export function DefaultLayout() {
return (
<>
<div className="absolute top-0 left-0 isolate z-40 h-14 w-full" data-tauri-drag-region></div>
<Outlet />
<PreferencesDialog />
</>
);
}
@@ -35,6 +35,7 @@ export function BrowseLayout() {
<PreferencesDialog />
<WatchPreferencesDialog />
<MediaDialog />
<SettingsDialog />
</>
);
}

View File

@@ -12,7 +12,7 @@ afterEach(() => {
describe("SettingsLoaderMount", () => {
it("handle valid onboarding", async () => {
mockIPC((cmd, _args) => {
if (cmd === "is_onboarded") return false;
if (cmd === "settings") return { onboardingComplete: false };
});
useGlobalStore.getState().sessionSucceeded(true);
@@ -21,14 +21,14 @@ describe("SettingsLoaderMount", () => {
await act(async () => {});
expect(useGlobalStore.getState().settings.status).toBe("ready");
expect(useGlobalStore.getState().settings.onboarded).toBe(false);
expect(useGlobalStore.getState().settings.onboardingComplete).toBe(false);
r.unmount();
});
it("handle invalid onboarding flow", async () => {
mockIPC((cmd, _args) => {
if (cmd === "is_onboarded")
if (cmd === "settings")
throw { message: "Failed to get settings", code: "errors.graphql.server" };
});

View File

@@ -12,8 +12,8 @@ export function SettingsLoaderMount() {
const { settingsRequested, settingsSucceeded, settingsFailed } = useGlobalStore.getState();
settingsRequested();
api
.isOnboarded()
.then(onboarded => settingsSucceeded({ onboarded }))
.settings()
.then(settings => settingsSucceeded(settings))
.catch(settingsFailed);
}, [api, status]);

View File

@@ -0,0 +1,39 @@
import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
import { useEffect, useRef } from "react";
import { useGlobalStore } from "@/stores/global";
export function MetricsProvider({ children }: React.PropsWithChildren) {
const enableAnalytics = useGlobalStore(s => s.settings.enableAnalytics);
const initializedRef = useRef(false);
useEffect(() => {
if (initializedRef.current) return;
posthog.init(import.meta.env.VITE_PUBLIC_POSTHOG_KEY, {
api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,
capture_pageview: true,
persistence: "localStorage",
opt_out_capturing_by_default: true,
disable_session_recording: true,
loaded: ph => {
if (enableAnalytics) {
ph.opt_in_capturing();
}
},
});
initializedRef.current = true;
}, [enableAnalytics]);
useEffect(() => {
if (enableAnalytics) {
posthog.opt_in_capturing();
} else {
posthog.opt_out_capturing();
posthog.reset();
}
}, [enableAnalytics]);
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}

View File

@@ -4,25 +4,28 @@ import { TooltipProvider } from "@popcorntime/ui/components/tooltip";
import { CheckCircle } from "lucide-react";
import { CountryProvider } from "@/hooks/useCountry";
import { UpdaterProvider } from "@/hooks/useUpdater";
import { MetricsProvider } from "@/metrics";
export function Providers({ children }: React.PropsWithChildren) {
return (
<CountryProvider>
<UpdaterProvider>
<TooltipProvider>
<SidebarProvider className="h-full" defaultOpen={false}>
{children}
<Toaster
duration={2000}
expand={false}
icons={{
success: <CheckCircle className="ml-4 size-4" />,
}}
className="-z-10"
/>
</SidebarProvider>
</TooltipProvider>
</UpdaterProvider>
</CountryProvider>
<MetricsProvider>
<CountryProvider>
<UpdaterProvider>
<TooltipProvider>
<SidebarProvider className="h-full" defaultOpen={false}>
{children}
<Toaster
duration={2000}
expand={false}
icons={{
success: <CheckCircle className="ml-4 size-4" />,
}}
className="-z-10"
/>
</SidebarProvider>
</TooltipProvider>
</UpdaterProvider>
</CountryProvider>
</MetricsProvider>
);
}

View File

@@ -38,7 +38,7 @@ describe("SplashRoute", () => {
it("shows Splash while boot is not initialized", async () => {
useGlobalStore.setState(s => {
s.app.boot = "cold";
s.settings.onboarded = false;
s.settings.onboardingComplete = false;
s.session.isActive = false;
});
@@ -54,7 +54,7 @@ describe("SplashRoute", () => {
it("redirects to onboarding when not onboarded", async () => {
useGlobalStore.setState(s => {
s.app.boot = "booted";
s.settings.onboarded = false;
s.settings.onboardingComplete = false;
});
const r = renderWithRouter("/");
@@ -69,7 +69,7 @@ describe("SplashRoute", () => {
it("redirects to login when onboarded but session is not active", async () => {
useGlobalStore.setState(s => {
s.app.boot = "booted";
s.settings.onboarded = true;
s.settings.onboardingComplete = true;
});
const r = renderWithRouter("/");
@@ -84,7 +84,7 @@ describe("SplashRoute", () => {
it("shows splash when active but app not initialized", async () => {
useGlobalStore.setState(s => {
s.app.boot = "booting";
s.settings.onboarded = true;
s.settings.onboardingComplete = true;
s.session.isActive = true;
// missing providers
});
@@ -102,7 +102,7 @@ describe("SplashRoute", () => {
useGlobalStore.setState(s => {
s.app.boot = "booted";
// prevent onboarding
s.settings.onboarded = true;
s.settings.onboardingComplete = true;
// prevent login
s.session.isActive = true;
s.preferences.country = "CA";
@@ -119,7 +119,7 @@ describe("SplashRoute", () => {
it("reacts when app initialization flips", async () => {
useGlobalStore.setState(s => {
s.app.boot = "cold";
s.settings.onboarded = true;
s.settings.onboardingComplete = true;
});
const r = renderWithRouter("/");

View File

@@ -5,7 +5,7 @@ import { useGlobalStore } from "@/stores/global";
export function SplashRoute() {
const appBoot = useGlobalStore(s => s.app.boot);
const onboarded = useGlobalStore(s => s.settings.onboarded);
const onboarded = useGlobalStore(s => s.settings.onboardingComplete);
const isActive = useGlobalStore(s => s.session.isActive);
const preferredCountry = useGlobalStore(s => s.preferences.country);
const initialRedirectAttempted = useRef(false);

View File

@@ -10,7 +10,7 @@ const setAllReady = () => {
s.providersSucceeded([]);
s.sessionSucceeded(true);
s.preferencesSucceeded({ country: "US", language: "en" });
s.settingsSucceeded({ onboarded: true });
s.settingsSucceeded({ onboardingComplete: true });
};
beforeEach(() => {
@@ -55,7 +55,7 @@ describe("boot & app flags", () => {
it("sets app booting", () => {
expect(useGlobalStore.getState().app.boot).toBe("cold");
useGlobalStore.getState().sessionSucceeded(false);
useGlobalStore.getState().settingsSucceeded({ onboarded: false });
useGlobalStore.getState().settingsSucceeded({ onboardingComplete: false });
expect(useGlobalStore.getState().app.boot).toBe("booting");
});

View File

@@ -8,7 +8,7 @@ import { subscribeWithSelector } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
import { isTauriError, type TauriError } from "@/hooks/useTauri";
import { devtools } from "@/stores/devtools";
import type { Provider, SearchArguments, SortKey } from "@/tauri/types";
import type { Provider, SearchArguments, Settings, SortKey } from "@/tauri/types";
export type SortOrder = "ASC" | "DESC";
type UpdateProgress = "downloading" | "downloaded" | "installing" | "installed";
@@ -33,11 +33,10 @@ interface PreferencesState {
error?: TauriError;
}
interface SettingsState {
type SettingsState = {
status: Status;
onboarded: boolean;
error?: TauriError;
}
} & Settings;
interface ProvidersState {
status: Status;
@@ -72,9 +71,14 @@ interface DialogWatchPreferencesState {
isOpen: boolean;
}
interface DialogSettingsState {
isOpen: boolean;
}
interface DialogsState {
media: DialogMediaState;
preferences: DialogPreferencesState;
settings: DialogSettingsState;
watchPreferences: DialogWatchPreferencesState;
}
@@ -120,7 +124,7 @@ interface GlobalMutations {
preferencesFailed(err: unknown): void;
settingsRequested(): void;
settingsSucceeded(partial?: { onboarded?: boolean }): void;
settingsSucceeded(settings: Settings): void;
settingsFailed(err: unknown): void;
providersRequested(): void;
@@ -135,6 +139,7 @@ interface GlobalMutations {
closeMedia(): void;
togglePreferences(): void;
toggleWatchPreferences(): void;
toggleSettings(): void;
browseUpdate: (input: BrowseUpdateInput) => void;
togglePreferFavorites: () => void;
@@ -154,7 +159,8 @@ export const useGlobalStore = create<GlobalState & GlobalMutations>()(
},
settings: {
status: "idle",
onboarded: false,
enableAnalytics: false,
onboardingComplete: false,
},
preferences: {
status: "idle",
@@ -175,6 +181,9 @@ export const useGlobalStore = create<GlobalState & GlobalMutations>()(
media: {
isOpen: false,
},
settings: {
isOpen: false,
},
preferences: {
isOpen: false,
},
@@ -239,12 +248,12 @@ export const useGlobalStore = create<GlobalState & GlobalMutations>()(
state.settings.error = undefined;
}),
settingsSucceeded: (partial?: { onboarded?: boolean }) =>
settingsSucceeded: (settings: Settings) =>
set(state => {
state.settings.status = "ready";
state.settings = {
...state.settings,
...partial,
...settings,
};
}),
@@ -377,6 +386,11 @@ export const useGlobalStore = create<GlobalState & GlobalMutations>()(
set(state => {
state.browse.preferFavorites = !state.browse.preferFavorites;
}),
toggleSettings: () =>
set(state => {
state.dialogs.settings.isOpen = !state.dialogs.settings.isOpen;
}),
}))
),
{

View File

@@ -74,22 +74,6 @@ async setMediaReaction(params: SetReactionInput) : Promise<Result<SetReactionMut
async showMainWindow() : Promise<void> {
await TAURI_INVOKE("show_main_window");
},
async isOnboarded() : Promise<Result<boolean, { message: string; code: Code }>> {
try {
return { status: "ok", data: await TAURI_INVOKE("is_onboarded") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async setOnboarded() : Promise<Result<null, { message: string; code: Code }>> {
try {
return { status: "ok", data: await TAURI_INVOKE("set_onboarded") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async validate() : Promise<Result<null, { message: string; code: Code }>> {
try {
return { status: "ok", data: await TAURI_INVOKE("validate") };
@@ -113,6 +97,22 @@ async initializeSessionAuthorization() : Promise<Result<null, { message: string;
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async settings() : Promise<Result<Settings, { message: string; code: Code }>> {
try {
return { status: "ok", data: await TAURI_INVOKE("settings") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async updateSettings(settings: SettingsInput) : Promise<Result<Settings, { message: string; code: Code }>> {
try {
return { status: "ok", data: await TAURI_INVOKE("update_settings", { settings }) };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
}
}
@@ -174,6 +174,8 @@ export type SetFavoriteProviderInput = { country: Country; providerKey: string;
export type SetFavoriteProviderMutation = { setFavoriteProvider: boolean }
export type SetReactionInput = { mediaId: number; reaction: UserReactionType | null }
export type SetReactionMutation = { setReaction: boolean }
export type Settings = { onboardingComplete?: boolean; enableAnalytics?: boolean }
export type SettingsInput = { onboardingComplete?: boolean | null; enableAnalytics?: boolean | null }
export type SortKey = "ID" | "RELEASED_AT" | "CREATED_AT" | "UPDATED_AT" | "POSITION"
export type Tag = string
export type Tvshow = { inProduction: boolean; id: number; __typename: string; title: string; slug: string; overview: string | null; tagline: string | null; languages: Language[]; poster: string | null; backdrop: string | null; released: string | null; year: number | null; country: Country | null; tags: Tag[]; trailers: string[]; genres: Genre[]; classification: string | null; countries: Country[]; kind: MediaKind; videos: MediaVideo[]; ratings: ExternalRating[]; ranking: Ranking | null; pochoclinReview: PochoclinReview | null; similars: MediaSimilar[]; similarsFree: MediaSimilar[]; charts: MediaCharts[]; availabilities: Availability[]; talents: People[]; reaction: UserReactionType | null }

View File

@@ -19,11 +19,11 @@ serde_json.workspace = true
uuid.workspace = true
tauri.workspace = true
poem.workspace = true
toml.workspace = true
popcorntime-error.workspace = true
oauth2 = "5.0.0"
toml = "0.9.7"
keyring = { version = "3.6.3", features = [
"apple-native",
"windows-native",

View File

@@ -6,3 +6,8 @@ authors.workspace = true
publish = false
[dependencies]
toml.workspace = true
serde.workspace = true
specta.workspace = true
anyhow.workspace = true
tokio.workspace = true

View File

@@ -1 +1,78 @@
use anyhow::{Ok, Result};
use serde::{Deserialize, Serialize};
use specta::Type;
use std::{
path::{Path, PathBuf},
sync::Arc,
};
use tokio::sync::RwLock;
const SETTINGS_FILE: &str = "settings.toml";
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default, Type)]
#[serde(rename_all = "camelCase")]
pub struct Settings {
#[serde(default)]
pub onboarding_complete: bool,
#[serde(default)]
pub enable_analytics: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default, Type)]
#[serde(rename_all = "camelCase")]
pub struct SettingsInput {
#[serde(default)]
pub onboarding_complete: Option<bool>,
#[serde(default)]
pub enable_analytics: Option<bool>,
}
#[derive(Debug)]
pub struct SettingsService {
snapshot: Arc<RwLock<Settings>>,
config_dir: PathBuf,
}
fn read_snapshot(path: &Path) -> Result<Settings> {
if path.exists() {
let content = std::fs::read_to_string(path)?;
let settings: Settings = toml::from_str(&content)?;
Ok(settings)
} else {
Ok(Settings::default())
}
}
impl SettingsService {
pub fn new(config_dir: &Path) -> Result<Self> {
let snapshot = read_snapshot(&config_dir.join(SETTINGS_FILE))?;
Ok(Self {
snapshot: Arc::new(RwLock::new(snapshot)),
config_dir: config_dir.to_path_buf(),
})
}
pub async fn get(&self) -> Result<Settings> {
Ok(self.snapshot.read().await.clone())
}
pub async fn update<F>(&self, f: F) -> Result<Settings>
where
F: FnOnce(&mut Settings),
{
let mut settings = self.snapshot.write().await;
f(&mut settings);
if !self.config_dir.exists() {
std::fs::create_dir_all(&self.config_dir)?;
}
let path = self.config_dir.join(SETTINGS_FILE);
let settings_inner = settings.clone();
let content = toml::to_string_pretty(&settings_inner)?;
std::fs::write(path, content)?;
Ok(settings_inner)
}
}

View File

@@ -41,6 +41,7 @@ tauri-plugin-single-instance = { workspace = true, features = ["deep-link"] }
popcorntime-session.workspace = true
popcorntime-graphql-client.workspace = true
popcorntime-error.workspace = true
popcorntime-settings.workspace = true
specta.workspace = true
tauri-specta = { workspace = true, features = ["derive", "typescript"] }

View File

@@ -485,7 +485,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"es": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -973,7 +984,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"ar": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -1461,7 +1483,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"fa": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -1949,7 +1982,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"ur": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -2437,7 +2481,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"hi": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -2925,7 +2980,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"de": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -3413,7 +3479,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"nl": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -3901,7 +3978,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"sv": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -4389,7 +4477,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"ga": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -4877,7 +4976,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"fi": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -5365,7 +5475,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"he": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -5853,7 +5974,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"no": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -6341,7 +6473,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"da": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -6829,7 +6972,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"it": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -7317,7 +7471,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"ja": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -7805,7 +7970,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"pl": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -8293,7 +8469,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"tr": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -8781,7 +8968,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"et": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -9269,7 +9467,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"el": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -9757,7 +9966,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"pt": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -10245,7 +10465,18 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
},
"sr": {
"$['header']['popular']": "76327dec9ae3a06a8c3c283e6a81d3710ae736021b583875c13e4e8fb2ad83c0",
@@ -10733,6 +10964,17 @@
"$['onboardingProviders']['selected']": "eef30365e9cc4bd8da360e251728fa69114d8cdf133b093249ee07223b2a7abb",
"$['onboardingProviders']['noFound']": "dc3a0116c7c3d33dffde52310d11c2b039904039d5e13b4c915f621c7167ecf4",
"$['onboardingPreferences']['showAll']": "ebd10fbf3cb038b4a7355c15c166c2deef946af74a1969b55f51faf5dc9cf501",
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4"
"$['onboardingProviders']['selectCountry']": "e19b22ef6d936dbebc4cdb5ca340aa74af6b18c5b10115959c640d85a600a9d4",
"$['menu']['settings']": "d43fb8a961ff2d1f037995587ed8883ac4b733ecaf1b0b74201123ada13df810",
"$['settings']['toast']": "36ffa4529bb50637e9f0d1e3d1c209ae8246236ea4e856d95ddcf993b5db4e2c",
"$['settings']['settings']": "74a883a037bc227f91891ab654a753d3a99f31ab06ae5b5d2b6e594a692b41f8",
"$['settings']['description']": "86095b212ba6d2fc7e54c102b3de1f62e698aee75939b54e12de918f6c060a5c",
"$['settings']['analytics']": "94c116ee118a72998db6cd10b586a9ef112a1891ff8ced8c7b2944ff62453b5f",
"$['settings']['analytics-description']": "b1cc043af19eedc5e22078ed0ef859091b321f0e2921ab915651e52c3ba42c8e",
"$['settings']['logs']": "ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515",
"$['settings']['logs-description']": "af971f78c75f6b9a8ae534b23a094723837e42415c1246923b746df5c80b84d7",
"$['settings']['open-logs']": "ed077f3d8125d60dca1979c7133601bd187d47c73ed9975028f677e49e709942",
"$['settings']['update']": "46ed69579c396feaa780e716e778b9dea387bebfe925e9d661ec6882c96525e9",
"$['settings']['error']": "63f6bf5375c13518fbc291863e4715083eeb5d50fa2c2aa00cdf3958267f693b"
}
}

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "اللغة والمنطقة",
"watchPreferences": "تفضيلات المشاهدة",
"manifest": "بيان",
"logs": "سجلات التصحيح",
"settings": "إعدادات التطبيق",
"signOut": "تسجيل الخروج"
},
"settings": {
"toast": "تم تحديث إعداداتك بنجاح.",
"settings": "الإعدادات",
"description": "قم بتكوين خيارات خاصة بالتطبيق مثل التحليلات والسمات والتفضيلات المحلية الأخرى.",
"analytics": "تحليلات",
"analytics-description": "إرسال بيانات الاستخدام المجهولة للمساعدة في تحسين التطبيق وتوجيه التطوير.",
"logs": "السجلات",
"logs-description": "عرض سجلات التطبيق لتصحيح الأخطاء واستكشاف المشكلات.",
"open-logs": "يفتح",
"update": "تحديث الإعدادات",
"error": "فشل في تحديث الإعدادات"
},
"preferences": {
"preferences": "التفضيلات",
"description": "اختر لغتك وبلدك المفضلين للاستمتاع بمحتوى مخصص.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Sprog og region",
"watchPreferences": "Seerpræferencer",
"manifest": "Manifest",
"logs": "Fejlretningslogfiler",
"settings": "App-indstillinger",
"signOut": "Log ud"
},
"settings": {
"toast": "Dine indstillinger er blevet opdateret med succes.",
"settings": "Indstillinger",
"description": "Konfigurer app-specifikke indstillinger som analyser, temaer og andre lokale præferencer.",
"analytics": "Analyser",
"analytics-description": "Send anonyme brugsdata for at hjælpe med at forbedre appen og guide udviklingen.",
"logs": "Logfiler",
"logs-description": "Se applikationslogfiler for fejlfinding og problemløsning.",
"open-logs": "Åben",
"update": "Opdater indstillinger",
"error": "Kunne ikke opdatere indstillingerne"
},
"preferences": {
"preferences": "Indstillinger",
"description": "Vælg dit foretrukne sprog og land for at nyde skræddersyet indhold.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Sprache & Region",
"watchPreferences": "Sehgewohnheiten",
"manifest": "Manifest",
"logs": "Debug-Protokolle",
"settings": "App-Einstellungen",
"signOut": "Abmelden"
},
"settings": {
"toast": "Ihre Einstellungen wurden erfolgreich aktualisiert.",
"settings": "Einstellungen",
"description": "Konfigurieren Sie app-spezifische Optionen wie Analysen, Themen und andere lokale Einstellungen.",
"analytics": "Analytik",
"analytics-description": "Sende anonyme Nutzungsdaten, um die App zu verbessern und die Entwicklung zu steuern.",
"logs": "Protokolle",
"logs-description": "Anwendungsprotokolle zur Fehlerbehebung und Fehlersuche anzeigen.",
"open-logs": "Offen",
"update": "Einstellungen aktualisieren",
"error": "Aktualisierung der Einstellungen fehlgeschlagen"
},
"preferences": {
"preferences": "Einstellungen",
"description": "Wählen Sie Ihre bevorzugte Sprache und Ihr Land aus, um maßgeschneiderte Inhalte zu genießen.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Γλώσσα & περιοχή",
"watchPreferences": "Προτιμήσεις παρακολούθησης",
"manifest": "μανιφέστο",
"logs": "Αρχεία καταγραφής εντοπισμού σφαλμάτων",
"settings": "Ρυθμίσεις εφαρμογής",
"signOut": "Αποσύνδεση"
},
"settings": {
"toast": "Οι ρυθμίσεις σας έχουν ενημερωθεί με επιτυχία.",
"settings": "Ρυθμίσεις",
"description": "Ρυθμίστε επιλογές συγκεκριμένες για την εφαρμογή όπως αναλύσεις, θέματα και άλλες τοπικές προτιμήσεις.",
"analytics": "Αναλύσεις",
"analytics-description": "Στείλτε ανώνυμα δεδομένα χρήσης για να βοηθήσετε στη βελτίωση της εφαρμογής και να καθοδηγήσετε την ανάπτυξη.",
"logs": "Αρχεία καταγραφής",
"logs-description": "Προβολή των αρχείων καταγραφής της εφαρμογής για εντοπισμό και επίλυση προβλημάτων.",
"open-logs": "Ανοιχτό",
"update": "Ρυθμίσεις ενημέρωσης",
"error": "Αποτυχία ενημέρωσης ρυθμίσεων"
},
"preferences": {
"preferences": "Προτιμήσεις",
"description": "Επιλέξτε την προτιμώμενη γλώσσα και χώρα σας για να απολαμβάνετε προσαρμοσμένο περιεχόμενο.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Language & region",
"watchPreferences": "Watch preferences",
"manifest": "Manifesto",
"logs": "Debug logs",
"settings": "App settings",
"signOut": "Sign out"
},
"settings": {
"toast": "Your settings have been successfully updated.",
"settings": "Settings",
"description": "Configure app-specific options like analytics, themes, and other local preferences.",
"analytics": "Analytics",
"analytics-description": "Send anonymous usage data to help improve the app and guide development.",
"logs": "Logs",
"logs-description": "View application logs for debugging and troubleshooting.",
"open-logs": "Open",
"update": "Update settings",
"error": "Failed to update settings"
},
"preferences": {
"preferences": "Preferences",
"description": "Select your preferred language and country to enjoy tailored content.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Idioma y región",
"watchPreferences": "Preferencias de visualización",
"manifest": "Manifiesto",
"logs": "Registros de depuración",
"settings": "Configuración de la aplicación",
"signOut": "Cerrar sesión"
},
"settings": {
"toast": "Tus configuraciones se han actualizado correctamente.",
"settings": "Configuración",
"description": "Configura opciones específicas de la aplicación como analíticas, temas y otras preferencias locales.",
"analytics": "Analíticas",
"analytics-description": "Enviar datos de uso anónimos para ayudar a mejorar la aplicación y guiar su desarrollo.",
"logs": "Registros",
"logs-description": "Ver registros de la aplicación para depuración y resolución de problemas.",
"open-logs": "Abierto",
"update": "Configuración de actualización",
"error": "Error al actualizar la configuración"
},
"preferences": {
"preferences": "Preferencias",
"description": "Selecciona tu idioma y país preferidos para disfrutar de contenido adaptado.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Keel ja piirkond",
"watchPreferences": "Vaatamise eelistused",
"manifest": "Manifesto",
"logs": "Logid",
"settings": "Rakenduse seaded",
"signOut": "Logi välja"
},
"settings": {
"toast": "Seerotaatewochin keffilachehu.",
"settings": "አቀማመጦች",
"description": "የመተግበሪያ ልዩ አማራጮችን እንደ ትንታኔዎች፣ ገጽታዎች እና ሌሎች አካባቢ ተመሳሳይ ቅድመ አላባት ያቀናብሩ።",
"analytics": "Analüüsid",
"analytics-description": "Saada anonüümset kasutusandmeid, et aidata rakenduse täiustamisel ja arenduse suunamisel.",
"logs": "Logid",
"logs-description": "Rakibata galmee raawwii barnootaa fi rakkoo hiikuu ilaaluu.",
"open-logs": "Avatud",
"update": "አማራጮችን አዘምን",
"error": "ቅኝቱን ማሻሻል አልተቻለም"
},
"preferences": {
"preferences": "ቅደም ተከተል፦",
"description": "Valitud sisu nautimiseks valige oma eelistatud keel ja riik.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "زبان و منطقه",
"watchPreferences": "ترجیحات تماشا",
"manifest": "مانیفست",
"logs": "گزارش‌های اشکال‌زدایی",
"settings": "تنظیمات برنامه",
"signOut": "خروج از حساب"
},
"settings": {
"toast": "تنظیمات شما با موفقیت به‌روزرسانی شدند.",
"settings": "تنظیمات",
"description": "تنظیم گزینه‌های خاص برنامه مانند تحلیل‌ها، تم‌ها و سایر تنظیمات محلی.",
"analytics": "تجزیه و تحلیل",
"analytics-description": "ارسال داده‌های ناشناس استفاده برای کمک به بهبود اپلیکیشن و هدایت توسعه.",
"logs": "سیاههها",
"logs-description": "مشاهده لاگ‌های برنامه برای اشکال‌زدایی و رفع مشکلات.",
"open-logs": "باز",
"update": "تنظیمات به‌روزرسانی",
"error": "تنظیمات به‌روزرسانی نشد"
},
"preferences": {
"preferences": "تنظیمات",
"description": "زبان و کشور مورد نظر خود را انتخاب کنید تا از محتوای متناسب با سلیقه خود لذت ببرید.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Kieli ja alue",
"watchPreferences": "Katseluvalinnat",
"manifest": "Manifesti",
"logs": "Virhelokin kirjaus",
"settings": "Sovelluksen asetukset",
"signOut": "Kirjaudu ulos"
},
"settings": {
"toast": "Asetuksesi on päivitetty onnistuneesti.",
"settings": "Asetukset",
"description": "Määritä sovelluskohtaisia asetuksia, kuten analytiikkaa, teemoja ja muita paikallisia mieltymyksiä.",
"analytics": "Analytiikka",
"analytics-description": "Lähetä anonyymeja käyttötietoja auttamaan sovelluksen parantamisessa ja kehityksen ohjaamisessa.",
"logs": "Lokit",
"logs-description": "Tarkastele sovelluksen lokitietoja virheenkorjaukseen ja vianmääritykseen.",
"open-logs": "Avata",
"update": "Päivitä asetukset",
"error": "Asetusten päivittäminen epäonnistui"
},
"preferences": {
"preferences": "Asetukset",
"description": "Valitse haluamasi kieli ja maa nauttiaksesi räätälöidystä sisällöstä.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Langue et région",
"watchPreferences": "Préférences de visionnage",
"manifest": "Manifeste",
"logs": "Journaux de débogage",
"settings": "Paramètres de l'application",
"signOut": "Se déconnecter"
},
"settings": {
"toast": "Vos paramètres ont été mis à jour avec succès.",
"settings": "Paramètres",
"description": "Configurez des options spécifiques à l'application comme les analyses, les thèmes et d'autres préférences locales.",
"analytics": "Analytique",
"analytics-description": "Envoyer des données d'utilisation anonymes pour aider à améliorer l'application et orienter son développement.",
"logs": "Journaux",
"logs-description": "Afficher les journaux de l'application pour le débogage et le dépannage.",
"open-logs": "Ouvrir",
"update": "Paramètres de mise à jour",
"error": "Échec de la mise à jour des paramètres"
},
"preferences": {
"preferences": "Préférences",
"description": "Sélectionnez votre langue et votre pays préférés pour profiter d'un contenu personnalisé.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Teanga & réigiún",
"watchPreferences": "Roghanna féachana",
"manifest": "Manifiosta",
"logs": "Loga dífhabhtacha",
"settings": "Socruithe Aipe",
"signOut": "Logáil amach"
},
"settings": {
"toast": "Tá do shocruithe nuashonraithe go rathúil.",
"settings": "Socruithe",
"description": "Cumraigh roghanna ar leith don aip cosúil le hanailísíocht, téamaí, agus roghanna áitiúla eile.",
"analytics": "Anailís",
"analytics-description": "Seol sonraí úsáide gan ainm chun cabhrú leis an aip a fheabhsú agus forbairt a threorú.",
"logs": "Logaí",
"logs-description": "Féach ar logaí feidhmchláir chun dífhabhtú agus fabhtcheartú a dhéanamh.",
"open-logs": "Oscail",
"update": "Nuashonraigh socruithe",
"error": "Theip ar shocruithe a nuashonrú"
},
"preferences": {
"preferences": "Roghanna",
"description": "Roghnaigh do theanga agus do thír is fearr leat chun taitneamh a bhaint as ábhar saincheaptha.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "שפה ואזור",
"watchPreferences": "העדפות צפייה",
"manifest": "מניפסט",
"logs": "יומני ניפוי שגיאות",
"settings": "הגדרות האפליקציה",
"signOut": "התנתק"
},
"settings": {
"toast": "ההגדרות שלך עודכנו בהצלחה.",
"settings": "הגדרות",
"description": "הגדר אפשרויות ספציפיות לאפליקציה כמו ניתוח נתונים, ערכות נושא והעדפות מקומיות אחרות.",
"analytics": "אנליטיקס",
"analytics-description": "שלח נתוני שימוש אנונימיים כדי לעזור לשפר את האפליקציה ולהנחות את הפיתוח.",
"logs": "יומני רישום",
"logs-description": "הצג יומני אפליקציה לאיתור תקלות ופתרון בעיות.",
"open-logs": "לִפְתוֹחַ",
"update": "עדכן הגדרות",
"error": "נכשל בעדכון ההגדרות"
},
"preferences": {
"preferences": "העדפות",
"description": "בחר את השפה והמדינה המועדפות עליך כדי ליהנות מתוכן מותאם.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "भाषा और क्षेत्र",
"watchPreferences": "देखने की प्राथमिकताएँ",
"manifest": "घोषणापत्र",
"logs": "डीबग लॉग्स",
"settings": "ऐप सेटिंग्स",
"signOut": "साइन आउट करें"
},
"settings": {
"toast": "आपकी सेटिंग्स सफलतापूर्वक अपडेट कर दी गई हैं।",
"settings": "सेटिंग्स",
"description": "ऐप-विशिष्ट विकल्पों जैसे कि एनालिटिक्स, थीम्स और अन्य स्थानीय प्राथमिकताओं को कॉन्फ़िगर करें।",
"analytics": "एनालिटिक्स",
"analytics-description": "ऐप को सुधारने और विकास का मार्गदर्शन करने में मदद के लिए गुमनाम उपयोग डेटा भेजें।",
"logs": "लॉग्स",
"logs-description": "ऐप्लिकेशन लॉग्स को डिबगिंग और समस्याओं का समाधान करने के लिए देखें।",
"open-logs": "खुला",
"update": "सेटिंग्स अपडेट करें",
"error": "सेटिंग्स अपडेट करने में विफल रहा"
},
"preferences": {
"preferences": "प्राथमिकताएँ",
"description": "अपनी पसंदीदा भाषा और देश का चयन करें ताकि आप विशेष सामग्री का आनंद ले सकें।",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Lingua e regione",
"watchPreferences": "Preferenze di visione",
"manifest": "Manifesto",
"logs": "Log di debug",
"settings": "Impostazioni dell'app",
"signOut": "Esci"
},
"settings": {
"toast": "Le tue impostazioni sono state aggiornate con successo.",
"settings": "Impostazioni",
"description": "Configura opzioni specifiche dell'app come analisi, temi e altre preferenze locali.",
"analytics": "Analisi",
"analytics-description": "Invia dati di utilizzo anonimi per aiutare a migliorare l'app e guidare lo sviluppo.",
"logs": "Registri",
"logs-description": "Visualizza i registri dell'applicazione per il debug e la risoluzione dei problemi.",
"open-logs": "Aprire",
"update": "Aggiorna impostazioni",
"error": "Impossibile aggiornare le impostazioni"
},
"preferences": {
"preferences": "Preferenze",
"description": "Seleziona la tua lingua e il tuo paese preferiti per goderti contenuti su misura.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "言語と地域",
"watchPreferences": "視聴設定",
"manifest": "マニフェスト",
"logs": "デバッグログ",
"settings": "アプリ設定",
"signOut": "ログアウト"
},
"settings": {
"toast": "設定が正常に更新されました。",
"settings": "設定",
"description": "アプリ固有のオプションを設定して、分析、テーマ、その他のローカル設定を調整します。",
"analytics": "アナリティクス",
"analytics-description": "アプリを改善し、開発を進めるために匿名の使用データを送信します。",
"logs": "ログ",
"logs-description": "デバッグおよびトラブルシューティングのためにアプリケーションログを表示します。",
"open-logs": "開ける",
"update": "設定を更新する",
"error": "設定の更新に失敗しました"
},
"preferences": {
"preferences": "設定",
"description": "言語と国を選択して、あなたに合わせたコンテンツをお楽しみください。",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Taal & regio",
"watchPreferences": "Bekijk voorkeuren",
"manifest": "Manifest",
"logs": "Foutopsporingslogboeken",
"settings": "App-instellingen",
"signOut": "Afmelden"
},
"settings": {
"toast": "Je instellingen zijn succesvol bijgewerkt.",
"settings": "Instellingen",
"description": "Configureer app-specifieke opties zoals analytics, thema's en andere lokale voorkeuren.",
"analytics": "Analytics",
"analytics-description": "Verzend anonieme gebruiksgegevens om de app te verbeteren en de ontwikkeling te begeleiden.",
"logs": "Logboeken",
"logs-description": "Bekijk toepassingslogboeken voor debuggen en probleemoplossing.",
"open-logs": "Open",
"update": "Instellingen bijwerken",
"error": "Instellingen bijwerken mislukt"
},
"preferences": {
"preferences": "Voorkeuren",
"description": "Selecteer je voorkeurstaal en land om van op maat gemaakte inhoud te genieten.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Språk og region",
"watchPreferences": "Se preferanser for visning",
"manifest": "Manifest",
"logs": "Feilrettingslogger",
"settings": "App-innstillinger",
"signOut": "Logg ut"
},
"settings": {
"toast": "Innstillingene dine har blitt oppdatert.",
"settings": "Innstillinger",
"description": "Konfigurer app-spesifikke alternativer som analyseverktøy, temaer og andre lokale preferanser.",
"analytics": "Analyser",
"analytics-description": "Send anonyme bruksdata for å hjelpe med å forbedre appen og veilede utviklingen.",
"logs": "Logger",
"logs-description": "Se programlogger for feilsøking og feilanalyse.",
"open-logs": "Åpne",
"update": "Oppdater innstillinger",
"error": "Kunne ikke oppdatere innstillinger"
},
"preferences": {
"preferences": "Preferanser",
"description": "Velg ditt foretrukne språk og land for å nyte skreddersydd innhold.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Język i region",
"watchPreferences": "Preferencje oglądania",
"manifest": "Manifest",
"logs": "Dzienniki debugowania",
"settings": "Ustawienia aplikacji",
"signOut": "Wyloguj się"
},
"settings": {
"toast": "Twoje ustawienia zostały pomyślnie zaktualizowane.",
"settings": "Ustawienia",
"description": "Skonfiguruj opcje specyficzne dla aplikacji, takie jak analityka, motywy i inne preferencje lokalne.",
"analytics": "Analityka",
"analytics-description": "Wyślij anonimowe dane użytkowania, aby pomóc w ulepszaniu aplikacji i kierować jej rozwojem.",
"logs": "Dzienniki",
"logs-description": "Zobacz dzienniki aplikacji w celu debugowania i rozwiązywania problemów.",
"open-logs": "Otwarte",
"update": "Zaktualizuj ustawienia",
"error": "Nie udało się zaktualizować ustawień"
},
"preferences": {
"preferences": "Preferencje",
"description": "Wybierz preferowany język i kraj, aby cieszyć się dostosowaną treścią.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Idioma e região",
"watchPreferences": "Preferências de visualização",
"manifest": "Manifesto",
"logs": "Registros de depuração",
"settings": "Configurações do aplicativo",
"signOut": "Sair"
},
"settings": {
"toast": "Suas configurações foram atualizadas com sucesso.",
"settings": "Configurações",
"description": "Configure opções específicas do aplicativo como análises, temas e outras preferências locais.",
"analytics": "Analytics",
"analytics-description": "Envie dados de uso anônimos para ajudar a melhorar o aplicativo e orientar o desenvolvimento.",
"logs": "Registros",
"logs-description": "Visualizar logs do aplicativo para depuração e solução de problemas.",
"open-logs": "Abrir",
"update": "Configurações de atualização",
"error": "Falha ao atualizar as configurações"
},
"preferences": {
"preferences": "Preferências",
"description": "Selecione seu idioma e país preferidos para desfrutar de conteúdo personalizado.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Језик и регион",
"watchPreferences": "Preferencije gledanja",
"manifest": "Manifest",
"logs": "Дебаг логови",
"settings": "Podešavanja aplikacije",
"signOut": "Odjavi se"
},
"settings": {
"toast": "Vaša podešavanja su uspešno ažurirana.",
"settings": "Podešavanja",
"description": "Konfigurišite opcije specifične za aplikaciju kao što su analitika, teme i druge lokalne postavke.",
"analytics": "Analitika",
"analytics-description": "Pošaljite anonimne podatke o korišćenju kako biste pomogli u poboljšanju aplikacije i usmeravanju razvoja.",
"logs": "Дневници",
"logs-description": "Pregledajte logove aplikacije za otklanjanje grešaka i rešavanje problema.",
"open-logs": "Отворено",
"update": "Ажурирање подешавања",
"error": "Nije uspelo ažuriranje podešavanja"
},
"preferences": {
"preferences": "Preferencije",
"description": "Izaberite željeni jezik i državu da biste uživali u sadržaju prilagođenom vama.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Språk och region",
"watchPreferences": "Tittarinställningar",
"manifest": "Manifest",
"logs": "Felsökningsloggar",
"settings": "Appinställningar",
"signOut": "Logga ut"
},
"settings": {
"toast": "Dina inställningar har uppdaterats framgångsrikt.",
"settings": "Inställningar",
"description": "Konfigurera app-specifika alternativ som analys, teman och andra lokala inställningar.",
"analytics": "Analys",
"analytics-description": "Skicka anonym användardata för att hjälpa till att förbättra appen och vägleda utvecklingen.",
"logs": "Loggar",
"logs-description": "Visa applikationsloggar för felsökning och avhjälpning.",
"open-logs": "Öppna",
"update": "Uppdatera inställningar",
"error": "Misslyckades med att uppdatera inställningarna"
},
"preferences": {
"preferences": "Inställningar",
"description": "Välj ditt föredragna språk och land för att njuta av skräddarsytt innehåll.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "Dil ve bölge",
"watchPreferences": "İzleme tercihleri",
"manifest": "Manifesto",
"logs": "Hata ayıklama günlükleri",
"settings": "Uygulama ayarları",
"signOut": "Oturum kapat"
},
"settings": {
"toast": "Ayarlarınız başarıyla güncellendi.",
"settings": "Ayarlar",
"description": "Uygulamaya özgü seçenekleri, analitik, temalar ve diğer yerel tercihler gibi yapılandırın.",
"analytics": "Analitikler",
"analytics-description": "Uygulamayı geliştirmek ve geliştirme sürecine rehberlik etmek için anonim kullanım verilerini gönder.",
"logs": "Günlükler",
"logs-description": "Uygulama günlüklerini hata ayıklama ve sorun giderme için görüntüle.",
"open-logs": "Açık",
"update": "Ayarları güncelle",
"error": "Ayarlar güncellenemedi"
},
"preferences": {
"preferences": "Tercihler",
"description": "Tercih ettiğiniz dili ve ülkeyi seçerek size özel içeriklerin keyfini çıkarın.",

View File

@@ -43,10 +43,21 @@
"menu": {
"preferences": "زبان اور علاقہ",
"watchPreferences": "دیکھنے کی ترجیحات",
"manifest": "منشور",
"logs": "ڈی بگ لاگز",
"settings": "ایپ کی ترتیبات",
"signOut": "سائن آؤٹ"
},
"settings": {
"toast": "آپ کی ترتیبات کامیابی سے اپڈیٹ ہو گئی ہیں۔",
"settings": "ترتیبات",
"description": "ایپ کی مخصوص اختیارات جیسے تجزیات، تھیمز، اور دیگر مقامی ترجیحات کو ترتیب دیں۔",
"analytics": "تجزیات",
"analytics-description": "ایپ کو بہتر بنانے اور ترقی کی رہنمائی کے لئے گمنام استعمال کا ڈیٹا بھیجیں۔",
"logs": "نوشتہ جات",
"logs-description": "ایپلیکیشن لاگز کو ڈیبگنگ اور مسئلہ حل کرنے کے لیے دیکھیں۔",
"open-logs": "کھولیں۔",
"update": "ترتیبات کو اپ ڈیٹ کریں",
"error": "ترتیبات کو اپ ڈیٹ کرنے میں ناکامی ہوئی ہے۔"
},
"preferences": {
"preferences": "ترجیحات",
"description": "اپنی پسندیدہ زبان اور ملک کا انتخاب کریں تاکہ آپ کو مخصوص مواد مل سکے۔",

View File

@@ -4,4 +4,5 @@ pub mod event;
pub mod graphql;
pub mod logs;
pub mod session;
pub mod settings;
pub mod window;

View File

@@ -3,6 +3,7 @@ use anyhow::Context;
use popcorntime_error::Code;
use popcorntime_graphql_client::client::ApiClient;
use popcorntime_session::{AuthorizationService, SessionUpdateEvent};
use popcorntime_settings::SettingsService;
use popcorntime_tauri::event::{SessionServerReady, SessionUpdate};
#[cfg(debug_assertions)]
use specta_typescript::Typescript;
@@ -23,11 +24,11 @@ fn main() {
popcorntime_tauri::graphql::set_favorites_multiple_providers,
popcorntime_tauri::graphql::set_media_reaction,
popcorntime_tauri::window::show_main_window,
popcorntime_tauri::session::is_onboarded,
popcorntime_tauri::session::set_onboarded,
popcorntime_tauri::session::validate,
popcorntime_tauri::session::logout,
popcorntime_tauri::session::initialize_session_authorization,
popcorntime_tauri::settings::settings,
popcorntime_tauri::settings::update_settings,
])
.events(collect_events![SessionServerReady, SessionUpdate]);
@@ -77,18 +78,24 @@ fn main() {
(
paths.app_data_dir().expect("missing app data dir"),
paths.app_cache_dir().expect("missing app cache dir"),
paths.config_dir().expect("missing config dir"),
paths.app_config_dir().expect("missing config dir"),
)
};
std::fs::create_dir_all(&app_data_dir).expect("failed to create app data dir");
std::fs::create_dir_all(&app_cache_dir).expect("failed to create cache dir");
let config_dir = config_dir.join(app_handle.config().identifier.as_str());
std::fs::create_dir_all(&config_dir).expect("failed to create config dir");
tracing::info!(version = %app_handle.package_info().version,
name = %app_handle.package_info().name, "starting app");
tracing::info!(
version = %app_handle.package_info().version,
name = %app_handle.package_info().name, "starting app"
);
// settings service
let settings_service = SettingsService::new(&config_dir)?;
app_handle.manage(settings_service);
// auth service
let auth_service =
AuthorizationService::new(&config_dir, app_handle.config().identifier.as_str())?;
app_handle.manage(ApiClient::new(auth_service.try_access_token())?);
@@ -109,6 +116,7 @@ fn main() {
},
)?;
// deep links
let app_handle_isolated = app_handle.clone();
app.deep_link().on_open_url(move |event| {
tracing::info!(

View File

@@ -33,20 +33,6 @@ pub async fn validate(service: State<'_, AuthorizationService>) -> Result<(), Er
service.validate().await.map_err(Into::into)
}
#[tauri::command(async)]
#[specta::specta]
#[instrument(skip(service), err(Debug))]
pub async fn is_onboarded(service: State<'_, AuthorizationService>) -> Result<bool, Error> {
service.is_onboarded().map_err(Into::into)
}
#[tauri::command(async)]
#[specta::specta]
#[instrument(skip(service), err(Debug))]
pub async fn set_onboarded(service: State<'_, AuthorizationService>) -> Result<(), Error> {
service.set_onboarded(true).map_err(Into::into)
}
#[tauri::command(async)]
#[specta::specta]
#[instrument(skip(service), err(Debug))]

View File

@@ -0,0 +1,32 @@
use crate::error::Error;
use popcorntime_settings::{Settings, SettingsInput, SettingsService};
use tauri::State;
use tracing::instrument;
#[tauri::command(async)]
#[specta::specta]
#[instrument(skip(service), err(Debug))]
pub async fn settings(service: State<'_, SettingsService>) -> Result<Settings, Error> {
service.get().await.map_err(Into::into)
}
#[tauri::command(async)]
#[specta::specta]
#[instrument(skip(service), err(Debug))]
pub async fn update_settings(
service: State<'_, SettingsService>,
settings: SettingsInput,
) -> Result<Settings, Error> {
service
.update(|current_settings| {
if let Some(onboarding_complete) = settings.onboarding_complete {
current_settings.onboarding_complete = onboarding_complete;
}
if let Some(enable_analytics) = settings.enable_analytics {
current_settings.enable_analytics = enable_analytics;
}
})
.await
.map_err(Into::into)
}

View File

@@ -29,17 +29,17 @@
"@types/react": "^19.1.16",
"@types/react-dom": "^19.1.9",
"@vitejs/plugin-react-swc": "4.1.0",
"@vitest/browser": "3.2.4",
"@vitest/coverage-v8": "3.2.4",
"autoprefixer": "^10.4.20",
"globals": "^16.4.0",
"playwright": "^1.55.1",
"postcss": "^8.5.3",
"storybook": "^9.1.10",
"tailwind-merge": "^3.3.1",
"typescript": "^5.9.3",
"vite": "^6.1.0",
"vitest": "catalog:",
"@vitest/browser": "3.2.4",
"playwright": "^1.55.1",
"@vitest/coverage-v8": "3.2.4"
"vitest": "catalog:"
},
"dependencies": {
"@hookform/resolvers": "catalog:",
@@ -56,6 +56,7 @@
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",

View File

@@ -0,0 +1,27 @@
"use client";
import { cn } from "@popcorntime/ui/lib/utils";
import * as SwitchPrimitive from "@radix-ui/react-switch";
import type * as React from "react";
function Switch({ className, ...props }: React.ComponentProps<typeof SwitchPrimitive.Root>) {
return (
<SwitchPrimitive.Root
data-slot="switch"
className={cn(
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className={cn(
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitive.Root>
);
}
export { Switch };

82
pnpm-lock.yaml generated
View File

@@ -179,6 +179,9 @@ importers:
lucide-react:
specifier: 'catalog:'
version: 0.544.0(react@19.1.1)
posthog-js:
specifier: ^1.266.0
version: 1.269.0
react:
specifier: 'catalog:'
version: 19.1.1
@@ -345,6 +348,9 @@ importers:
'@radix-ui/react-slot':
specifier: ^1.2.3
version: 1.2.3(@types/react@19.1.16)(react@19.1.1)
'@radix-ui/react-switch':
specifier: ^1.2.6
version: 1.2.6(@types/react-dom@19.1.9(@types/react@19.1.16))(@types/react@19.1.16)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-tabs':
specifier: ^1.1.13
version: 1.1.13(@types/react-dom@19.1.9(@types/react@19.1.16))(@types/react@19.1.16)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -1143,6 +1149,9 @@ packages:
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@posthog/core@1.2.2':
resolution: {integrity: sha512-f16Ozx6LIigRG+HsJdt+7kgSxZTHeX5f1JlCGKI1lXcvlZgfsCR338FuMI2QRYXGl+jg/vYFzGOTQBxl90lnBg==}
'@protobufjs/aspromise@1.1.2':
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
@@ -1519,6 +1528,19 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-switch@1.2.6':
resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-tabs@1.1.13':
resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==}
peerDependencies:
@@ -2728,6 +2750,9 @@ packages:
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
engines: {node: '>=18'}
core-js@3.45.1:
resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
@@ -3034,6 +3059,9 @@ packages:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
fflate@0.4.8:
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@@ -4120,6 +4148,20 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
posthog-js@1.269.0:
resolution: {integrity: sha512-Qwa6hiAxfUFRihPpulCs13IicDGYLrkXpxaD+dRjuUjT8lIFW/O3PQOOKvs2QvaxnZYkM81ZYvwlsUvrgrdKiw==}
peerDependencies:
'@rrweb/types': 2.0.0-alpha.17
rrweb-snapshot: 2.0.0-alpha.17
peerDependenciesMeta:
'@rrweb/types':
optional: true
rrweb-snapshot:
optional: true
preact@10.27.2:
resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==}
prelude-ls@1.1.2:
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
engines: {node: '>= 0.8.0'}
@@ -4976,6 +5018,9 @@ packages:
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
engines: {node: '>= 14'}
web-vitals@4.2.4:
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -5839,6 +5884,8 @@ snapshots:
'@polka/url@1.0.0-next.29': {}
'@posthog/core@1.2.2': {}
'@protobufjs/aspromise@1.1.2': {}
'@protobufjs/base64@1.1.2': {}
@@ -6233,6 +6280,21 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.16
'@radix-ui/react-switch@1.2.6(@types/react-dom@19.1.9(@types/react@19.1.16))(@types/react@19.1.16)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.16)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.16)(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.16))(@types/react@19.1.16)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.16)(react@19.1.1)
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.16)(react@19.1.1)
'@radix-ui/react-use-size': 1.1.1(@types/react@19.1.16)(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.1.16
'@types/react-dom': 19.1.9(@types/react@19.1.16)
'@radix-ui/react-tabs@1.1.13(@types/react-dom@19.1.9(@types/react@19.1.16))(@types/react@19.1.16)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -7080,9 +7142,9 @@ snapshots:
std-env: 3.9.0
test-exclude: 7.0.1
tinyrainbow: 2.0.0
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@18.19.129)(@vitest/browser@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.7.0)
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.0)(@vitest/browser@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.7.0)
optionalDependencies:
'@vitest/browser': 3.2.4(playwright@1.55.1)(vite@6.3.6(@types/node@18.19.129)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.7.0))(vitest@3.2.4)
'@vitest/browser': 3.2.4(playwright@1.55.1)(vite@6.3.6(@types/node@24.5.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.7.0))(vitest@3.2.4)
transitivePeerDependencies:
- supports-color
@@ -7496,6 +7558,8 @@ snapshots:
cookie@1.0.2: {}
core-js@3.45.1: {}
core-util-is@1.0.3: {}
create-jest@29.7.0(@types/node@24.5.0):
@@ -7792,6 +7856,8 @@ snapshots:
node-domexception: 1.0.0
web-streams-polyfill: 3.3.3
fflate@0.4.8: {}
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -9104,6 +9170,16 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
posthog-js@1.269.0:
dependencies:
'@posthog/core': 1.2.2
core-js: 3.45.1
fflate: 0.4.8
preact: 10.27.2
web-vitals: 4.2.4
preact@10.27.2: {}
prelude-ls@1.1.2: {}
prettier-plugin-tailwindcss@0.6.14(prettier@3.6.2):
@@ -10019,6 +10095,8 @@ snapshots:
web-streams-polyfill@4.0.0-beta.3: {}
web-vitals@4.2.4: {}
webidl-conversions@3.0.1: {}
webidl-conversions@7.0.0: {}