refactor: add cleanup for expired subscriptions

This commit is contained in:
zhom
2026-03-02 18:49:47 +04:00
parent 3cb68c53ad
commit 23d25928fc
13 changed files with 247 additions and 17 deletions
+40 -7
View File
@@ -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>
+16 -1
View File
@@ -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">
+2 -1
View File
@@ -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",
+2 -1
View File
@@ -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",
+2 -1
View File
@@ -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",
+2 -1
View File
@@ -120,7 +120,8 @@
"removed": "暗号化パスワードが削除されました",
"passwordSaved": "暗号化パスワードが設定されました",
"passwordMismatch": "パスワードが一致しません",
"passwordTooShort": "パスワードは8文字以上である必要があります"
"passwordTooShort": "パスワードは8文字以上である必要があります",
"requiresProOrOwner": "プロファイルの暗号化はProユーザーとチームオーナーのみ利用できます。"
},
"commercial": {
"title": "商用ライセンス",
+2 -1
View File
@@ -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",
+2 -1
View File
@@ -120,7 +120,8 @@
"removed": "Пароль шифрования удалён",
"passwordSaved": "Пароль шифрования установлен",
"passwordMismatch": "Пароли не совпадают",
"passwordTooShort": "Пароль должен содержать не менее 8 символов"
"passwordTooShort": "Пароль должен содержать не менее 8 символов",
"requiresProOrOwner": "Шифрование профилей доступно для пользователей Pro и владельцев команд."
},
"commercial": {
"title": "Коммерческая лицензия",
+2 -1
View File
@@ -120,7 +120,8 @@
"removed": "加密密码已删除",
"passwordSaved": "加密密码已设置",
"passwordMismatch": "密码不匹配",
"passwordTooShort": "密码必须至少8个字符"
"passwordTooShort": "密码必须至少8个字符",
"requiresProOrOwner": "配置文件加密仅适用于Pro用户和团队所有者。"
},
"commercial": {
"title": "商业许可",