Files
Felitendo e0b2692e83 docs(admin): call it FTStore Admin, not "the console"
The app is FTStore Admin. Its own AGENTS.md already sets the vocabulary — "the
FTStore Admin server" for web/, "the FTStore Admin app" for this one — and then
most of the comments in it said "the console" anyway.

Three user-visible strings went with it: the lock screen, the menu item that
locks it, and the biometric prompt. The storage-notification footer named the
console too. Their catalog keys move with them, in every locale the app ships.

Not "the app": inside these files that already means FTStore on somebody's
phone, so a blind swap produced lines like "Dashboard and app only, never the
app".

AdminConsoleView is AdminHomeView. The Console/ directory keeps its name: the
history of Console/*.swift runs back through manager/FTStore/Admin/ and is
documented as doing so, which is worth more than a path nobody reads as a name.

Also fills a translation gap this turned up — the status-change footer in the
feedback detail was untranslated.
2026-08-10 17:54:46 +02:00

71 lines
2.2 KiB
Swift

//
// AnyCodable.swift
// FTStore Admin
//
// Created by samara on 27.04.2025.
//
// Copied from FTStore's engine (`manager/FTStore/Utilities/`). FTStore Admin
// decodes a few open-ended server payloads through it — entitlements, privacy
// manifests, the settings map — and copying fifty lines is cheaper than
// linking an engine into an app that signs and installs nothing.
//
import Foundation
struct AnyCodable: Codable {
let value: Any
init(_ value: Any) {
self.value = value
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() {
self.value = NSNull()
} else if let bool = try? container.decode(Bool.self) {
self.value = bool
} else if let int = try? container.decode(Int.self) {
self.value = int
} else if let uint = try? container.decode(UInt.self) {
self.value = uint
} else if let double = try? container.decode(Double.self) {
self.value = double
} else if let string = try? container.decode(String.self) {
self.value = string
} else if let array = try? container.decode([AnyCodable].self) {
self.value = array.map { $0.value }
} else if let dictionary = try? container.decode([String: AnyCodable].self) {
self.value = dictionary.mapValues { $0.value }
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyCodable cannot decode value")
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self.value {
case is NSNull:
try container.encodeNil()
case let bool as Bool:
try container.encode(bool)
case let int as Int:
try container.encode(int)
case let uint as UInt:
try container.encode(uint)
case let double as Double:
try container.encode(double)
case let string as String:
try container.encode(string)
case let array as [Any]:
try container.encode(array.map { AnyCodable($0) })
case let dictionary as [String: Any]:
try container.encode(dictionary.mapValues { AnyCodable($0) })
default:
throw EncodingError.invalidValue(self.value, EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyCodable cannot encode value"))
}
}
}