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.
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.
- 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.
Update the section title from "Data Export" to "Data Management" to better reflect that the component handles both data export and import functionality.
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.
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.
- 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
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
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.
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.
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
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
- 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.
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.
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).
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.
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.