Files
Felitendo c59ce5b955 feat(admin): warn at 80% storage, and say when somebody posts
Three switches, each its own: an operator who wants to know the disk is
filling up does not necessarily want a banner for every feature request,
and one toggle would make them take both or neither. All off until
asked for, and permission is requested when the first goes on rather
than at launch — an app that asks before it has anything to say is one
people say no to.

Storage warns at 80% and not at the cap. The cap is where downloads
stop, so being told then is being told too late; the server already logs
that crossing. It warns once and re-arms when usage drops back under, so
"80% reached" does not repeat on every check for a week.

The two boards are watched by newest id rather than by a count. A count
moves both ways as things are decided, so "is there something I have not
seen?" cannot be answered from it. A first run records where things
stand and says nothing, since announcing the newest of what already
existed is a notification about something already read.

`/api/dashboard/alerts` is its own route rather than a field on
`status`, which is a screenful drawn while somebody is looking at it.
This one is polled in the background, where the whole cost has to be
three counters, two ids and a ratio. It decides nothing: whether a
number becomes a notification is the app's call against what it saw last
time, and a server that decided would have to know which device had
already been told.

Local notifications, not push. This app ships no credential and talks to
one server the operator configured; a push pipeline would put a third
party in the middle of that. The cost is stated in the footer rather
than hidden: a local notification can only be posted while the app runs,
so it is a nudge and not a pager.
2026-08-09 19:30:46 +02:00

77 lines
2.4 KiB
Plaintext

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>FTStoreAdminBaseURL</key>
<string>$(FTSTORE_ADMIN_BASE_URL)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>app.ftstore.admin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ftstore-admin</string>
</array>
</dict>
</array>
<!-- Imported, not exported: FTStore owns `app.ftstore.ipa`, the console only
reads one. Without a declaration the system has no type for the `.ipa`
extension, `UTType(filenameExtension:)` falls back to a dynamic `dyn.…`
identifier, and the upload screen's file importer greys out every IPA on
the device. -->
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>com.apple.itunes.ipa</string>
<string>public.data</string>
<string>public.content</string>
</array>
<key>UTTypeDescription</key>
<string>iOS Package Archive</string>
<key>UTTypeIcons</key>
<dict>
<key>UTTypeIconText</key>
<string>IPA</string>
</dict>
<key>UTTypeIdentifier</key>
<string>app.ftstore.ipa</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>ipa</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/octet-stream</string>
</array>
</dict>
</dict>
</array>
<key>NSCameraUsageDescription</key>
<string>Take a photo to use as a tweak's icon in the FTStore Admin console.</string>
<key>NSFaceIDUsageDescription</key>
<string>Face ID unlocks the FTStore Admin console on this device.</string>
<!-- The console posts local notifications for storage, feedback and tweak
requests (`AdminAlertWatcher`). A local notification can only be posted
while the app is running, so the background refresh is what lets it say
anything when the console is not open. Opportunistic by design: iOS
decides when, and may decide "not for hours". -->
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>app.ftstore.admin.alerts</string>
</array>
<key>UILaunchScreen</key>
<dict/>
</dict>
</plist>