新增景区排队管理功能

This commit is contained in:
2026-07-07 15:32:25 +08:00
parent 854a66689f
commit 0aa8b14e1f
20 changed files with 4642 additions and 1 deletions

View File

@ -34,6 +34,12 @@ final class AppStore {
static let lastLocationReportTime = "key_last_location_report_time"
static let locationReminderMinutes = "key_location_reminder_minutes"
static let isOpenReceiveVoice = "key_is_open_receive_voice"
static let scenicQueuePunchSpotId = "_scenic_queue_punch_spot_id"
static let scenicQueuePunchSpotName = "_scenic_queue_punch_spot_name"
static let scenicQueueRemark = "_scenic_queue_remark"
static let scenicQueueSettingsSnapshot = "_scenic_queue_settings_snapshot"
static let scenicQueueOfflineTTS = "_scenic_queue_offline_tts_v1"
static let scenicQueuePresetVoices = "_scenic_queue_preset_voices_v1"
}
private let defaults: UserDefaults
@ -161,6 +167,30 @@ final class AppStore {
set { defaults.set(newValue, forKey: accountScopedKey(Key.isOpenReceiveVoice)) }
}
/// ID
var scenicQueuePunchSpotId: String {
get { defaults.string(forKey: accountScopedKey(Key.scenicQueuePunchSpotId)) ?? "" }
set { defaults.set(newValue, forKey: accountScopedKey(Key.scenicQueuePunchSpotId)) }
}
///
var scenicQueuePunchSpotName: String {
get { defaults.string(forKey: accountScopedKey(Key.scenicQueuePunchSpotName)) ?? "" }
set { defaults.set(newValue, forKey: accountScopedKey(Key.scenicQueuePunchSpotName)) }
}
///
var scenicQueueRemark: String {
get { defaults.string(forKey: accountScopedKey(Key.scenicQueueRemark)) ?? "" }
set { defaults.set(newValue, forKey: accountScopedKey(Key.scenicQueueRemark)) }
}
/// 使线 TTS
var scenicQueueUseOfflineTTS: Bool {
get { defaults.bool(forKey: accountScopedKey(Key.scenicQueueOfflineTTS)) }
set { defaults.set(newValue, forKey: accountScopedKey(Key.scenicQueueOfflineTTS)) }
}
/// Android `getAccountCachePrefix`
var accountCachePrefix: String {
let uid = userId.trimmingCharacters(in: .whitespacesAndNewlines)
@ -306,6 +336,50 @@ final class AppStore {
defaults.removeObject(forKey: accountScopedKey(Key.lastLocationReportTime))
}
///
func hasScenicQueuePunchSpotSaved() -> Bool {
let id = scenicQueuePunchSpotId.trimmingCharacters(in: .whitespacesAndNewlines)
return Int64(id).map { $0 > 0 } ?? false
}
///
func saveScenicQueueSettingsSnapshot(_ snapshot: ScenicQueueSettingsSnapshot) {
guard let data = try? JSONEncoder().encode(snapshot) else { return }
defaults.set(data, forKey: accountScopedKey(Key.scenicQueueSettingsSnapshot))
}
///
func scenicQueueSettingsSnapshot() -> ScenicQueueSettingsSnapshot? {
guard let data = defaults.data(forKey: accountScopedKey(Key.scenicQueueSettingsSnapshot)) else { return nil }
return try? JSONDecoder().decode(ScenicQueueSettingsSnapshot.self, from: data)
}
/// 5
func saveScenicQueuePresetVoices(_ voices: [String]) {
let normalized = voices
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
.prefix(5)
defaults.set(Array(normalized), forKey: accountScopedKey(Key.scenicQueuePresetVoices))
}
///
func scenicQueuePresetVoices() -> [String] {
defaults.stringArray(forKey: accountScopedKey(Key.scenicQueuePresetVoices)) ?? []
}
///
func clearScenicQueueSnapshot() {
[
Key.scenicQueuePunchSpotId,
Key.scenicQueuePunchSpotName,
Key.scenicQueueRemark,
Key.scenicQueueSettingsSnapshot,
Key.scenicQueueOfflineTTS,
Key.scenicQueuePresetVoices,
].forEach { defaults.removeObject(forKey: accountScopedKey($0)) }
}
///
func clearPermissionSnapshot() {
let prefix = accountScopedKey("")
@ -317,6 +391,12 @@ final class AppStore {
Key.lastLocationReportTime,
Key.locationReminderMinutes,
Key.isOpenReceiveVoice,
Key.scenicQueuePunchSpotId,
Key.scenicQueuePunchSpotName,
Key.scenicQueueRemark,
Key.scenicQueueSettingsSnapshot,
Key.scenicQueueOfflineTTS,
Key.scenicQueuePresetVoices,
]
keys.forEach { defaults.removeObject(forKey: prefix + $0) }
}