Commit Graph
76 Commits
Author SHA1 Message Date
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
Jose Olarte III 4de4fbecaf refactor: rename SetVisibilityDialog to SetBulkVisibilityDialog and remove unused code
- Rename SetVisibilityDialog.vue to SetBulkVisibilityDialog.vue for better clarity
- Update all component references in MembersList.vue (import, registration, template usage)
- Update component class name from SetVisibilityDialog to SetBulkVisibilityDialog
- Rename calling function name to showSetBulkVisibilityDialog to match class name change
- Remove unused properties and created() method from App.vue

This cleanup removes dead code and improves component naming consistency.
2025-10-17 17:59:41 +08:00
Jose Olarte III e3598992e7 feat: pause auto-refresh when SetVisibilityDialog is open
- Pause auto-refresh when SetVisibilityDialog becomes visible
- Resume auto-refresh when dialog is closed
- Prevents background refresh interference during visibility settings
- Fix type compatibility for visibilityDialogMembers data structure

This ensures users can interact with the visibility dialog without
the members list refreshing in the background, providing a better
user experience for setting member visibility preferences.
2025-10-16 17:49:59 +08:00
Jose Olarte III ea19195850 refactor: extract SetVisibilityDialog into standalone component
- Extract "Set Visibility to Meeting Members" dialog from App.vue into dedicated SetVisibilityDialog.vue component
- Move dialog logic directly to MembersList.vue for better component coupling
- Remove unnecessary intermediate state management from App.vue
- Clean up redundant style definitions (rely on existing Tailwind CSS classes)
- Remove unused logger imports and debug functions
- Add explanatory comment for Vue template constant pattern

This improves maintainability by isolating dialog functionality and follows established component patterns in the codebase.
2025-10-16 17:18:56 +08:00
Jose Olarte III ca545fd4b8 feat: add auto-refresh with countdown to MembersList
- Auto-refresh members list every 10 seconds
- Display countdown timer in refresh buttons
- Manual refresh resets countdown to 10 seconds
2025-10-15 20:25:01 +08:00
Jose Olarte III 07b538cadc feat: implement member visibility dialog with checkbox selection and refresh
- Add "Set Visibility" dialog for meeting members who need visibility settings
- Filter members to show only those not in contacts or without seesMe set
- Implement checkbox selection with "Select All" functionality
- Add reactive checkbox behavior with proper state management
- Default all checkboxes to checked when dialog opens
- Implement "Set Visibility" action to add contacts and set seesMe property
- Add success notifications with count of affected members
- Disable "Set Visibility" button when no members are selected
- Use notification callbacks for data refresh
- Hide "Set Visibility" buttons when no members need visibility settings
- Add proper dialog state management and cleanup
- Ensure dialog closes before triggering data refresh to prevent stale states

The implementation provides a smooth user experience for managing member visibility settings with proper state synchronization between components.
2025-10-14 21:21:35 +08:00
Jose Olarte III b84546686a WIP: button and icon additions
- Mirrored "Refresh" and "Visibility" buttons on top and bottom of member list
- Added back info icons for list actions
- When clicked, Person icon shows informative notification
2025-10-13 21:38:12 +08:00
Jose Olarte III 461ee84d2a WIP: meeting members adjustments 2025-10-12 23:30:03 +08:00