mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-06-06 23:13:58 +02:00
refactor: add cleanup for expired subscriptions
This commit is contained in:
@@ -41,6 +41,10 @@ export function ProfileSyncDialog({
|
||||
cloudUser.plan !== "free" &&
|
||||
(cloudUser.subscriptionStatus === "active" ||
|
||||
cloudUser.planPeriod === "lifetime");
|
||||
const canUseEncryption =
|
||||
isCloudSyncEligible &&
|
||||
cloudUser != null &&
|
||||
(cloudUser.plan !== "team" || cloudUser.teamRole === "owner");
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [isSyncing, setIsSyncing] = useState(false);
|
||||
const [syncMode, setSyncMode] = useState<SyncMode>(
|
||||
@@ -92,6 +96,11 @@ export function ProfileSyncDialog({
|
||||
return;
|
||||
}
|
||||
|
||||
if (newMode === "Encrypted" && !canUseEncryption) {
|
||||
showErrorToast(t("settings.encryption.requiresProOrOwner"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newMode === "Encrypted" && !hasE2ePassword) {
|
||||
showErrorToast(t("sync.mode.passwordRequired"));
|
||||
return;
|
||||
@@ -116,7 +125,15 @@ export function ProfileSyncDialog({
|
||||
setIsSaving(false);
|
||||
}
|
||||
},
|
||||
[profile, hasConfig, hasE2ePassword, onSyncConfigOpen, onClose, t],
|
||||
[
|
||||
profile,
|
||||
hasConfig,
|
||||
hasE2ePassword,
|
||||
canUseEncryption,
|
||||
onSyncConfigOpen,
|
||||
onClose,
|
||||
t,
|
||||
],
|
||||
);
|
||||
|
||||
const handleSyncNow = useCallback(async () => {
|
||||
@@ -225,16 +242,32 @@ export function ProfileSyncDialog({
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-3">
|
||||
<RadioGroupItem value="Encrypted" id="sync-encrypted" />
|
||||
<Label htmlFor="sync-encrypted" className="cursor-pointer">
|
||||
<RadioGroupItem
|
||||
value="Encrypted"
|
||||
id="sync-encrypted"
|
||||
disabled={!canUseEncryption}
|
||||
/>
|
||||
<Label
|
||||
htmlFor="sync-encrypted"
|
||||
className={
|
||||
canUseEncryption
|
||||
? "cursor-pointer"
|
||||
: "cursor-not-allowed opacity-50"
|
||||
}
|
||||
>
|
||||
<span className="font-medium">
|
||||
{t("sync.mode.encrypted", "E2E Encrypted Sync")}
|
||||
</span>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t(
|
||||
"sync.mode.encryptedDescription",
|
||||
"Encrypted before upload. Server never sees plaintext data.",
|
||||
)}
|
||||
{canUseEncryption
|
||||
? t(
|
||||
"sync.mode.encryptedDescription",
|
||||
"Encrypted before upload. Server never sees plaintext data.",
|
||||
)
|
||||
: t(
|
||||
"settings.encryption.requiresProOrOwner",
|
||||
"Profile encryption is available for Pro users and team owners.",
|
||||
)}
|
||||
</p>
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { useCloudAuth } from "@/hooks/use-cloud-auth";
|
||||
import { useCommercialTrial } from "@/hooks/use-commercial-trial";
|
||||
import { useLanguage } from "@/hooks/use-language";
|
||||
import type { PermissionType } from "@/hooks/use-permissions";
|
||||
@@ -129,6 +130,13 @@ export function SettingsDialog({
|
||||
isCameraAccessGranted,
|
||||
} = usePermissions();
|
||||
const { trialStatus } = useCommercialTrial();
|
||||
const { user: cloudUser } = useCloudAuth();
|
||||
const canUseEncryption =
|
||||
cloudUser != null &&
|
||||
cloudUser.plan !== "free" &&
|
||||
(cloudUser.subscriptionStatus === "active" ||
|
||||
cloudUser.planPeriod === "lifetime") &&
|
||||
(cloudUser.plan !== "team" || cloudUser.teamRole === "owner");
|
||||
const {
|
||||
currentLanguage,
|
||||
changeLanguage,
|
||||
@@ -853,7 +861,14 @@ export function SettingsDialog({
|
||||
)}
|
||||
</p>
|
||||
|
||||
{hasE2ePassword ? (
|
||||
{!canUseEncryption ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t(
|
||||
"settings.encryption.requiresProOrOwner",
|
||||
"Profile encryption is available for Pro users and team owners.",
|
||||
)}
|
||||
</p>
|
||||
) : hasE2ePassword ? (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="default">
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"removed": "Encryption password removed",
|
||||
"passwordSaved": "Encryption password set",
|
||||
"passwordMismatch": "Passwords do not match",
|
||||
"passwordTooShort": "Password must be at least 8 characters"
|
||||
"passwordTooShort": "Password must be at least 8 characters",
|
||||
"requiresProOrOwner": "Profile encryption is available for Pro users and team owners."
|
||||
},
|
||||
"commercial": {
|
||||
"title": "Commercial License",
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"removed": "Contraseña de cifrado eliminada",
|
||||
"passwordSaved": "Contraseña de cifrado establecida",
|
||||
"passwordMismatch": "Las contraseñas no coinciden",
|
||||
"passwordTooShort": "La contraseña debe tener al menos 8 caracteres"
|
||||
"passwordTooShort": "La contraseña debe tener al menos 8 caracteres",
|
||||
"requiresProOrOwner": "El cifrado de perfiles está disponible para usuarios Pro y propietarios de equipos."
|
||||
},
|
||||
"commercial": {
|
||||
"title": "Licencia Comercial",
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"removed": "Mot de passe de chiffrement supprimé",
|
||||
"passwordSaved": "Mot de passe de chiffrement défini",
|
||||
"passwordMismatch": "Les mots de passe ne correspondent pas",
|
||||
"passwordTooShort": "Le mot de passe doit contenir au moins 8 caractères"
|
||||
"passwordTooShort": "Le mot de passe doit contenir au moins 8 caractères",
|
||||
"requiresProOrOwner": "Le chiffrement des profils est disponible pour les utilisateurs Pro et les propriétaires d'équipe."
|
||||
},
|
||||
"commercial": {
|
||||
"title": "Licence commerciale",
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"removed": "暗号化パスワードが削除されました",
|
||||
"passwordSaved": "暗号化パスワードが設定されました",
|
||||
"passwordMismatch": "パスワードが一致しません",
|
||||
"passwordTooShort": "パスワードは8文字以上である必要があります"
|
||||
"passwordTooShort": "パスワードは8文字以上である必要があります",
|
||||
"requiresProOrOwner": "プロファイルの暗号化はProユーザーとチームオーナーのみ利用できます。"
|
||||
},
|
||||
"commercial": {
|
||||
"title": "商用ライセンス",
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"removed": "Senha de criptografia removida",
|
||||
"passwordSaved": "Senha de criptografia definida",
|
||||
"passwordMismatch": "As senhas não coincidem",
|
||||
"passwordTooShort": "A senha deve ter pelo menos 8 caracteres"
|
||||
"passwordTooShort": "A senha deve ter pelo menos 8 caracteres",
|
||||
"requiresProOrOwner": "A criptografia de perfis está disponível para usuários Pro e proprietários de equipe."
|
||||
},
|
||||
"commercial": {
|
||||
"title": "Licença Comercial",
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"removed": "Пароль шифрования удалён",
|
||||
"passwordSaved": "Пароль шифрования установлен",
|
||||
"passwordMismatch": "Пароли не совпадают",
|
||||
"passwordTooShort": "Пароль должен содержать не менее 8 символов"
|
||||
"passwordTooShort": "Пароль должен содержать не менее 8 символов",
|
||||
"requiresProOrOwner": "Шифрование профилей доступно для пользователей Pro и владельцев команд."
|
||||
},
|
||||
"commercial": {
|
||||
"title": "Коммерческая лицензия",
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"removed": "加密密码已删除",
|
||||
"passwordSaved": "加密密码已设置",
|
||||
"passwordMismatch": "密码不匹配",
|
||||
"passwordTooShort": "密码必须至少8个字符"
|
||||
"passwordTooShort": "密码必须至少8个字符",
|
||||
"requiresProOrOwner": "配置文件加密仅适用于Pro用户和团队所有者。"
|
||||
},
|
||||
"commercial": {
|
||||
"title": "商业许可",
|
||||
|
||||
Reference in New Issue
Block a user