diff --git a/public/locales/af/settings.json b/public/locales/af/settings.json index 67e2e7e48..310aad9f4 100644 --- a/public/locales/af/settings.json +++ b/public/locales/af/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "Hierdie aksie vereis \"n herbegin om veranderinge toe te pas", "action-restart-copy": "Jou voorkeur sal gestoor word en toegepas word die volgende keer as Tari Universe begin word.", "app-restart-required": "App herbegin benodig", + "application-info": "Application Information", "applyInviteCode": "Pas Uitnodigingskode Toe", "cancel": "Cancel", "change-language": "Verander taal", diff --git a/public/locales/cn/settings.json b/public/locales/cn/settings.json index bfe5b18d0..b9c06dbad 100644 --- a/public/locales/cn/settings.json +++ b/public/locales/cn/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "此操作需要重启以应用更改", "action-restart-copy": "您的偏好将被保存,并在下次启动 Tari 宇宙时应用。", "app-restart-required": "需要重启应用", + "application-info": "Application Information", "applyInviteCode": "应用邀请代码", "cancel": "取消", "change-language": "更改语言", diff --git a/public/locales/de/settings.json b/public/locales/de/settings.json index 4095a3ce1..1e6d53118 100644 --- a/public/locales/de/settings.json +++ b/public/locales/de/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "Diese Aktion erfordert einen Neustart, um Änderungen anzuwenden", "action-restart-copy": "Ihre Einstellungen werden gespeichert und beim nächsten Start des Tari-Universums angewendet.", "app-restart-required": "App-Neustart erforderlich", + "application-info": "Application Information", "applyInviteCode": "Einladungscode anwenden", "cancel": "Abbrechen", "change-language": "Sprache", diff --git a/public/locales/en/settings.json b/public/locales/en/settings.json index 126c74052..78ecc5c68 100644 --- a/public/locales/en/settings.json +++ b/public/locales/en/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "This action requires restart to apply changes", "action-restart-copy": "Your preference will be saved and applied the next time Tari Universe is started.", "app-restart-required": "App restart required", + "application-info": "Application Information", "applyInviteCode": "Apply Invite Code", "cancel": "Cancel", "change-language": "Language", diff --git a/public/locales/fr/settings.json b/public/locales/fr/settings.json index b1504f4e1..bff8c72d4 100644 --- a/public/locales/fr/settings.json +++ b/public/locales/fr/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "Cette action nécessite un redémarrage pour appliquer les modifications", "action-restart-copy": "Votre préférence sera enregistrée et appliquée lors du prochain démarrage de Tari Universe.", "app-restart-required": "Redémarrage de l\"application requis", + "application-info": "Application Information", "applyInviteCode": "Appliquer le code d\"invitation", "cancel": "Cancel", "change-language": "Langue", diff --git a/public/locales/hi/settings.json b/public/locales/hi/settings.json index 88a02da0c..7afc8af3f 100644 --- a/public/locales/hi/settings.json +++ b/public/locales/hi/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "इस क्रिया के लिए परिवर्तनों को लागू करने हेतु पुनः आरंभ करना आवश्यक है", "action-restart-copy": "आपकी पसंद को सहेजा जाएगा और अगली बार जब तारी यूनिवर्स शुरू होगा, तब लागू किया जाएगा।", "app-restart-required": "ऐप पुनः प्रारंभ आवश्यक है", + "application-info": "Application Information", "applyInviteCode": "आमंत्रण कोड लागू करें", "cancel": "रद्द करें", "change-language": "भाषा", diff --git a/public/locales/id/settings.json b/public/locales/id/settings.json index ad9cb7bec..9a7c574e7 100644 --- a/public/locales/id/settings.json +++ b/public/locales/id/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "Tindakan ini memerlukan restart untuk menerapkan perubahan", "action-restart-copy": "Preferensi Anda akan disimpan dan diterapkan saat Tari Universe dimulai berikutnya.", "app-restart-required": "Diperlukan restart aplikasi", + "application-info": "Application Information", "applyInviteCode": "Terapkan Kode Undangan", "cancel": "Batal", "change-language": "Bahasa", diff --git a/public/locales/ja/settings.json b/public/locales/ja/settings.json index 73c11441f..035dadd82 100644 --- a/public/locales/ja/settings.json +++ b/public/locales/ja/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "この操作には再起動が必要です。変更を適用します", "action-restart-copy": "設定は保存され、次回Tari Universeを起動したときに適用されます。", "app-restart-required": "アプリの再起動が必要です", + "application-info": "Application Information", "applyInviteCode": "招待コードを適用", "cancel": "キャンセル", "change-language": "言語", diff --git a/public/locales/ko/settings.json b/public/locales/ko/settings.json index c8903e26b..983f77169 100644 --- a/public/locales/ko/settings.json +++ b/public/locales/ko/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "이 작업은 변경 사항을 적용하기 위해 재시작이 필요합니다", "action-restart-copy": "선호 설정이 저장되어 다음에 Tari Universe가 시작될 때 적용됩니다.", "app-restart-required": "앱 재시작 필요", + "application-info": "Application Information", "applyInviteCode": "초대 코드 적용", "cancel": "취소", "change-language": "언어", diff --git a/public/locales/pl/settings.json b/public/locales/pl/settings.json index d97d82281..c55b506e4 100644 --- a/public/locales/pl/settings.json +++ b/public/locales/pl/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "Ta akcja wymaga ponownego uruchomienia, aby wprowadzić zmiany", "action-restart-copy": "Twoje preferencje zostaną zapisane i zastosowane przy następnym uruchomieniu Tari Universe.", "app-restart-required": "Wymagany restart aplikacji", + "application-info": "Application Information", "applyInviteCode": "Zastosuj Kod Zaproszenia", "cancel": "Anuluj", "change-language": "Zmień język", diff --git a/public/locales/ru/settings.json b/public/locales/ru/settings.json index 904e4c108..0d6c57508 100644 --- a/public/locales/ru/settings.json +++ b/public/locales/ru/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "Для применения изменений требуется перезапуск", "action-restart-copy": "Ваши предпочтения будут сохранены и применены при следующем запуске Tari Universe.", "app-restart-required": "Требуется перезапуск приложения", + "application-info": "Application Information", "applyInviteCode": "Применить код приглашения", "cancel": "Отмена", "change-language": "Язык", diff --git a/public/locales/tr/settings.json b/public/locales/tr/settings.json index e43dc22b2..615769463 100644 --- a/public/locales/tr/settings.json +++ b/public/locales/tr/settings.json @@ -2,6 +2,7 @@ "action-requires-restart": "Bu işlem değişikliklerin uygulanması için yeniden başlatma gerektirir", "action-restart-copy": "Tercihiniz kaydedilecek ve bir sonraki Tari Universe başlatıldığında uygulanacaktır.", "app-restart-required": "Uygulama yeniden başlatılması gerekiyor", + "application-info": "Application Information", "applyInviteCode": "Davet Kodunu Uygula", "cancel": "İptal", "change-language": "Dil Değiştir", diff --git a/src-tauri/src/app_config.rs b/src-tauri/src/app_config.rs index a3933926c..25916f463 100644 --- a/src-tauri/src/app_config.rs +++ b/src-tauri/src/app_config.rs @@ -5,6 +5,7 @@ use sys_locale::get_locale; use crate::credential_manager::CredentialManager; use crate::{consts::DEFAULT_MONERO_ADDRESS, internal_wallet::generate_password}; use anyhow::anyhow; +use chrono::{DateTime, Utc}; use log::{debug, info, warn}; use monero_address_creator::network::Mainnet; use monero_address_creator::Seed as MoneroSeed; @@ -186,6 +187,7 @@ pub struct GpuThreads { pub(crate) struct AppConfig { config_version: u32, config_file: Option, + created_at: Option>, mode: MiningMode, display_mode: DisplayMode, auto_mining: bool, @@ -226,6 +228,7 @@ impl AppConfig { Self { config_version: default_version(), config_file: None, + created_at: None, mode: MiningMode::Eco, display_mode: DisplayMode::Light, auto_mining: true, @@ -269,6 +272,7 @@ impl AppConfig { if file.exists() { debug!(target: LOG_TARGET, "Loading app config from file: {:?}", file); let config = fs::read_to_string(&file).await?; + self.created_at = Some(file.clone().metadata()?.created()?.into()); self.apply_loaded_config(config); } else { info!(target: LOG_TARGET, "App config does not exist or is corrupt. Creating new one"); @@ -276,6 +280,10 @@ impl AppConfig { self.monero_address = address; self.monero_address_is_generated = true; } + + if self.update_config_file().await.is_ok() { + self.created_at = Some(file.clone().metadata()?.created()?.into()); + } } self.update_config_file().await?; Ok(()) diff --git a/src/containers/floating/Settings/sections/general/AppDataSettings.tsx b/src/containers/floating/Settings/sections/general/AppDataSettings.tsx new file mode 100644 index 000000000..6244c0531 --- /dev/null +++ b/src/containers/floating/Settings/sections/general/AppDataSettings.tsx @@ -0,0 +1,33 @@ +import { IconButton } from '@app/components/elements/buttons/IconButton'; +import { Stack } from '@app/components/elements/Stack'; +import { Typography } from '@app/components/elements/Typography'; +import { useCopyToClipboard } from '@app/hooks/helpers/useCopyToClipboard'; +import { useAppConfigStore } from '@app/store/useAppConfigStore'; +import { useTranslation } from 'react-i18next'; +import { IoCheckmarkOutline, IoCopyOutline } from 'react-icons/io5'; +import { SettingsGroupContent, SettingsGroupTitle } from '../../components/SettingsGroup.styles.ts'; + +export default function AppDataSettings() { + const { t } = useTranslation('settings'); + const { isCopied, copyToClipboard } = useCopyToClipboard(); + const anon_id = useAppConfigStore((s) => s.anon_id); + + return ( + <> + + {t('application-info')} + + {anon_id && ( + + + {/* eslint-disable-next-line i18next/no-literal-string */} + Anon ID: {anon_id} + copyToClipboard(anon_id)} size="small"> + {!isCopied ? : } + + + + )} + + ); +} diff --git a/src/containers/floating/Settings/sections/general/GeneralSettings.tsx b/src/containers/floating/Settings/sections/general/GeneralSettings.tsx index 366d3845d..aa123ec43 100644 --- a/src/containers/floating/Settings/sections/general/GeneralSettings.tsx +++ b/src/containers/floating/Settings/sections/general/GeneralSettings.tsx @@ -1,3 +1,5 @@ +import { SettingsGroupWrapper } from '@app/containers/floating/Settings/components/SettingsGroup.styles'; +import AppDataSettings from './AppDataSettings'; import ThemeSettings from './ThemeSettings'; import AirdropPermissionSettings from './AirdropPermissionSettings.tsx'; import LogsSettings from './LogsSettings.tsx'; @@ -11,11 +13,14 @@ export const GeneralSettings = () => { <> + - - + + + + ); }; diff --git a/src/containers/floating/Settings/sections/general/LanguageSettings.tsx b/src/containers/floating/Settings/sections/general/LanguageSettings.tsx index bd48604fe..f5c87a38a 100644 --- a/src/containers/floating/Settings/sections/general/LanguageSettings.tsx +++ b/src/containers/floating/Settings/sections/general/LanguageSettings.tsx @@ -1,8 +1,10 @@ -import React from 'react'; +import { Stack } from '@app/components/elements/Stack'; import { useTranslation } from 'react-i18next'; import { Typography } from '@app/components/elements/Typography.tsx'; import LanguageDropdown from '../../components/LanguageDropdown.tsx'; import { + SettingsGroup, + SettingsGroupAction, SettingsGroupContent, SettingsGroupTitle, SettingsGroupWrapper, @@ -18,26 +20,29 @@ export default function LanguageSettings() { })); return ( - - - - {t('change-language')} - - - - - - - - {t('should-use-system-language')} - - - setShouldAlwaysUseSystemLanguage(event.target.checked)} - /> - - - + + + {t('change-language')} + + + + + {t('should-use-system-language')} + + + + setShouldAlwaysUseSystemLanguage(event.target.checked)} + /> + + + + + + + + + ); } diff --git a/src/containers/phase/Setup/components/Footer.tsx b/src/containers/phase/Setup/components/Footer.tsx index d59ca3de5..3a1ce9555 100644 --- a/src/containers/phase/Setup/components/Footer.tsx +++ b/src/containers/phase/Setup/components/Footer.tsx @@ -1,11 +1,18 @@ import Lottie from 'react-lottie'; import SetupProgress from './SetupProgress'; +import { useAppConfigStore } from '@app/store/useAppConfigStore'; import { Container, LottieWrapper, StatusWrapper } from './Footer.styles'; import animationData from './lil-soon-cookies.json'; import AirdropPermission from './AirdropPermission/AirdropPermission'; export default function Footer() { + const created_at = useAppConfigStore((s) => s.created_at); + const now = new Date(); + const config_creation_date = created_at ? new Date(created_at) : null; + + const diff = config_creation_date ? now.getTime() - config_creation_date.getTime() : 0; + const isFirstLoad = diff > 0 && diff < 1000 * 60; // 1 min buffer const defaultOptions = { loop: true, autoplay: true, @@ -14,6 +21,7 @@ export default function Footer() { preserveAspectRatio: 'xMidYMid slice', }, }; + return ( @@ -22,7 +30,7 @@ export default function Footer() { - + {isFirstLoad ? : null} ); } diff --git a/src/containers/phase/Setup/components/Permissions.tsx b/src/containers/phase/Setup/components/Permissions.tsx deleted file mode 100644 index ce3b64ede..000000000 --- a/src/containers/phase/Setup/components/Permissions.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ToggleSwitch } from '@app/components/elements/ToggleSwitch'; -import { Typography } from '@app/components/elements/Typography'; -import { useAppConfigStore } from '@app/store/useAppConfigStore'; -import { useCallback } from 'react'; -import { Trans, useTranslation } from 'react-i18next'; -import styled from 'styled-components'; - -const Wrapper = styled.div` - display: flex; - height: 45px; - padding: 11px 11px 11px 20px; - justify-content: center; - align-items: center; - border-radius: 60px; - backdrop-filter: blur(4px); - border: 1px solid rgba(0, 0, 0, 0.1); - background: rgba(255, 255, 255, 0.35); - gap: 15px; - color: #000; -`; - -export default function Permissions() { - const { t } = useTranslation('airdrop'); - const allowTelemetry = useAppConfigStore((s) => s.allow_telemetry); - const setAllowTelemetry = useAppConfigStore((s) => s.setAllowTelemetry); - - const handleChange = useCallback(async () => { - await setAllowTelemetry(!allowTelemetry); - }, [allowTelemetry, setAllowTelemetry]); - - return ( - - - {t('permission.setup')} - - - - ); -} diff --git a/src/hooks/useSetUp.ts b/src/hooks/useSetUp.ts index 0a9f92259..5a4b06425 100644 --- a/src/hooks/useSetUp.ts +++ b/src/hooks/useSetUp.ts @@ -18,7 +18,7 @@ export function useSetUp() { const setCriticalError = useAppStateStore((s) => s.setCriticalError); const isAfterAutoUpdate = useAppStateStore((s) => s.isAfterAutoUpdate); const setSettingUpFinished = useAppStateStore((s) => s.setSettingUpFinished); - const setSeenPermissions = useAirdropStore((s) => s.setSeenPermissions); + const fetchApplicationsVersionsWithRetry = useAppStateStore((s) => s.fetchApplicationsVersionsWithRetry); const syncedAidropWithBackend = useAirdropStore((s) => s.syncedWithBackend); @@ -57,7 +57,7 @@ export function useSetUp() { setSetupDetails(p.title, p.title_params, p.progress); } if (p.progress >= 1) { - handlePostSetup().finally(() => setSeenPermissions(true)); + handlePostSetup(); } break; default: @@ -82,7 +82,6 @@ export function useSetUp() { handlePostSetup, isAfterAutoUpdate, setCriticalError, - setSeenPermissions, setSetupDetails, adminShow, ]); diff --git a/src/store/useAirdropStore.ts b/src/store/useAirdropStore.ts index c2e4c6585..7c0b3ca8e 100644 --- a/src/store/useAirdropStore.ts +++ b/src/store/useAirdropStore.ts @@ -136,7 +136,6 @@ interface AirdropState { bonusTiers?: BonusTier[]; referralQuestPoints?: ReferralQuestPoints; miningRewardPoints?: MiningPoint; - seenPermissions: boolean; } interface AirdropStore extends AirdropState { @@ -150,14 +149,12 @@ interface AirdropStore extends AirdropState { setReferralCount: (referralCount: ReferralCount) => void; setFlareAnimationType: (flareAnimationType?: AnimationType) => void; setBonusTiers: (bonusTiers: BonusTier[]) => void; - setSeenPermissions: (seenPermissions: boolean) => void; setUserGems: (userGems: number) => void; logout: () => Promise; } const initialState: AirdropState = { authUuid: '', - seenPermissions: false, syncedWithBackend: false, }; @@ -221,7 +218,6 @@ export const useAirdropStore = create()( return backendInMemoryConfig; }, setMiningRewardPoints: (miningRewardPoints) => set({ miningRewardPoints, flareAnimationType: 'BonusGems' }), - setSeenPermissions: (seenPermissions) => set({ seenPermissions }), logout: async () => { set(clearState); @@ -234,11 +230,7 @@ export const useAirdropStore = create()( airdropTokens: s.airdropTokens, miningRewardPoints: s.miningRewardPoints, referralQuestPoints: s.referralQuestPoints, - seenPermissions: s.seenPermissions, }), } ) ); -useAirdropStore - .getState() - .setSeenPermissions(useAirdropStore.getState().seenPermissions || initialState.seenPermissions); // https://zustand.docs.pmnd.rs/migrations/migrating-to-v5#persist-middlware-no-longer-stores-item-at-store-creation diff --git a/src/types/app-status.ts b/src/types/app-status.ts index 34c3057f2..8b6b402b1 100644 --- a/src/types/app-status.ts +++ b/src/types/app-status.ts @@ -36,6 +36,7 @@ export interface AppConfig { use_tor: boolean; visual_mode: boolean; monero_address_is_generated?: boolean; + created_at: string; } export enum ExternalDependencyStatus {