From 01bab5422712299b49f656e6b4c42a3bf314f519 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 02 Jul 2026 11:02:42 +0800
Subject: [PATCH] Release 8.3.5 free tag order persistence
---
src/Scripts/pro_custom_container_quota_qa.sh | 45 +++++++++++++++++++++++++++++++++------------
1 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/src/Scripts/pro_custom_container_quota_qa.sh b/src/Scripts/pro_custom_container_quota_qa.sh
index 7c7225f..2c69e92 100755
--- a/src/Scripts/pro_custom_container_quota_qa.sh
+++ b/src/Scripts/pro_custom_container_quota_qa.sh
@@ -26,6 +26,25 @@
fail(message)
+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}")
+
+
pro = read("ProEntitlement.swift")
data_layer = read("DataLayer.swift")
tag_editor = read("TagEditorView.swift")
@@ -57,7 +76,9 @@
require(data_layer, "static func moveApp(path: String, from sourceTag: String, to targetTag: String, color: Int, copy: Bool) -> TagEditorMutationResult", "moveApp must return a mutation result")
require(data_layer, "TagDatabase.markCustomMaintainedIfNeeded", "membership changes must mark maintained system tags")
require(data_layer, "static func reorderTags(_ names: [String]) -> Bool", "tag order persistence must return whether it saved")
-require(data_layer, "guard ProEntitlementPolicy.isUnlocked(.persistentAppSorting) else { return false }", "tag order persistence must be Pro-gated")
+reorder_tags_body = function_body(data_layer, "static func reorderTags(_ names: [String]) -> Bool")
+if "ProEntitlementPolicy.isUnlocked(.persistentAppSorting)" in reorder_tags_body:
+ fail("tag list order persistence must be available to free users")
require(tag_editor, "onCustomContainerQuotaExceeded", "TagEditorView must expose quota-blocked callback")
require(tag_editor, "onCustomContainerQuotaStatusChanged", "TagEditorView must expose quota status refresh callback")
@@ -66,8 +87,10 @@
require(tag_editor, "guard handleMutationResult(result) else { return }", "TagEditorView must not update local state before a save succeeds")
require(tag_editor, "blockedCustomContainerQuota", "TagEditorView must surface quota blocks")
-require(content, "persistTagOrderIfAllowed()", "ContentView must route tag order save through a Pro gate")
-require(content, "pro.tagSorting.previewToast", "free tag sorting preview toast is missing")
+require(content, "persistTagOrder()", "ContentView must persist tag list order")
+persist_tag_order_body = function_body(content, "private func persistTagOrder()")
+if "proEntitlement.isUnlocked" in persist_tag_order_body or "pro.tagSorting.previewToast" in persist_tag_order_body:
+ fail("free users must be able to persist tag list order without a Pro preview toast")
require(content, "presentCustomContainerQuotaPrompt", "ContentView must show custom container quota prompt")
require(content, "customContainerQuotaAccessory", "ContentView tag editor must show quota count")
require(content, "@State private var customContainerQuotaStatus = ProEntitlementPolicy.customContainerQuotaStatus()", "ContentView quota count must be backed by SwiftUI state")
@@ -84,19 +107,19 @@
require(preferences, "onCustomContainerQuotaStatusChanged: refreshSettingsCustomContainerQuotaStatus", "Settings must refresh quota count after TagEditorView saves")
require(preferences, "settingsCustomContainerQuotaOverviewStatus = quotaStatus", "Settings scanApps must initialize quota count from the same store")
require(preferences, 'title: tr("pro.feature.maintainableTagCount")', "Pro comparison must include maintainable tag count row")
-require(preferences, 'title: tr("pro.feature.tagOrderPersistence")', "Pro comparison must include tag order row")
+if 'title: tr("pro.feature.tagOrderPersistence")' in preferences:
+ fail("Pro comparison must not list tag list order; it is available to free users")
require(preferences, 'title: tr("pro.feature.containerAppOrderPersistence")', "Pro comparison must include container app order row")
-first_three = [
+first_two = [
preferences.find('title: tr("pro.feature.maintainableTagCount")'),
- preferences.find('title: tr("pro.feature.tagOrderPersistence")'),
preferences.find('title: tr("pro.feature.containerAppOrderPersistence")'),
]
custom_color_index = preferences.find('title: tr("pro.feature.customTagColors")')
-if any(index == -1 for index in first_three) or custom_color_index == -1:
+if any(index == -1 for index in first_two) or custom_color_index == -1:
fail("could not locate Pro comparison feature rows")
-if not (first_three[0] < first_three[1] < first_three[2] < custom_color_index):
- fail("new Pro comparison rows must be the first three rows")
+if not (first_two[0] < first_two[1] < custom_color_index):
+ fail("Pro comparison rows must begin with tag quota and container app order")
required_l10n = [
"pro.feature.customContainerQuota",
@@ -106,9 +129,7 @@
"pro.customContainers.quotaUnlimitedShort",
"pro.customContainers.quotaPrompt",
"pro.customContainers.manageExisting",
- "pro.tagSorting.previewToast",
"pro.feature.maintainableTagCount",
- "pro.feature.tagOrderPersistence",
"pro.feature.containerAppOrderPersistence",
"settings.proStatus.maxSixTags",
"settings.proStatus.unlimitedTags",
@@ -139,5 +160,5 @@
if zh_hans.get("settings.proStatus.unlimitedTags") != "✅没有限制":
fail("zh-Hans unlimited tag status must include the approved leading checkmark")
-print("PASS Pro custom container quota QA: quota gate, tag-order preview, Pro table rows, and 29-language keys are wired")
+print("PASS Pro custom container quota QA: quota gate, free tag-order persistence, Pro table rows, and 29-language keys are wired")
PY
--
Gitblit v1.9.3