Ariver
2026-07-02 d77d35981ccd3f64c34727b8d21ac599012007bb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env bash
set -euo pipefail
 
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
 
python3 - "$ROOT_DIR" <<'PY'
import json
import re
import sys
from pathlib import Path
 
root = Path(sys.argv[1])
app = root / "Apptag"
preferences = app / "PreferencesView.swift"
pro = app / "ProEntitlement.swift"
l10n = app / "L10n.swift"
localization_dir = app / "Localization"
 
 
def fail(message: str) -> None:
    raise SystemExit(f"FAIL: {message}")
 
 
def read(path: Path) -> str:
    try:
        return path.read_text(encoding="utf-8")
    except Exception as error:
        fail(f"could not read {path}: {error}")
 
 
def function_body(source: str, signature: str) -> str:
    start = source.find(signature)
    if start == -1:
        fail(f"missing function signature: {signature}")
    brace = source.find("{", start)
    if brace == -1:
        fail(f"missing function body for: {signature}")
    depth = 0
    for index in range(brace, len(source)):
        char = source[index]
        if char == "{":
            depth += 1
        elif char == "}":
            depth -= 1
            if depth == 0:
                return source[brace + 1:index]
    fail(f"unterminated function body for: {signature}")
 
 
preferences_text = read(preferences)
pro_text = read(pro)
l10n_text = read(l10n)
 
required_pro = [
    "func remainingSeconds(at date: Date = Date()) -> Int",
    "ceil(endsAt.timeIntervalSince(date))",
    "max(0, Int(",
    "struct ProDisplayModePreviewState: Equatable",
    "func startDisplayModePreview(for displayMode: String)",
    "endsAt: now.addingTimeInterval(ProEntitlementConfig.themePreviewDuration)",
    "func stopDisplayModePreview()",
]
missing_pro = [needle for needle in required_pro if needle not in pro_text]
if missing_pro:
    fail("ProThemePreviewState countdown helper is incomplete: " + " | ".join(missing_pro))
 
required_preferences = [
    "themePreviewCountdownTimer",
    "@State private var themePreviewNow = Date()",
    "pro.theme.status.previewCountdown",
    ".replacingOccurrences(of: \"%time%\", with: countdown)",
    ".countdown(text: themePreviewCountdownText ?? \"00:00\")",
    "proEntitlement.displayModePreviewState",
    "formatThemePreviewCountdown(seconds:",
    "String(format: \"%02d:%02d\"",
    "proEntitlement.stopThemePreview()",
    "proEntitlement.stopDisplayModePreview()",
    ".font(.system(size: 10, weight: .semibold, design: .monospaced))",
]
missing_preferences = [needle for needle in required_preferences if needle not in preferences_text]
if missing_preferences:
    fail("Preferences countdown UI is incomplete: " + " | ".join(missing_preferences))
 
accessory_body = function_body(preferences_text, "private func themeOptionAccessory(for theme: AppGridTheme) -> ThemeOptionAccessory")
if "pro.card.previewBadge" in accessory_body:
    fail("current preview theme card must show MM:SS, not the old Preview badge")
 
codes_match = re.search(
    r"static\s+let\s+supported\s*:\s*\[\(code:\s*String,\s*name:\s*String\)\]\s*=\s*\[(.*?)\n\s*\]",
    l10n_text,
    re.S,
)
if not codes_match:
    fail("could not find L10n.supported")
codes = re.findall(r'\("([^"]+)",\s*"[^"]+"\)', codes_match.group(1))
if len(codes) != 29:
    fail(f"expected 29 languages, got {len(codes)}")
 
for code in codes:
    path = localization_dir / f"{code}.json"
    if not path.exists():
        fail(f"missing localization file: {path}")
    data = json.loads(path.read_text(encoding="utf-8"))
    value = data.get("pro.theme.status.previewCountdown", "")
    if not isinstance(value, str) or not value.strip():
        fail(f"{path.name} missing pro.theme.status.previewCountdown")
    if "%time%" not in value:
        fail(f"{path.name} countdown copy lost %time% placeholder")
    if "5" in value or "five" in value.lower():
        fail(f"{path.name} countdown copy must not hardcode preview duration: {value!r}")
 
print("PASS Pro theme preview countdown QA: UI timer, MM:SS card accessory, expiry cleanup, and 29-language placeholder coverage are wired")
PY