Commit Graph
100 Commits
Author SHA1 Message Date
Jose Olarte III 616d0fd6e0 fix(android): do not open Settings for exact alarm in scheduleDailyNotification
Allow scheduling to continue when exact alarm is not granted instead of
opening Settings and rejecting. Consumer apps can inform users about
exact alarms in their own UI.
2026-03-09 21:57:02 +08:00
Jose Olarte III 7ae36ec361 chore(deps): use npm nostr-tools instead of JSR @nostr/tools
- Replace "npm:@jsr/nostr__tools" with "nostr-tools" to fix npm 404
- Update imports in NewEditProjectView and Vite configs
- Remove Vite aliases so resolution uses package exports
2026-03-09 20:25:20 +08:00
Jose Olarte III f3cfa9552d refactor(notifications): use "Daily Reminder" terminology consistently
- Rename "Reminder Notification(s)" to "Daily Reminder" in Account and Help views
- Update NOTIFY_PUSH_SUCCESS title/message ("Notifications On", "Daily Reminder notifications are now enabled.")
- Align plugin spec doc with "Notifications" section naming
2026-03-06 19:02:45 +08:00
Jose Olarte III de486a2e23 docs: add end-user notification troubleshooting guide (iOS/Android)
- Add docs/NOTIFICATION_TROUBLESHOOTING.md for daily reminder/check-in
- Cover in-app settings, iOS (notifications, Focus), Android (notifications, exact alarms, battery)
- Include checklist table and note to backup identifier seed before uninstall
2026-03-06 18:14:07 +08:00
Jose Olarte III 6825bd5214 docs: add plugin feedback for Android rollover-interval bugs (since fixed)
Document two rollover-interval bugs for daily-notification-plugin with
logcat evidence and required fixes. Both issues have been fixed on the
plugin side; rollovers now chain every N minutes across reboots without
opening the app.
2026-03-04 22:27:08 +08:00
Jose Olarte III af63ab70e7 feat(notifications): add dev/test 10-minute rollover toggle and plugin spec
- Add dev-only "Use 10-minute rollover (testing)" toggle in Reminder
  Notifications (Account view). Visible only when not on prod API server
  (isNotProdServer). Toggle persists and reschedules reminder with
  rolloverIntervalMinutes when changed.
- Extend daily notification flow to pass optional rolloverIntervalMinutes
  to the plugin: NotificationService/NativeNotificationService options,
  PushNotificationPermission dialog options, first-time and edit flows.
- Add settings: reminderFastRolloverForTesting (Settings, AccountSettings,
  PlatformServiceMixin boolean mapping, migration 007).
- Centralize isNotProdServer(apiServer) in constants/app.ts; use in
  AccountViewView (toggle visibility), ImportAccountView, and TestView.
- Add docs/plugin-spec-rollover-interval-minutes.md for the plugin repo
  (configurable rollover interval and persistence after reboot).

Note: Daily notification plugin dependency is currently pointed at the
"rollover-interval" branch for testing this feature.
2026-03-03 21:31:07 +08:00
Jose Olarte III 96ae89bcfa docs: add daily notification duplicate/fallback analysis and plugin handoff 2026-03-02 20:35:08 +08:00
Jose Olarte III 7e2b16ddad docs: add plugin handoff for Android 6.0 (API 23) ZoneId fix
Add doc/plugin-feedback-android-6-api23-zoneid-fix.md for the
daily-notification-plugin repo: replace java.time.ZoneId with
TimeZone.getDefault().id so the plugin runs on API 23 without
affecting behavior on API 26+.
2026-02-27 21:50:39 +08:00
Jose Olarte III 29aff896be docs: add plugin feedback for Android rollover double-fire and user content
- New doc: rollover double-fire and missing user-set content diagnosis
- Link from DAILY_NOTIFICATION_BUG_DIAGNOSIS.md; include Cursor handoff section
2026-02-26 21:12:50 +08:00
Jose Olarte III d4721f5f4c fix(help): improve notification troubleshooting copy and use app constants
- Fix typo in Android Chrome "Clear browsing data" step
- Use TimeSafari in Settings steps and clarify PWA vs native install
- Add Battery & background subsection (iOS and Android)
- Add Focus / Do Not Disturb note under Check App Permissions
- Add quick checklist at top before Full Test
- Replace hardcoded app names with AppString.APP_NAME and APP_NAME_NO_SPACES
2026-02-26 17:58:32 +08:00
Jose Olarte III baac36607b fix: show readable copy for reminder notification toggle dialogs
Replace DIRECT_PUSH_TITLE in confirm and success messages with
"reminder notifications" so users see clear text instead of
the internal constant name.
2026-02-26 16:32:06 +08:00
Jose Olarte III 25c3cd99e4 docs: add plugin feedback for Android rollover after reboot
Document that boot recovery can skip rescheduling after device restart
(duplicate PendingIntent check), causing the next daily notification to
fail to fire on devices that clear AlarmManager alarms on reboot. Include
Scenario 1 logcat (notification fired when alarm survived), two-scenario
distinction, recommended fix (skipPendingIntentIdempotence in boot path),
duplicate-alarm clarification, and Cursor-ready implementation section
for the daily-notification-plugin repo.
2026-02-24 21:59:24 +08:00
Jose Olarte III cd5f9f5317 docs: add plugin feedback for Android post-reboot notification fallback text
Document bug where daily reminder shows fallback text after device restart
(Intent extras not surviving reboot). Includes root cause, recommended
plugin fixes (persist/restore title/body, use DB in recovery), and
verification steps for the daily-notification-plugin repo.
2026-02-23 21:24:29 +08:00
Jose Olarte III c28c47a3c9 docs: add plugin feedback for Android duplicate reminder notification
Add docs/plugin-feedback-android-duplicate-reminder-notification.md
describing the duplicate notification on first-time reminder setup (one
correct + one fallback). Root cause: ScheduleHelper schedules the
static reminder alarm and also enqueues the prefetch worker, which
on fallback schedules a second alarm via DailyNotificationScheduler.

Suggested fix: for static-reminder schedules, do not enqueue prefetch
(or have prefetch skip scheduleNotificationIfNeeded). The suggested
plugin changes were applied and fixed the issue.
2026-02-18 19:52:41 +08:00
Jose Olarte III 1b97ac08fd fix(electron): preserve electron-plugins.js during clean
Exclude electron/src/rt/electron-plugins.js from clean in build-electron.sh
so the hand-maintained plugin list is not deleted. Update Podfile.lock
(TimesafariDailyNotificationPlugin 1.1.0 → 1.1.6) and electron
package-lock.json.
2026-02-17 18:57:27 +08:00
Jose Olarte III 17ccfd1fea Fix: Android reminder toast and boot recovery
- Toast/notify: Keep dialog open until schedule flow finishes so success/error
  $notify runs while component is mounted (fixes missing toast on Android).
  Add success/error notify in edit-reminder callback (AccountViewView).
- Boot recovery: Split BootReceiver intent-filter so BOOT_COMPLETED and
  LOCKED_BOOT_COMPLETED use a filter without <data>; use a separate filter
  with <data scheme="package"/> for MY_PACKAGE_REPLACED/PACKAGE_REPLACED.
  Boot broadcasts have no Uri and were not matching the previous filter,
  so daily reminder now reschedules after device restart.
2026-02-17 17:55:13 +08:00
Jose Olarte III 0e096b1a46 Fix: Android daily notification: single schedule on edit, no double-cancel
Resolves long-standing issue where the second scheduled time (after editing
the reminder) did not fire on Android.

- PushNotificationPermission: add open(..., options?: { skipSchedule }).
  When skipSchedule is true (edit flow), dialog only invokes callback with
  time/message; parent is sole scheduler so the plugin is not called twice.
- AccountViewView: pass { skipSchedule: true } when opening the dialog for
  edit; keep cancel (iOS only) + single scheduleDailyNotification in callback.
- NativeNotificationService: serialize scheduleDailyNotification so only one
  schedule runs at a time (scheduleLock + doScheduleDailyNotification).
- AccountViewView: guard edit-reminder callback with editReminderScheduleInProgress
  so one schedule per user action.
- Gate pre-cancel on Android in edit flow (CONSUMING_APP brief): skip
  cancelDailyNotification before schedule on Android; plugin cancels internally.
- Use single stable reminder id and always pass id on both platforms (plugin 1.1.2+).
- Add doc/plugin-android-edit-reschedule-alarm-not-firing.md for plugin repo
  (cancel-before-reschedule may cancel the PendingIntent used for setAlarmClock).
2026-02-16 21:25:07 +08:00
Jose Olarte III ec55a74cbf WIP: Daily notification – optional id cleanup; Android issue not fixed
- NativeNotificationService: use single stable reminder id on iOS and Android,
  always pass id in scheduleOptions (plugin v1.1.2+ optional cleanup).
- Add doc/plugin-fix-scheduleExactNotification-calls.md for plugin repo
  (fix Java call sites for scheduleExactNotification 8th parameter).
- package-lock.json: update lockfile.

Android second-schedule issue still present; to be revisited.
2026-02-13 21:17:04 +08:00
Jose Olarte III c2fb493073 WIP: android daily notification re-schedule + plugin handoff doc
- NativeNotificationService: platform-specific schedule/cancel
  - iOS: pass id "daily_timesafari_reminder", call cancelDailyReminder before schedule
  - Android: no id (plugin uses "daily_notification"), skip pre-cancel to match test app
- Verification: return true when schedule succeeds but reminder not found (avoids error dialog)
- doc: android-daily-notification-second-schedule-issue.md
  - Symptom, timing (re-schedule-too-soon), test app vs TimeSafari
  - Plugin-side section: entry point, files, likely cause, suggested fixes, repro steps
  - For use in plugin repo (e.g. Cursor) to fix second-schedule

Re-scheduled notifications on Android still fail to fire; fix expected in plugin (see doc).
2026-02-13 18:44:38 +08:00
Jose Olarte IIIandCursor c05dff6654 feat(android): integrate daily notification plugin with native fetcher
Add native Android components for daily notification plugin integration:
- TimeSafariApplication: Custom Application class to register native fetcher
- TimeSafariNativeFetcher: Implements NativeNotificationContentFetcher interface
- network_security_config.xml: Allow cleartext for local development

Configuration updates:
- AndroidManifest.xml: Link custom Application class, add required permissions
- build.gradle: Add Java 17 compile options and required dependencies
- capacitor.config.ts: Add DailyNotification plugin configuration
- NativeNotificationService.ts: Use "daily_" prefixed ID for schedule rollover

Note: Subsequent notification scheduling after first fire still has issues
that require further investigation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 21:20:52 +08:00
Jose Olarte III a7fbb26847 docs: add Android physical device deployment guide
Add comprehensive guide for building and testing TimeSafari on physical
Android devices, covering device setup, USB debugging, network
configuration for local development servers, and troubleshooting.

- Create doc/android-physical-device-guide.md with full instructions
- Update BUILDING.md to reference the new guide in two places
2026-02-12 17:45:21 +08:00
Jose Olarte III 0a88f23bc7 docs(BUILDING): add Android emulator guide without Android Studio
- Add "Android Emulator Without Android Studio" section under Android Build
- Document env setup, SDK components, and API 36 system images
- Include Mac Silicon (arm64-v8a) and Intel (x86_64) AVD instructions
- Add steps: start emulator, build, install APK, and launch app
- Document one-shot build-and-run (debug:run, test:run)
- Update Prerequisites to mention command-line-only option and link to section
- Link to doc/android-emulator-deployment-guide.md for troubleshooting
2026-02-06 17:16:01 +08:00
Jose Olarte III fe9cdd6398 fix(ios): use Xcode 26 workaround in sync-only mode
--sync was calling npx cap sync ios directly, so pod install failed
on Xcode 26 (objectVersion 70). Define run_cap_sync_with_workaround
once before sync-only and use it for both --sync and the full build;
remove the duplicate definition at Step 6.6.
2026-02-06 16:50:53 +08:00
Jose Olarte III f5cb70ec8b docs: mark daily-notification Android receiver issue as resolved
Plugin fix is in @timesafari/daily-notification-plugin (explicit
PendingIntent component + id in Intent). Document resolution date,
summary of the fix, and follow-up steps (npm install, cap sync,
restore-local-plugins, test on device).
2026-02-06 15:52:35 +08:00
Jose Olarte III 1345118b79 refactor(push-notification): use native time input and polish notification UI
PushNotificationPermission:
- Swap hour/minute number inputs and AM/PM toggle for native <input type="time">
- Add timeValue computed to keep existing hour/minute/AM-PM state in sync
- Remove unused checkHourInput and checkMinuteInput
- Tighten copy and layout: headings, labels, char count, button spacing

AccountViewView:
- Show reminder time and message in a bordered box with Time/Message labels
- Adjust spacing in notifications section
2026-01-30 15:35:47 +08:00
Jose Olarte III 22c3ac80c2 feat(notifications): enable foreground notifications and rollover recovery
- iOS: set UNUserNotificationCenter delegate and implement willPresent
  so notifications show in foreground and DailyNotificationDelivered is
  posted for rollover; implement didReceive for tap handling; re-set
  delegate in applicationDidBecomeActive
- Android: move DailyNotificationReceiver and BootReceiver inside
  <application>; add NotifyReceiver; extend BootReceiver with
  LOCKED_BOOT_COMPLETED, MY_PACKAGE_REPLACED, directBootAware
- main.capacitor: import daily-notification-plugin at startup so
  plugin (and recovery) load on launch
- doc: add daily-notification-alignment-outline.md

Fixes foreground notifications not showing and rollover recovery;
Android receivers were previously declared outside <application>.
2026-01-29 21:10:34 +08:00
Jose Olarte III fb4ea08f3c fix(ios): add missing background task identifiers for daily notification plugin
Add com.timesafari.dailynotification.fetch and com.timesafari.dailynotification.notify
to BGTaskSchedulerPermittedIdentifiers in Info.plist to resolve registration
rejection errors. The plugin was attempting to register these identifiers but
they were not declared in the app's Info.plist, causing iOS to reject the
background task registrations.

Fixes Xcode log errors:
- Registration rejected; com.timesafari.dailynotification.fetch is not advertised
- Registration rejected; com.timesafari.dailynotification.notify is not advertised
2026-01-28 20:20:01 +08:00
Jose Olarte III 88f69054f4 Update AccountViewView.vue
fix(notifications): Reminder Notification toggle not turning off on native platforms

The toggle wasn't turning off on iOS/Android because:
1. Checkbox was being toggled directly before dialog confirmation
2. turnOffNotifications only handles web push, not native notifications

Fixed by:
- Preventing direct checkbox toggling with readonly and event handlers
- Adding native platform handling to cancel notifications via
  NotificationService.cancelDailyNotification() directly
- Bypassing web push unsubscribe flow on native platforms
2026-01-28 17:08:42 +08:00
Jose Olarte III 77e8d2d2ab fix(ios): run podspec fix script before pod install
Move the Daily Notification Plugin podspec fix script to execute before
the first pod install operation. Previously, the fix script ran after
pod install but before Capacitor sync, causing "No podspec found for
TimesafariDailyNotificationPlugin" errors when pod install tried to
resolve dependencies.

The Podfile expects TimesafariDailyNotificationPlugin.podspec, but the
plugin package only includes CapacitorDailyNotification.podspec. The
fix script creates the expected podspec file, but it must run before
CocoaPods attempts to resolve dependencies.

Fixes build failures on fresh installations where the podspec file
doesn't exist yet.
2026-01-27 15:53:11 +08:00
Jose Olarte III 31dfeb0988 fix(notifications): fix iOS notification scheduling and enable UI for native platforms
- Fix permission request to use correct iOS method (requestNotificationPermissions)
- Add robust handling for varying permission result formats
- Fix cancelDailyReminder to pass object parameter matching Swift plugin expectation
- Add notification cancellation before rescheduling to prevent duplicates
- Add verification after scheduling to ensure notification was actually scheduled
- Fix getStatus to handle both array and object response formats
- Enable notifications section in AccountView for native platforms (iOS/Android)
- Add edit button to allow users to modify existing notification time and message
- Add editReminderNotification method with form pre-population
- Add parseTimeTo24Hour helper for time format conversion

Fixes issues where:
- Notifications were stored but not actually scheduled with UNUserNotificationCenter
- cancelDailyReminder failed due to parameter type mismatch
- Notification time updates didn't properly cancel old notifications
- Users couldn't easily edit existing notification settings

The notification section is now visible on native platforms and includes an edit
button that opens the notification dialog with current values pre-populated.
2026-01-26 21:35:20 +08:00
Jose Olarte III 5a4ab84bfe feat(notifications): integrate DailyNotificationPlugin with UI for native platforms
Integrate DailyNotificationPlugin with notification UI to enable native
notifications on iOS/Android while maintaining web push for web/PWA.

- Add platform detection to PushNotificationPermission component
- Implement native notification flow via NotificationService
- Hide push server setting on native platforms (not needed)
- Add time conversion (AM/PM to 24-hour) for native plugin
- Add comprehensive documentation

Breaking Changes: None (backward compatible)
2026-01-23 19:06:16 +08:00
Jose Olarte III 84c3f79c57 fix(ios): resolve build errors for daily notification plugin
- Add podspec file for daily notification plugin with correct name
  - Create TimesafariDailyNotificationPlugin.podspec to match Capacitor's
    expected naming convention
  - Podspec name must match Podfile reference for CocoaPods compatibility
- Update Podfile to reference TimesafariDailyNotificationPlugin
- Add automated fix script for podspec creation
  - scripts/fix-daily-notification-podspec.sh creates podspec with correct
    name before Capacitor sync
  - Integrated into build-ios.sh build process
- Fix typo in package.json: change "pina" to "pinia" (^2.1.7)

Fixes:
- Vite build error: "Failed to resolve import 'pinia'"
- CocoaPods error: "No podspec found for 'TimesafariDailyNotificationPlugin'"
- CocoaPods error: "The name of the given podspec doesn't match the expected one"

The podspec file is created automatically during the build process to ensure
Capacitor sync can find the plugin with the expected name, while maintaining
compatibility with the actual podspec file name in the plugin package.
2026-01-22 18:03:14 +08:00
Jose Olarte III 14ffcb5434 feat: integrate daily notification plugin for native iOS/Android
Add native notification support via @timesafari/daily-notification-plugin
while maintaining existing Web Push for web/PWA builds. Platform detection
automatically selects the appropriate notification system at runtime.

Key Changes:
- Created NotificationService abstraction layer with unified API
- Implemented NativeNotificationService for iOS/Android
- Stubbed WebPushNotificationService for future web integration
- Registered DailyNotificationPlugin in Capacitor plugin system

Android Configuration:
- Added notification permissions (POST_NOTIFICATIONS, SCHEDULE_EXACT_ALARM, etc.)
- Registered DailyNotificationReceiver for alarm-based notifications
- Registered BootReceiver to restore schedules after device restart
- Added Room, WorkManager, and Coroutines dependencies
- Registered plugin in MainActivity.java

iOS Configuration:
- Added UIBackgroundModes (fetch, processing) to Info.plist
- Configured BGTaskSchedulerPermittedIdentifiers
- Added NSUserNotificationAlertStyle

Documentation:
- Created comprehensive integration guide
- Added architecture overview with diagrams
- Created implementation checklist
- Documented platform-specific behavior

Manual Steps Required:
- iOS: Enable Background Modes capability in Xcode
- iOS: Run `pod install` to install CapacitorDailyNotification pod
- Run `npx cap sync` to sync native projects

Platform Support:
- iOS: Native UNUserNotificationCenter (requires Xcode setup)
- Android: Native NotificationManager with AlarmManager
- Web/PWA: Existing Web Push (coexists, not yet wired to service)
- Electron: Ready (uses native implementation)

Status: Phase 1 complete - infrastructure ready for UI integration
Next: Update PushNotificationPermission.vue to use NotificationService
2026-01-21 22:22:48 +08:00
Jose Olarte III 679c4d6456 feat: integrate daily-notification-plugin from Gitea repository
- Add @timesafari/daily-notification-plugin dependency from private Gitea repo
- Configure .npmrc to be ignored by git to protect authentication tokens
- Remove .npmrc from version control (contains sensitive Gitea token)
- Update package-lock.json with new dependency

The plugin is installed via git URL and automatically builds during npm install
thanks to the prepare script in the plugin repository.

Installation requires Gitea personal access token configured in local .npmrc file.
2026-01-19 19:05:54 +08:00
Jose Olarte III bb92e3ac4f refactor(ios): preserve original image formats in share extension
Remove JPEG conversion and preserve original image formats and filenames
to test support for all image file types. Refactor image processing logic
for clarity and maintainability.

Changes:
- Remove forced JPEG conversion (was using 0.9 compression quality)
- Preserve original filename extensions instead of forcing .jpg
- Refactor from 3 branches to 2 (removed unlikely UIImage branch)
- Replace if statement with guard for non-image attachment filtering
- Simplify error check from optional binding to nil check
- Extract filename extension helper method to reduce duplication
- Update default filename from "shared-image.jpg" to "shared-image"

The only conversion that may occur is to PNG (lossless) when raw data
cannot be read from a file URL, preserving image quality while ensuring
compatibility.
2025-12-10 19:34:46 +08:00
Jose Olarte III a672c977a8 Fix Android image share workflow and add local plugin persistence
- Add SafeArea and SharedImage plugins to capacitor.plugins.json
- Create restore-local-plugins.js to persist plugins after cap sync
- Integrate plugin restoration into build scripts
- Improve Android share intent timing with retry logic
- Clean up debug logging while keeping essential error handling
- Add documentation for local plugin management

Fixes issue where SharedImage plugin wasn't recognized, causing
"UNIMPLEMENTED" errors. Image sharing now works correctly on Android.
2025-12-09 21:36:01 +08:00
Jose Olarte III 0c66142093 Fix iOS share extension large image handling
- Fix large images not loading from share sheet (exceeded UserDefaults 4MB limit)
- Store all shared images as files in App Group container instead of base64 in UserDefaults
- Fix image refresh when sharing new image while already on SharedPhotoView
- Add route query watcher to detect _refresh parameter changes
- Remove debug logging and redundant UIImage check

The previous implementation tried to store base64-encoded images in UserDefaults,
which failed for large images (HEIF images were often 6MB+ when base64 encoded). Now all images
are stored as files in the App Group container, with only the file path stored
in UserDefaults. This supports images of any size and provides consistent
behavior regardless of format.

Also fixes an issue where sharing a new image while already viewing
SharedPhotoView wouldn't refresh the displayed image.
2025-12-04 20:39:26 +08:00
Jose Olarte III 84983ee10b refactor(shared-image): replace temp file approach with native Capacitor plugins
Replace the buggy temp file polling mechanism with direct native-to-JS
communication via custom Capacitor plugins for iOS and Android. This
eliminates race conditions, file management complexity, and polling overhead.

BREAKING CHANGE: Removes backward compatibility with temp file approach.
Android minSdkVersion upgraded from 22 to 23.

Changes:
- iOS: Created SharedImagePlugin (CAPBridgedPlugin) and SharedImageUtility
  - Uses App Group UserDefaults for data sharing between extension and app
  - Implements getSharedImage() and hasSharedImage() methods
  - Removes temp file writing/reading logic from AppDelegate
- Android: Created SharedImagePlugin with @CapacitorPlugin annotation
  - Uses SharedPreferences for data storage instead of temp files
  - Implements same interface as iOS plugin
  - Removes temp file handling from MainActivity
- TypeScript: Added plugin definitions and registration
  - Created SharedImagePlugin interface and web fallback
  - Updated main.capacitor.ts to use plugin instead of Filesystem API
  - Removed pollForFileExistence() and related file I/O code
- Android: Upgraded minSdkVersion from 22 to 23
  - Required for SharedPreferences improvements and better API support

Benefits:
- Eliminates race conditions from file polling
- Removes file cleanup complexity
- Direct native-to-JS communication (no file I/O)
- Better performance (no polling overhead)
- More reliable data transfer between share extension and app
- Cleaner architecture with proper plugin abstraction
2025-12-04 18:03:47 +08:00
Jose Olarte III eeac7fdb66 fix(deps): align FontAwesome brands package version with other packages
Downgrade @fortawesome/free-brands-svg-icons from ^7.1.0 to ^6.5.1 to match
the version of @fortawesome/fontawesome-svg-core and other FontAwesome
packages. This resolves a TypeScript type error where IconDefinition from
the brands package was incompatible with the library.add() function due to
version mismatch.
2025-12-01 17:10:45 +08:00
Jose Olarte III 1a8383bc63 fix: update Android share intent handling for API 33+ compatibility
Fix deprecation warnings and ensure future compatibility by updating
getParcelableExtra() calls to use the new API introduced in Android 13
(API 33), while maintaining backward compatibility with older versions.

Changes:
- Update getParcelableExtra() to use typed version (Uri.class) on API 33+
- Update getParcelableArrayListExtra() to use typed version on API 33+
- Add version checks with Build.VERSION.SDK_INT >= TIRAMISU
- Maintain backward compatibility for API 22-32 using deprecated API
  with @SuppressWarnings("deprecation")
- No functional changes - share feature works identically across versions

The new API (getParcelableExtra(key, Class)) was introduced in API 33
to provide type safety. The old API is deprecated but still functional
on older versions, so we use runtime checks to support both.

This ensures the app builds cleanly with targetSdkVersion 36 and remains
compatible with minSdkVersion 22.
2025-11-28 16:43:36 +08:00
Jose Olarte III 4c771d8be3 fix: update iOS share extension deployment target and fix compatibility issues
Fix share extension not appearing in share sheet on iOS 18.6 and earlier
versions by updating deployment target and resolving API availability issues.

Changes:
- Update share extension deployment target from 26.1 to 14.0
  - iOS 18.6 cannot use extensions requiring iOS 26.1
  - UTType API requires iOS 14.0+ (used in ShareViewController)
- Update share extension display name from "TimeSafariShareExtension" to
  "TimeSafari" for cleaner appearance in share sheet
- Fix compiler warning: replace unused error binding with boolean check
  (if let error = error → if error != nil)

The share extension now works on iOS 14.0+ (matching UTType requirements)
and displays properly in the share sheet on older iOS versions.
2025-11-28 15:47:10 +08:00
Jose Olarte III 0627cd32b7 refactor: remove unused ShareImage plugin code, simplify to temp file approach
Remove unused Capacitor plugin implementation and simplify shared image
handling to use only the temp file approach, which is already working
reliably for both iOS and Android.

Changes:
- Delete ios/App/App/ShareImagePlugin.swift (unused, never registered)
- Delete ios/App/App/ShareImageBridge.swift (only used by plugin)
- Remove ~80 lines of plugin fallback code from src/main.capacitor.ts
- Simplify error handling to single code path
- Add documentation comment explaining temp file approach and future
  plugin implementation possibility

Benefits:
- ~154 lines of code removed
- Single, consistent code path for both platforms
- Easier to maintain and debug
- No functional changes - temp file approach already working

The temp file approach uses Capacitor's Filesystem plugin to read shared
image data written by native code (AppDelegate on iOS, MainActivity on
Android). This is simpler and more reliable than the plugin approach,
which required registration and bridge setup.

Future improvement: Consider implementing Capacitor plugins for direct
native-to-JS communication if lower latency becomes a priority, though
the current approach performs well in production.
2025-11-27 21:23:34 +08:00
Jose Olarte III e1eb91f26d feat: Add Android share target support for image sharing
Implement native Android share functionality to allow users to share
images from other apps directly to TimeSafari. This mirrors the iOS
share extension functionality and provides a seamless cross-platform
experience.

Changes:
- Add ACTION_SEND and ACTION_SEND_MULTIPLE intent filters to
  AndroidManifest.xml to register the app as a share target for images
- Implement share intent handling in MainActivity.java:
  - Process incoming share intents in onCreate() and onNewIntent()
  - Read shared image data from content URI using ContentResolver
  - Convert image to Base64 encoding
  - Write image data to temporary JSON file in app's internal storage
  - Use getFilesDir() which maps to Capacitor's Directory.Data
- Update src/main.capacitor.ts to support Android platform:
  - Extend checkAndStoreNativeSharedImage() to check for Android platform
  - Use Directory.Data for Android file operations (vs Directory.Documents for iOS)
  - Add Android to initial load and app state change listeners
  - Ensure shared image detection works on app launch and activation

The implementation follows the same pattern as iOS:
- Native layer (MainActivity) writes shared image to temp file
- JavaScript layer polls for file existence with exponential backoff
- Image is stored in temp database and user is navigated to SharedPhotoView

This enables users to share images from Gallery, Photos, and other apps
directly to TimeSafari on Android devices.
2025-11-26 20:01:14 +08:00
Jose Olarte III 09a230f43e refactor(ios): improve file polling and extract shared image storage logic
Replace hardcoded timeout with reliable file existence polling and extract
duplicate image storage code into reusable function.

File Polling Improvements:
- Replace hardcoded 300ms timeout with pollForFileExistence() function
- Use Filesystem.stat() to check file existence instead of guessing timing
- Implement exponential backoff (100ms, 200ms, 400ms, 800ms, 1600ms)
- Maximum 5 retries with configurable parameters
- More reliable: actually checks if file exists rather than fixed wait time

Code Deduplication:
- Extract storeSharedImageInTempDB() function to eliminate code duplication
- Consolidates clearing old data, base64-to-dataURL conversion, and DB storage
- Used in both temp file and plugin fallback code paths
- Improves maintainability and reduces risk of inconsistencies

This makes the code more robust and maintainable while ensuring the file
is actually available before attempting to read it.
2025-11-25 18:56:45 +08:00
Jose Olarte III eff4126043 feat(ios): improve share extension UX and fix image reload issues
Improve iOS share extension implementation to skip interstitial UI and
fix issues with subsequent image shares not updating the view.

iOS Share Extension Improvements:
- Replace SLComposeServiceViewController with custom UIViewController
  to eliminate interstitial "Post" button UI
- Use minimal URL (timesafari://) instead of deep link for app launch
- Implement app lifecycle detection via Capacitor appStateChange listener
  instead of relying solely on deep links

Deep Link and Navigation Fixes:
- Remove "shared-photo" from deep link schemas (no longer needed)
- Add empty path URL handling for share extension launches
- Implement processing lock to prevent duplicate image processing
- Add retry mechanism (300ms delay) to handle race conditions with
  AppDelegate writing temp files
- Use router.replace() when already on /shared-photo route to force refresh
- Clear old images from temp DB before storing new ones
- Delete temp file immediately after reading to prevent stale data

SharedPhotoView Component:
- Add route watcher (@Watch) to reload image when fileName query
  parameter changes
- Extract image loading logic into reusable loadSharedImage() method
- Improve error handling to clear image state on failures

This fixes the issue where sharing a second image while already on
SharedPhotoView would display the previous image instead of the new one.
2025-11-25 18:22:43 +08:00
Jose Olarte III ae49c0e907 feat(ios): implement native share target for images
Implement iOS Share Extension to enable native image sharing from Photos
and other apps directly into TimeSafari. Users can now share images from
the iOS share sheet, which will open in SharedPhotoView for use as gifts
or profile pictures.

iOS Native Implementation:
- Add TimeSafariShareExtension target with ShareViewController
- Configure App Groups for data sharing between extension and main app
- Implement ShareViewController to process shared images and convert to base64
- Store shared image data in App Group UserDefaults
- Add ShareImageBridge utility to read shared data from App Group
- Update AppDelegate to handle shared-photo deep link and bridge data to JS

JavaScript Integration:
- Add checkAndStoreNativeSharedImage() in main.capacitor.ts to read shared
  images from native layer via temporary file bridge
- Convert base64 data to data URL format for compatibility with base64ToBlob
- Integrate with existing SharedPhotoView component
- Add "shared-photo" to deep link validation schema

Build System:
- Integrate Xcode 26 / CocoaPods compatibility workaround into build-ios.sh
- Add run_pod_install_with_workaround() for explicit pod install
- Add run_cap_sync_with_workaround() for Capacitor sync (which runs pod
  install internally)
- Automatically detect project format version 70 and apply workaround
- Remove standalone pod-install-workaround.sh script

Code Cleanup:
- Remove verbose debug logs from ShareViewController, AppDelegate, and
  main.capacitor.ts
- Retain essential logger calls for production debugging

Documentation:
- Add ios-share-extension-setup.md with manual Xcode setup instructions
- Add ios-share-extension-git-commit-guide.md for version control best practices
- Add ios-share-implementation-status.md tracking implementation progress
- Add native-share-target-implementation.md with overall architecture
- Add xcode-26-cocoapods-workaround.md documenting the compatibility issue

The implementation uses a temporary file bridge (AppDelegate writes to Documents
directory, JS reads via Capacitor Filesystem plugin) as a workaround for
Capacitor plugin auto-discovery issues. This can be improved in the future by
properly registering ShareImagePlugin in Capacitor's plugin registry.
2025-11-24 20:46:58 +08:00
Jose Olarte III 1b4ab7a500 fix(ios): add shared Xcode scheme for App target
Resolves "No Scheme" issue in Xcode by adding a shared scheme file.
Capacitor doesn't automatically create shared schemes, so this needs
to be manually added and committed to version control.
2025-11-24 16:20:12 +08:00
Jose Olarte III 36eb9a16b0 fix: preserve contact methods and notes in export/import workflow
- Fix contactMethods not being exported (was checking for string instead of array)
- Add missing notes and iViewContent fields to $insertContact method
- Normalize empty strings to null when saving contacts in ContactEditView

This ensures contact data integrity is maintained during export/import operations.
2025-11-20 18:11:27 +08:00
Jose Olarte III 203cf6b078 refactor(DataExportSection): rename section title to "Data Management"
Update the section title from "Data Export" to "Data Management" to better reflect that the component handles both data export and import functionality.
2025-11-19 21:32:35 +08:00
Jose Olarte III 9b84b28a78 refactor: move Import Contacts section to DataExportSection component
- Move Import Contacts UI section from AccountViewView to DataExportSection
- Consolidate import/export functionality in a single component
- Move related methods: uploadImportFile, showContactImport, checkContactImports
- Convert module-level ref to component property (inputImportFileName)
- Remove unused imports (ref, ImportContent) from AccountViewView
- Rename "Download Contacts" button to "Export Contacts"
- Improve import UI styling with full-width file input and button
2025-11-19 19:26:36 +08:00
Jose Olarte III c4eb6f2d1d fix(GiftedDialog): preserve recipient when changing giver project
Modified selectProject() to only set receiver to "You" if no receiver
has been selected yet, preventing recipient from being reset when
changing giver project in Project-to-Person context.
2025-11-18 15:50:11 +08:00
Jose Olarte III 223031866b refactor: remove unused loadMoreCallback prop from EntityGrid
Remove loadMoreCallback prop and related backward compatibility code.
No parent components were using this prop, and it has been superseded
by the internal pagination mechanism using fetchProjects() and beforeId.
2025-11-17 19:58:55 +08:00
Jose Olarte III cb75b25529 refactor: consolidate project loading into EntityGrid component
Unify project loading and searching logic in EntityGrid.vue to eliminate
duplication. Make entities prop optional for projects, add internal
project state, and auto-load projects when needed.

- EntityGrid: Combine search/load into fetchProjects(), add internal
  allProjects state, handle pagination internally for both search and
  load modes
- OnboardMeetingSetupView: Remove project loading methods
- MeetingProjectDialog: Remove project props
- GiftedDialog: Remove project loading logic
- EntitySelectionStep: Make projects prop optional

Reduces code duplication by ~150 lines and simplifies component APIs.
All project selection now uses EntityGrid's internal loading.
2025-11-17 19:49:17 +08:00
Jose Olarte III acf104eaa7 refactor: remove debug loggers from EntityGrid component
Remove three logger.debug() calls used for debugging project search
results and pagination state. Error logging remains intact.
2025-11-13 21:41:34 +08:00
Jose Olarte III e793d7a9e2 refactor: defer project loading until MeetingProjectDialog opens
- Move loadProjects() call from created() to handleDialogOpen()
- Remove allProjects check from ensureSelectedProjectLoaded()
- Projects now load only when dialog is opened, improving initial page load performance
- ensureSelectedProjectLoaded() now directly fetches project by handleId when needed
2025-11-13 21:28:47 +08:00
Jose Olarte III 3ecae0be0f refactor(OnboardMeetingSetupView): fix selected project display after refresh
Refactor selectedProject computation to use separate storage instead of
relying on allProjects array. This fixes a bug where the selected project
wouldn't display after page refresh if it wasn't in the initial allProjects
batch.

Changes:
- Add selectedProjectData property to store selected project independently
- Simplify selectedProject computed to return selectedProjectData directly
- Add fetchProjectByHandleId() to fetch single project by handleId
- Add ensureSelectedProjectLoaded() to check allProjects first, then fetch
- Update handleProjectLinkAssigned() to store directly in selectedProjectData
- Remove band-aid solution of adding selected projects to allProjects array
- Update startEditing() and cancelEditing() to ensure selected project loads
- Call ensureSelectedProjectLoaded() in created() lifecycle hook

This ensures the selected project always displays correctly, even when:
- Selected from search results (not in allProjects)
- Page is refreshed (allProjects reloads without selected project)
- Project is in a later pagination batch
2025-11-13 19:21:22 +08:00
Jose Olarte III d37e53b1a9 fix: pause MembersList auto-refresh during project dialog interaction
Stop auto-refresh when MeetingProjectDialog opens and resume when it closes
to prevent UI conflicts during project selection.
2025-11-13 18:10:35 +08:00
Jose Olarte III 2f89c7e13b feat(EntityGrid): add server-side search with pagination for projects
Implement server-side search for projects using API endpoint with
pagination support via beforeId parameter. Contacts continue using
client-side filtering from complete local database.

- Add PlatformServiceMixin for internal apiServer access
- Implement performProjectSearch() with pagination
- Update infinite scroll to handle search pagination
- Add search lifecycle management and error handling

No breaking changes to parent components.
2025-11-12 21:06:20 +08:00
Jose Olarte III 6bf4055c2f feat: add pagination support for project lists in dialogs
Add server-side pagination to EntityGrid component for projects, enabling
infinite scrolling to load all available projects instead of stopping after
the initial batch.

Changes:
- EntityGrid: Add loadMoreCallback prop to trigger server-side loading when
  scroll reaches end of loaded projects
- OnboardMeetingSetupView: Update loadProjects() to support pagination with
  beforeId parameter and add handleLoadMoreProjects() callback
- MeetingProjectDialog: Accept and pass through loadMoreCallback to EntityGrid
- GiftedDialog: Add pagination support to loadProjects() and
  handleLoadMoreProjects() callback
- EntitySelectionStep: Accept and pass through loadMoreCallback prop to
  EntityGrid when showing projects

This ensures users can access all projects in MeetingProjectDialog and
GiftedDialog by automatically loading more as they scroll, matching the
behavior already present in DiscoverView.

All project uses of EntityGrid now use pagination by default.
2025-11-12 17:10:03 +08:00
Jose Olarte III bf7ee630d0 feat(meeting): enable selecting all projects in meeting setup
Update loadProjects to fetch all projects instead of only user's projects
by switching from plansByIssuer to plans endpoint.
2025-11-12 15:58:23 +08:00
Jose Olarte III a5a9af5ddc feat(meetings): add project selection dialog for meeting setup
Replace Project Link text input with interactive selection dialog
using new MeetingProjectDialog component. Dialog displays user's
projects with icons and issuer information, following the same
pattern as ProjectRepresentativeDialog.

- Create MeetingProjectDialog with EntityGrid integration
- Add clickable project field with icon, name, and issuer display
- Load projects from /api/v2/report/plansByIssuer endpoint
- Show issuer name instead of handleId for better UX
- Refactor loadProjects to remove unused rowId field
2025-11-11 21:34:11 +08:00
Jose Olarte III 4e3e293495 refactor(EntityGrid): simplify alphabetical section label
Change "Everyone Else" to "Everyone" for clearer, more concise labeling
2025-11-11 15:32:11 +08:00
Jose Olarte III 65533c15d2 Merge branch 'project-representative-dialog' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into project-representative-dialog 2025-11-11 15:15:01 +08:00
Jose Olarte III 2530bc0ec2 fix: ensure consistent "Recently Added" contacts in ProjectRepresentativeDialog
EntityGrid's recentContacts assumes contacts are sorted by date added
(newest first), but ProjectRepresentativeDialog was receiving contacts
sorted alphabetically from NewEditProjectView, causing it to show
different "Recently Added" contacts than GiftedDialog.

- Changed NewEditProjectView to use $contactsByDateAdded() instead of
  $getAllContacts()
- Added documentation comments to EntityGrid.vue to prevent this issue
  in future reuses
2025-11-11 15:06:07 +08:00
Jose Olarte III 9a3409c29f refactor: remove unused code from ProjectRepresentativeDialog
- Remove conflictChecker prop (always passed as no-op function)
- Remove unused emitCancel method and cancel event handling
- Simplify handleEntitySelected by removing unnecessary type check
- Update NewEditProjectView to remove conflict-checker binding and empty cancel handler

The conflictChecker prop was not needed since representative selection
doesn't require conflict detection. The cancel event was never emitted
and the parent handler was empty, so both were removed.
2025-11-07 17:43:44 +08:00
Jose Olarte III 749204f96b fix: database connection error causing navigation redirect on iOS/Android
Handle "Connection already exists" error when initializing SQLite database
on Capacitor platforms. The native connection can persist across app
restarts while the JavaScript connection Map is empty, causing a mismatch.

When createConnection fails with "already exists":
- Check if connection exists in JavaScript Map and retrieve it if present
- If not in Map, close the native connection and recreate to sync both sides
- Handle "already open" errors gracefully when opening existing connections

This fixes the issue where clicking "Backup Identifier Seed" would redirect
to StartView instead of SeedBackupView due to database initialization
failures in the router navigation guard.

Fixes navigation issue on both iOS and Android platforms.
2025-11-06 21:38:51 +08:00
Jose Olarte III a142737771 feat: replace authorized representative input with contact selection dialog
Replace the plain text input for authorized representative with an
interactive contact selection interface that provides better UX and
maintains data consistency.

Changes:
- Add ProjectRepresentativeDialog component using EntityGrid for contact selection (excludes "You" and "Unnamed" special entities)
- Replace text input with clickable field showing contact icon, name, and DID
- Implement conditional UI states: initial "Assign..." placeholder vs assigned representative display with unset button
- Refactor selectedRepresentative to computed property derived from agentDid (single source of truth, prevents sync issues)
- Inline representativeDisplayName for simplicity
- Support changing representative by clicking on assigned field
- Support unsetting representative via trash button

The new implementation ensures agentDid remains the authoritative state while selectedRepresentative is automatically computed, preventing the previously possible desync when agentDid was set directly (e.g., via the
"make original owner an authorized representative" button).
2025-11-05 20:20:43 +08:00
Jose Olarte III d9230d0be8 fix: restore proper dialog max-height 2025-11-05 16:25:06 +08:00
Jose Olarte III 38f301f053 Merge branch 'master' into entity-selection-list-component 2025-11-05 16:12:39 +08:00
Jose Olarte III c06ffec466 refactor: combine member processing methods in BulkMembersDialog
Consolidate organizerAdmitAndAddWithVisibility() and
memberAddContactWithVisibility() into a single unified method
processSelectedMembers() that handles both organizer and member
modes based on the isOrganizer prop.

- Remove redundant handleMainAction() wrapper method
- Update template to call processSelectedMembers directly
- Reduce code duplication by ~30% (140 lines → 98 lines)
- Maintain identical functionality for both modes

This simplifies the component structure and makes the processing
logic easier to maintain.
2025-11-04 18:39:45 +08:00
Jose Olarte III 8b199ec76c refactor: remove redundant dialogType prop from BulkMembersDialog
Remove dialogType prop and consolidate to use only isOrganizer prop.

- Remove dialogType prop from BulkMembersDialog component
- Replace all dialogType checks with isOrganizer boolean checks
- Add comments clarifying isOrganizer true/false meanings
- Remove dialog-type prop binding from MembersList component

This simplifies the component API while maintaining the same functionality.
2025-11-04 17:57:38 +08:00
Jose Olarte III d32cca4f53 feat(EntityGrid): implement infinite scroll for entity lists
Add infinite scroll functionality to EntityGrid component using VueUse's
useInfiniteScroll composable to handle large volumes of entities efficiently.

Changes:
- Integrate @vueuse/core useInfiniteScroll composable
- Add infinite scroll state management (displayedCount, reset function)
- Configure initial batch size (20 items) and increment size (20 items)
- Update displayedEntities, alphabeticalContacts to support progressive loading
- Add canLoadMore() logic for people, projects, and search modes
- Reset scroll state when search term or entities prop changes
- Remove maxItems prop (replaced by infinite scroll)
- Simplify displayEntitiesFunction signature (removed maxItems parameter)
- Update EntitySelectionStep and test files to remove max-items prop

Technical details:
- Uses template ref (scrollContainer) to access scrollable container
- Recent contacts (3) count toward initial batch for people grid
- Special entities (You, Unnamed) always displayed, don't count toward limits
- Infinite scroll works for both entity types and search results
- Constants are configurable at top of component (INITIAL_BATCH_SIZE, INCREMENT_SIZE)

This improves performance and UX when displaying large lists of contacts or
projects by loading content progressively as users scroll.
2025-11-03 21:47:25 +08:00
Jose Olarte III 4004d9fe52 feat(EntityGrid): Split contacts into recent and alphabetical sections
When displaying contacts (not search results), show the 3 most recently
added contacts at the top with a "Recently Added" heading, followed by
the rest sorted alphabetically with an "Everyone Else" heading.

- Add recentContacts and alphabeticalContacts computed properties
- Hide "You" and "Unnamed" special entities during search
- Only show search spinner when actively searching with a term
- Style section headings with uppercase, improved spacing, and borders
2025-11-03 16:32:59 +08:00
Jose Olarte III 2f99d0b416 fix(components): prevent icon shrinking in PersonCard and ProjectCard
Add shrink-0 class to icon elements to maintain consistent icon sizing
when card layouts flex or wrap content.
2025-10-31 19:10:13 +08:00
Jose Olarte III 9c3002f9c7 feat(EntityGrid): sort search results alphabetically
Sort search results alphabetically while preserving original order for
default list when no search term is present.
2025-10-31 19:07:50 +08:00
Jose Olarte III 82fd7cddf7 feat: Add showUnnamedEntity prop to EntityGrid
Add prop to control visibility of "Unnamed" entity, matching showYouEntity
pattern. Defaults to true for backward compatibility.
2025-10-31 18:59:35 +08:00
Jose Olarte III 10f2920e11 feat(EntityGrid): display no results message for empty search queries
Add contextual feedback message when a search term is entered but no matching entities are found. The message dynamically adjusts its wording based on whether searching for people or projects.
2025-10-31 18:34:54 +08:00
Jose Olarte III d7db7731cf Merge branch 'master' into meeting-members-admission-dialog 2025-10-30 21:55:48 +08:00
Jose Olarte III 75c89b471c fix: linting 2025-10-30 21:49:35 +08:00
Jose Olarte III a804877a08 feat: Add quick search to EntityGrid with date-based contact sorting
- Add search-as-you-type functionality with 500ms debounce
- Implement search across contact names and DIDs, project names and handleIds
- Add loading spinner and dynamic clear button
- Add $contactsByDateAdded() method to PlatformServiceMixin for newest-first sorting
- Update GiftedDialog to use date-based contact ordering
- Maintain backward compatibility with existing $contacts() alphabetical sorting
- Add proper cleanup for search timeouts on component unmount

The search feature provides real-time filtering with visual feedback,
while the new sorting ensures recently added contacts appear first.
2025-10-30 21:16:36 +08:00
Jose Olarte III f7441f39e7 feat: remove Show All navigation card from entity grids
- Remove ShowAllCard component and all related functionality
- Remove showAllRoute, showAllQueryParams, and hideShowAll props
- Remove shouldShowAll computed property from EntityGrid
- Clean up ShowAll-related code from EntitySelectionStep and GiftedDialog
- Delete ShowAllCard.vue component file
- Update component documentation to reflect removal

This simplifies the entity selection interface by removing the navigation
card that allowed users to view all entities in a separate view.
2025-10-30 17:31:18 +08:00
Jose Olarte III 9628d5c8c6 refactor: move display text logic to BulkMembersDialog component
- Replace individual text props with single isOrganizer boolean prop
- Add computed properties for title, description, buttonText, and emptyStateText
- Simplify parent component interface by removing text prop passing
- Update quote style from single to double quotes for consistency
- Improve component encapsulation and maintainability
2025-10-30 16:11:45 +08:00
Jose Olarte III b37051f25d refactor: unify member dialogs into reusable BulkMembersDialog component
- Merge AdmitPendingMembersDialog and SetBulkVisibilityDialog into single BulkMembersDialog
- Add dynamic props for dialog type, title, description, button text, and empty state
- Support both 'admit' and 'visibility' modes with conditional behavior
- Rename setVisibilityForSelectedMembers to addContactWithVisibility for clarity
- Update success counting to track contacts added vs visibility set
- Improve error messages to reflect primary action of adding contacts
- Update MembersList to use unified dialog with role-based configuration
- Remove unused libsUtil import from MembersList
- Update comments and method names to reflect unified functionality
- Rename closeMemberSelectionDialogCallback to closeBulkMembersDialogCallback

This consolidation eliminates ~200 lines of duplicate code while maintaining
all existing functionality and improving maintainability through a single
source of truth for bulk member operations.
2025-10-29 18:21:32 +08:00
Jose Olarte III 7b87ab2a5c feat: add "Select All" footer to member selection dialogs
- Add tfoot with "Select All" checkbox to AdmitPendingMembersDialog
- Add tfoot with "Select All" checkbox to SetBulkVisibilityDialog
- Both footer checkboxes sync with header checkboxes for consistent UX
- Users can now select/deselect all members from top or bottom of table
2025-10-29 15:24:37 +08:00
Jose Olarte III ca7ead224b fix: resolve PostCSS parsing error in FeedFilters.vue
- Add missing <style scoped> section to FeedFilters.vue to fix PostCSS error

The PostCSS error was occurring because Vue single-file components require
a <style> section, even if empty, for proper CSS processing.
2025-10-29 15:18:54 +08:00
Jose Olarte III bfc2f07326 fix: resolve admission status styling issues for non-organizers in MembersList
- Fix undefined admitted property for non-organizers by defaulting to true
- Update conditional styling logic to show blue background for non-admitted current user
- Add hand icon indicator for current user in members list
- Improve sorting to prioritize current user after organizer
- Refactor currentUserInList variable inline for cleaner code
- Update text color and hourglass icon conditions to include current user

The server was returning undefined for the admitted property when non-organizers
viewed the members list, causing incorrect styling. Non-organizers now properly
see their admission status and get appropriate visual indicators.
2025-10-28 21:05:06 +08:00
Jose Olarte III 562713d5a4 feat: hide contact instruction when no non-contact members exist
- Add condition to only show "add to contacts" instruction when there are members who are not already contacts
- Use existing getNonContactMembers() method to check for non-contact members
- Fix line length warning by breaking long comment into multiple lines
2025-10-28 18:58:40 +08:00
Jose Olarte III 8100ee5be4 refactor: optimize success message logic in AdmitPendingMembersDialog
- Simplify success message generation using ternary operators
- Remove visibilitySetCount due to its implied nature
- Handle case when contactAddedCount is 0 by omitting contact-related text
- Use more compact logic that only applies ternaries to variable parts
- Maintain proper pluralization for both admitted members and contacts

The message now shows:
- "n member/s admitted." when no contacts added
- "n member/s admitted and added as contact/s." when counts equal
- "n member/s admitted, n added as contact/s." when counts differ
2025-10-28 18:44:36 +08:00
Jose Olarte III 966ca8276d refactor: simplify pending members dialog description text
- Replace verbose explanation with concise, direct question
- Streamline the admission dialog interface
2025-10-28 17:34:16 +08:00
Jose Olarte III 27e38f583b feat: improve auto-refresh handling during member admission dialogs
- Add stopAutoRefresh() calls before showing confirmation dialogs
- Add startAutoRefresh() calls after dialog interactions complete
- Ensure auto-refresh resumes properly in all dialog callback paths
- Fix missing onCancel handler for contact confirmation dialog

This prevents auto-refresh from interfering with user interactions
during member admission workflows while ensuring it resumes afterward.
2025-10-28 17:21:14 +08:00
Jose Olarte III 1e3ecf6d0f refactor: migrate dialog styles from scoped CSS to Tailwind utilities
- Remove scoped CSS styles for .dialog-overlay and .dialog from AdmitPendingMembersDialog.vue
- Remove scoped CSS overflow style from FeedFilters.vue dialog
- Update Tailwind .dialog utility class to include max-height and overflow-y-auto
- Consolidate dialog styling into reusable Tailwind components for consistency
2025-10-28 15:57:36 +08:00
Jose Olarte III 2f1eeb6700 fix: resolve duplicate names in Visibility dialog after Admit dialog
- Add deduplication logic to getMembersForVisibility() method to prevent duplicate entries
- Fix timing issue with isManualRefresh flag reset in showSetBulkVisibilityDialog()
- Ensure Visibility dialog shows each member only once when following Admit dialog
- Remove debugging console logs after issue resolution

The issue was caused by multiple calls to getMembersForVisibility() returning
duplicate member entries, which were then displayed in the Visibility dialog.
The fix deduplicates members by DID to ensure each member appears only once.
2025-10-24 17:31:46 +08:00
Jose Olarte III e048e4c86b fix: restrict pending member styling to organizers only
- Apply special styling (blue background, grayed text, hourglass icon) only when current user is organizer
- Non-organizers now see consistent styling for all visible members
- Maintains organizer's ability to distinguish between admitted and pending members
- Fixes issue where non-organizers saw inconsistent styling for all members
2025-10-24 15:40:34 +08:00
Jose Olarte III 16ed5131c4 feat: restrict dialog access based on user roles
- AdmitPendingMembersDialog now only triggers for meeting organizers
- SetBulkVisibilityDialog now only triggers for members who can see other members
- Removes overly restrictive admission status check for visibility dialog
- Ensures proper role-based access control for meeting management features
2025-10-24 15:23:39 +08:00
Jose Olarte III e647af0777 refactor: convert entity display to list style
- Switch from grid display to list layout for persons and projects
- Re-styled special entities (unnamed, You) to match
- Added max-height limit to list in preparation for scrolling and displaying more items
2025-10-24 13:26:36 +08:00
Jose Olarte III ad51c187aa Update AdmitPendingMembersDialog.vue
feat: add DID display to Pending Members dialog

- Restructure member display with better visual hierarchy
- Add DID display with responsive truncation for mobile
- Simplify button labels ("Admit + Add Contacts" and "Admit Only")
2025-10-23 19:59:55 +08:00
Jose Olarte III 6fbc9c2a5b feat: Add AdmitPendingMembersDialog for bulk member admission
- Add new AdmitPendingMembersDialog component with checkbox selection
- Support two action modes: "Admit + Add Contacts" and "Admit Only"
- Integrate dialog into MembersList with proper sequencing
- Show admit dialog before visibility dialog when pending members exist
- Fix auto-refresh pause/resume logic for both dialogs
- Ensure consistent dialog behavior between initial load and manual refresh
- Add proper async/await handling for data refresh operations
- Optimize dialog state management and remove redundant code
- Maintain proper flag timing to prevent race conditions

The admit dialog now shows automatically when there are pending members,
allowing organizers to efficiently admit multiple members at once while
optionally adding them as contacts and setting visibility preferences.
2025-10-22 21:56:00 +08:00
Jose Olarte III 035509224b feat: change icon for pending members
- Changed from an animating spinner to a static hourglass
2025-10-21 22:00:21 +08:00
Jose Olarte III e9ea89edae feat: enhance members list UI with visual indicators and improved styling
- Sort members list with organizer first, then non-admitted, then admitted
- Add crown icon for meeting organizer identification
- Add spinner icon for non-admitted members
- Implement conditional styling for non-admitted members
- Update button styling to use circle icons instead of rounded backgrounds
- Improve visual hierarchy with better spacing and color coding
2025-10-21 18:13:10 +08:00
Jose Olarte III 4f3a1b390d feat: auto-show visibility dialog for meeting members
- Show dialog on initial load if members need visibility settings
- Show dialog during auto-refresh only when new members are added (not removed)
- Show dialog on manual refresh if any members need visibility settings
- Remove manual "Set Visibility" buttons from UI as dialog now appears automatically
- Add logic to track previous visibility members and detect changes
- Improve UX by proactively prompting users to set visibility for new meeting members

The dialog now appears automatically in these scenarios:
- Component initialization with members needing visibility
- Auto-refresh when new members join the meeting
- Manual refresh when members need visibility settings
2025-10-17 19:17:47 +08:00