Hey everyone! StoryPad 2.33.0 is now live. This release is all about video, more backup options, and a smoother, more reliable app. Here’s what’s new:
Video support
- Add video support: capture, playback, and a unified media viewer (new
AssetType.video,SpMediaTile,SpMediaViewerreplacingSpImagesViewer/SpVideoPlayerView, consolidated “Add media” action sheet, “Record Video” FAB action). - Gate media uploads behind a Wi-Fi/cellular setting (“Media Upload”: Wi-Fi Only / Wi-Fi & Cellular), defaulting to Wi-Fi & Cellular.
- Compress videos at pick time and stop buffering them in memory (
video_compress, file-to-file copy instead of reading intoUint8List). - Fail the video viewer page gracefully when a file can’t be decoded, instead of spinning forever.
- Extract media dimensions at pick time, not insert time (
PickedMediaObject). - Remove video button from FAB to avoid too many actions; add “Record Video” to quick actions instead.
- Close video integration gaps across storage, import/export, stats, and export (tar import, Cloud Optimize, Storage Management, Export Assets stats, Stats/home recap counts, analytics, markdown/text export, iOS/macOS permission strings; removed unused
SpVideoDemoSheet). - Rename the image embed to
media, with legacyimageembeds still supported for backward compatibility. - Fix copilot review comments on the video-support PR.
- Keep picked video at 1080p (
Res1920x1080Quality) instead of a blurry fixed-bitrate preset, and show a real determinate compression progress screen with cancel. - Fix a
showLoadingbug (found while wiring up the compression screen) where a rebuilt dialog chained an extraNavigator.pop, occasionally popping the whole root navigator to a black screen. - Use the native Android Photo Picker for media selection instead of the generic system file picker.
Multi-cloud backup (Nextcloud)
- Add Nextcloud support as a backup destination.
- Add customizable Nextcloud storage folder and per-service configuration display on the connected-service detail screen.
- Delete assets from every connected cloud service, not just Google Drive.
- Fix asset display/download/export/status to work with any connected cloud service (replace
GoogleDriveAssetDownloaderServicewith genericBackupAssetDownloaderService; fix Library tab’s per-asset backup-status badges). - Fix RevenueCat identity, data-loss, and stale-status bugs surfaced by multi-service review (signed-out Drive shadowing a connected Nextcloud account, local delete silently orphaning remote copies, Cupertino form error text not rendering, cross-service success/sync-status leakage).
- Fix
destinationKeycollision, single-provider download fallback, and account-switch gaps (percent-encode Nextcloud folder segment, try every matching destination on download, fixeligibleServices/globalId checks, disable server/username fields during reconnect, name the actual provider inQuotaExceptionmessages). - Allow asset delete when a cloud destination isn’t reachable, instead of requiring sign-in to every provider it was ever uploaded to.
- Label asset delete action with only the reachable cloud provider(s), not every provider ever uploaded to.
- Show where Google Drive backups are stored (info line + link to Google’s appdata folder docs) on the backup detail screen.
Maps & location
- Speed up map markers: decode marker bitmaps at marker size, cache them in memory/disk, and stop discarding completed icons on every viewport change.
- Fix map pins flickering (invisible → placeholder → gone → photo) by resolving the first image asset before the initial draw.
- Add a manual “Map Provider” setting (Google Maps / MapTiler), surfaced for mainland-China timezones since Google Maps tile blocking can’t be detected from inside the app.
- Auto-refresh map pins and stats after story edits made elsewhere, reusing the existing global story-change listener.
- Zoom the map picker in closer initially so a place name resolves without a manual zoom-in; tidy map overlay button styling.
- Proxy MapTiler tile requests through static.storypad.me via a Cloudflare Worker, so the MapTiler API key is no longer embedded in the app.
- Disable MapTiler-backed online geocoding (
SpNullGeocodingServiceused instead) as part of the tile-proxy change. - Extract the static.storypad.me base URL into a shared
kCdnBaseUrlconstant.
Firebase/CDN decoupling
- Make Firebase init non-blocking (7s timeout,
kFirebaseAvailableflag) so startup never hangs where Firebase/Google domains are blocked or throttled. - Relocate CDN asset sources into the new static.storypad.me submodule and compile the storage hash map at build time (
lib/gen/storage_hash_map.dart) instead of loading/parsing it at runtime. - Cut Remote Config and Cloud Storage over to the CDN and drop Firebase:
CdnCloudStorageAdaptoris now the sole cloud storage adaptor, newHttpRemoteConfigAdaptorfetchesremote-config.jsonwith a SharedPreferences cache; Firebase-backed adaptors andfirebase_remote_config/firebase_storagedeps removed. - Route direct
FirebaseCrashlytics.instance.log()calls (backup provider, cloud optimize) through the error-reporting adaptor so they don’t throw[core/no-app]when Firebase is unavailable. - Skip gallery template usage tracking entirely when Firebase is unavailable, instead of touching Firestore and relying on try/catch.
- Let the community flavor build without the private
private_keys/submodule, falling back to an emptydart_defines/community.json.
UI/UX improvements
- Move the story share action into the Show/Edit AppBars, dropping the redundant one from the theme bottom sheet.
- Simplify the My Templates list into a flat, dividered list (matching Tags) with a real drag handle, replacing boxed cards with a markdown preview.
- Use the cached image file before rendering for smoother image rendering during scroll.
- Guard pop-with-result callbacks against stale route pops, so one back action can no longer pop more than one stacked screen.
- Fix JSON export silently no-op’ing with zero feedback on a fresh session (
lastDbUpdatedAtstill null). - Make add-ons enabling open a sheet instead of toggling directly from the list tile, matching the reminders UX.
- Auto re-authenticate on genuine app resume (real backgrounding), not only when the lock barrier route isn’t current.
- Add a period reminder tile floating over the period calendar’s story list.
- Show combined sync step count across all signed-in services instead of resetting per service; disable the app-lock barrier during the biometric toggle’s native auth prompt.
- Cap the period calendar’s story-list top padding via
SpStoryList; hide “next reminder” hints predicted more than 60 days out. - Add a “Duplicate” action to the read-only story view, opening a pre-filled new-story edit view.
- Simplify the Templates AppBar action wiring, dropping the bubbled-up
appBarActionsLoaderCallback/notifier plumbing. - Cover the app-lock barrier instantly on
inactive(not justpaused) so real content can no longer flash during the OS app-switcher snapshot. - Replace the fonts picker’s sticky header with a flat lazy
ListView.builderto fix scroll lag.
Bug & crash fixes
- Fix untranslated reminder notification labels on cold start by moving notification init/reschedule after
easy_localizationhas loaded. - Fix a batch of Crashlytics-reported fatal crashes:
Bad state: No elementfrom.lastin a Drive helper (use.lastOrNull).Bad state: Future already completedinGoogleDriveCloudService.getFileContent(missingcancelOnError).permissionRequestInProgresscrash on rapid reminder toggle.- Crash when sharing a story with empty text and no attachments.
- Unhandled
Connection abortedinAudioPlayerService, plus a completer left permanently uncompleted. Bad state: No elementintogglePinForStorieson stale selection.Invalid argumentin the storage usage bar’s width clamp.- Unhandled avatar image load failures (Crashlytics reported as fatal).
- Unhandled Firestore error in
GalleryTemplateUsageService(now swallowed as best-effort). - Fatal font-load crash slipping past the Crashlytics ignore filter (broadened beyond
SocketException). ScrollController.offsetread crash during multi-attach (list/grid layout switch).- Spurious fatal report from an unhandled asset-download completer race.
double.clampcrash in the storage bar from negative/non-finite layout width.PlayerInterruptedExceptionfrom disposing anAudioPlayermid-setup.
- Fix
light_rain.wavnot being a perfect loop. - Stop tag reorder from being scrambled by sync (partial-year restore rebuilding tags early,
_reindexoutranking real reorders, undefined tie order on equal index) — three separate root causes fixed together.
Localization
- Sync reviewed Korean translations from a community contributor, replacing machine-translated strings with a human review covering 303 of 526 keys (fixes mistranslations and two outright bugs: Japanese text in a snackbar, and an untranslated placeholder that would never interpolate).
Dependency updates
- Bump
js-yaml4.1.1 → 4.3.0, then 4.3.0 → 4.3.1 (bin/firebase_admin). - Bump
websocket-driver0.7.4 → 0.7.5 (bin/firebase_admin, indirect).
Housekeeping
- Move docs to agent repo (internal doc relocation, no app behavior change).
- Version bumps: 2.32.1+795 → +796 → +798 → 2.33.0+800-internal.