Compare commits

..

1 Commits

Author SHA1 Message Date
Matthew Raymer
2e290ad488 grr: experimental diagnosis and fix for build:web:serve 2025-07-18 09:40:12 +00:00
313 changed files with 13467 additions and 10957 deletions

View File

@@ -8,3 +8,28 @@ alwaysApply: true
✅ remove whitespace at the end of lines
✅ use npm run lint-fix to check for warnings
✅ do not use npm run dev let me handle running and supplying feedback
✅ do not add or commit for the user; let him control that process
always preview changes and commit message to use and allow me to copy and paste
✅ Preferred Commit Message Format
Short summary in the first line (concise and high-level).
Avoid long commit bodies unless truly necessary.
✅ Valued Content in Commit Messages
Specific fixes or features.
Symptoms or problems that were fixed.
Notes about tests passing or TS/linting errors being resolved (briefly).
❌ Avoid in Commit Messages
Vague terms: “improved”, “enhanced”, “better” — especially from AI.
Minor changes: small doc tweaks, one-liners, cleanup, or lint fixes.
Redundant blurbs: repeated across files or too generic.
Multiple overlapping purposes in a single commit — prefer narrow, focused commits.
Long explanations of what can be deduced from good in-line code comments.
Guiding Principle
Let code and inline documentation speak for themselves. Use commits to highlight what isn't obvious from reading the code.

View File

@@ -1,14 +0,0 @@
---
alwaysApply: true
---
# Directive for Documentation Generation
1. Produce a **small, focused set of documents** rather than an overwhelming volume.
2. Ensure the content is **maintainable and worth preserving**, so that humans
are motivated to keep it up to date.
3. Prioritize **educational value**: the documents must clearly explain the
workings of the system.
4. Avoid **shallow, generic, or filler explanations** often found in
AI-generated documentation.
5. Aim for **clarity, depth, and usefulness**, so readers gain genuine understanding.
6. Always check the local system date to determine current date.

View File

@@ -312,21 +312,3 @@ Description of current situation or problem.
**Last Updated**: 2025-07-09
**Version**: 1.0
**Maintainer**: Matthew Raymer
### Heading Uniqueness
- **Rule**: No duplicate heading content at the same level
- **Scope**: Within a single document
- **Rationale**: Maintains clear document structure and navigation
- **Example**:
```markdown
## Features ✅
### Authentication
### Authorization
## Features ❌ (Duplicate heading)
### Security
### Performance
```

View File

@@ -1,96 +1,70 @@
---
description:
globs:
description:
globs:
alwaysApply: true
---
---
description:
globs:
alwaysApply: true
---
# Time Safari Context
## Project Overview
Time Safari is an application designed to foster community building through gifts,
gratitude, and collaborative projects. The app should make it extremely easy and
intuitive for users of any age and capability to recognize contributions, build
trust networks, and organize collective action. It is built on services that
preserve privacy and data sovereignty.
Time Safari is an application designed to foster community building through gifts, gratitude, and collaborative projects. The app should make it extremely easy and intuitive for users of any age and capability to recognize contributions, build trust networks, and organize collective action. It is built on services that preserve privacy and data sovereignty.
The ultimate goals of Time Safari are two-fold:
1. **Connect** Make it easy, rewarding, and non-threatening for people to
connect with others who have similar interests, and to initiate activities
together. This helps people accomplish and learn from other individuals in
less-structured environments; moreover, it helps them discover who they want
to continue to support and with whom they want to maintain relationships.
1. **Connect** Make it easy, rewarding, and non-threatening for people to connect with others who have similar interests, and to initiate activities together. This helps people accomplish and learn from other individuals in less-structured environments; moreover, it helps them discover who they want to continue to support and with whom they want to maintain relationships.
2. **Reveal** Widely advertise the great support and rewards that are being
given and accepted freely, especially non-monetary ones. Using visuals and text,
display the kind of impact that gifts are making in the lives of others. Also
show useful and engaging reports of project statistics and personal accomplishments.
2. **Reveal** Widely advertise the great support and rewards that are being given and accepted freely, especially non-monetary ones. Using visuals and text, display the kind of impact that gifts are making in the lives of others. Also show useful and engaging reports of project statistics and personal accomplishments.
## Core Approaches
Time Safari should help everyday users build meaningful connections and organize
collective efforts by:
Time Safari should help everyday users build meaningful connections and organize collective efforts by:
1. **Recognizing Contributions**: Creating permanent, verifiable records of gifts
and contributions people give to each other and their communities.
1. **Recognizing Contributions**: Creating permanent, verifiable records of gifts and contributions people give to each other and their communities.
2. **Facilitating Collaboration**: Making it ridiculously easy for people to ask
for or propose help on projects and interests that matter to them.
2. **Facilitating Collaboration**: Making it ridiculously easy for people to ask for or propose help on projects and interests that matter to them.
3. **Building Trust Networks**: Enabling users to maintain their network and activity
visibility. Developing reputation through verified contributions and references,
which can be selectively shown to others outside the network.
3. **Building Trust Networks**: Enabling users to maintain their network and activity visibility. Developing reputation through verified contributions and references, which can be selectively shown to others outside the network.
4. **Preserving Privacy**: Ensuring personal identifiers are only shared with
explicitly authorized contacts, allowing private individuals including children
to participate safely.
4. **Preserving Privacy**: Ensuring personal identifiers are only shared with explicitly authorized contacts, allowing private individuals including children to participate safely.
5. **Engaging Content**: Displaying people's records in compelling stories, and
highlighting those projects that are lifting people's lives long-term, both in
physical support and in emotional-spiritual-creative thriving.
5. **Engaging Content**: Displaying people's records in compelling stories, and highlighting those projects that are lifting people's lives long-term, both in physical support and in emotional-spiritual-creative thriving.
## Technical Foundation
This application is built on a privacy-preserving claims architecture (via
endorser.ch) with these key characteristics:
This application is built on a privacy-preserving claims architecture (via endorser.ch) with these key characteristics:
- **Decentralized Identifiers (DIDs)**: User identities are based on public/private
key pairs stored on their devices
- **Cryptographic Verification**: All claims and confirmations are
cryptographically signed
- **User-Controlled Visibility**: Users explicitly control who can see their
identifiers and data
- **Merkle-Chained Claims**: Claims are cryptographically chained for verification
and integrity
- **Native and Web App**: Works on Capacitor (iOS, Android), Desktop (Electron
and CEFPython), and web browsers
- **Decentralized Identifiers (DIDs)**: User identities are based on public/private key pairs stored on their devices
- **Cryptographic Verification**: All claims and confirmations are cryptographically signed
- **User-Controlled Visibility**: Users explicitly control who can see their identifiers and data
- **Merkle-Chained Claims**: Claims are cryptographically chained for verification and integrity
- **Native and Web App**: Works on Capacitor (iOS, Android), Desktop (Electron and CEFPython), and web browsers
## User Journey
The typical progression of usage follows these stages:
1. **Gratitude & Recognition**: Users begin by expressing and recording gratitude
for gifts received, building a foundation of acknowledgment.
1. **Gratitude & Recognition**: Users begin by expressing and recording gratitude for gifts received, building a foundation of acknowledgment.
2. **Project Proposals**: Users propose projects and ideas, reaching out to connect
with others who share similar interests.
2. **Project Proposals**: Users propose projects and ideas, reaching out to connect with others who share similar interests.
3. **Action Triggers**: Offers of help serve as triggers and motivations to execute
proposed projects, moving from ideas to action.
3. **Action Triggers**: Offers of help serve as triggers and motivations to execute proposed projects, moving from ideas to action.
## Context for LLM Development
When developing new functionality for Time Safari, consider these design principles:
1. **Accessibility First**: Features should be usable by non-technical users with
minimal learning curve.
1. **Accessibility First**: Features should be usable by non-technical users with minimal learning curve.
2. **Privacy by Design**: All features must respect user privacy and data sovereignty.
3. **Progressive Enhancement**: Core functionality should work across all devices,
with richer experiences where supported.
3. **Progressive Enhancement**: Core functionality should work across all devices, with richer experiences where supported.
4. **Voluntary Collaboration**: The system should enable but never coerce participation.
@@ -98,40 +72,31 @@ with richer experiences where supported.
6. **Network Effects**: Consider how features scale as more users join the platform.
7. **Low Resource Requirements**: The system should be lightweight enough to run
on inexpensive devices users already own.
7. **Low Resource Requirements**: The system should be lightweight enough to run on inexpensive devices users already own.
## Use Cases to Support
LLM development should focus on enhancing these key use cases:
1. **Community Building**: Tools that help people find others with shared
interests and values.
1. **Community Building**: Tools that help people find others with shared interests and values.
2. **Project Coordination**: Features that make it easy to propose collaborative
projects and to submit suggestions and offers to existing ones.
2. **Project Coordination**: Features that make it easy to propose collaborative projects and to submit suggestions and offers to existing ones.
3. **Reputation Building**: Methods for users to showcase their contributions
and reliability, in contexts where they explicitly reveal that information.
3. **Reputation Building**: Methods for users to showcase their contributions and reliability, in contexts where they explicitly reveal that information.
4. **Governance Experimentation**: Features that facilitate decision-making and
collective governance.
4. **Governance Experimentation**: Features that facilitate decision-making and collective governance.
## Constraints
When developing new features, be mindful of these constraints:
1. **Privacy Preservation**: User identifiers must remain private except when
explicitly shared.
1. **Privacy Preservation**: User identifiers must remain private except when explicitly shared.
2. **Platform Limitations**: Features must work within the constraints of the target
app platforms, while aiming to leverage the best platform technology available.
2. **Platform Limitations**: Features must work within the constraints of the target app platforms, while aiming to leverage the best platform technology available.
3. **Endorser API Limitations**: Backend features are constrained by the endorser.ch
API capabilities.
3. **Endorser API Limitations**: Backend features are constrained by the endorser.ch API capabilities.
4. **Performance on Low-End Devices**: The application should remain performant
on older/simpler devices.
4. **Performance on Low-End Devices**: The application should remain performant on older/simpler devices.
5. **Offline-First When Possible**: Key functionality should work offline when feasible.
@@ -151,14 +116,12 @@ on older/simpler devices.
## Project Architecture
- The application must work on web browser, PWA (Progressive Web Application),
desktop via Electron, and mobile via Capacitor
- The application must work on web browser, PWA (Progressive Web Application), desktop via Electron, and mobile via Capacitor
- Building for each platform is managed via Vite
## Core Development Principles
### DRY development
- **Code Reuse**
- Extract common functionality into utility functions
- Create reusable components for UI patterns
@@ -214,24 +177,14 @@ on older/simpler devices.
- Use shared test configurations
- Create reusable test helpers
- Implement consistent test patterns
- F.I.R.S.T. (for Unit Tests)
F Fast
I Independent
R Repeatable
S Self-validating
T Timely
### SOLID Principles
- **Single Responsibility**: Each class/component should have only one reason to
change
- **Single Responsibility**: Each class/component should have only one reason to change
- Components should focus on one specific feature (e.g., QR scanning, DID management)
- Services should handle one type of functionality (e.g., platform services,
crypto services)
- Services should handle one type of functionality (e.g., platform services, crypto services)
- Utilities should provide focused helper functions
- **Open/Closed**: Software entities should be open for extension but closed for
modification
- **Open/Closed**: Software entities should be open for extension but closed for modification
- Use interfaces for service definitions
- Implement plugin architecture for platform-specific features
- Allow component behavior extension through props and events
@@ -252,7 +205,6 @@ on older/simpler devices.
- Implement factory patterns for component creation
### Law of Demeter
- Components should only communicate with immediate dependencies
- Avoid chaining method calls (e.g., `this.service.getUser().getProfile().getName()`)
- Use mediator patterns for complex component interactions
@@ -260,7 +212,6 @@ on older/simpler devices.
- Keep component communication through defined events and props
### Composition over Inheritance
- Prefer building components through composition
- Use mixins for shared functionality
- Implement feature toggles through props
@@ -268,7 +219,6 @@ on older/simpler devices.
- Use service composition for complex features
### Interface Segregation
- Define clear interfaces for services
- Keep component APIs minimal and focused
- Split large interfaces into smaller, specific ones
@@ -276,7 +226,6 @@ on older/simpler devices.
- Implement role-based interfaces for different use cases
### Fail Fast
- Validate inputs early in the process
- Use TypeScript strict mode
- Implement comprehensive error handling
@@ -284,7 +233,6 @@ on older/simpler devices.
- Use assertions for development-time validation
### Principle of Least Astonishment
- Follow Vue.js conventions consistently
- Use familiar naming patterns
- Implement predictable component behaviors
@@ -292,7 +240,6 @@ on older/simpler devices.
- Keep UI interactions intuitive
### Information Hiding
- Encapsulate implementation details
- Use private class members
- Implement proper access modifiers
@@ -300,7 +247,6 @@ on older/simpler devices.
- Use TypeScript's access modifiers effectively
### Single Source of Truth
- Use Pinia for state management
- Maintain one source for user data
- Centralize configuration management
@@ -308,9 +254,23 @@ on older/simpler devices.
- Implement proper state synchronization
### Principle of Least Privilege
- Implement proper access control
- Use minimal required permissions
- Follow privacy-by-design principles
- Restrict component access to necessary data
- Implement proper authentication/authorization
### Continuous Integration/Continuous Deployment (CI/CD)
- Automated testing on every commit
- Consistent build process across platforms
- Automated deployment pipelines
- Quality gates for code merging
- Environment-specific configurations
This expanded documentation provides:
1. Clear principles for development
2. Practical implementation guidelines
3. Real-world examples
4. TypeScript integration
5. Best practices for Time Safari

View File

@@ -1,34 +0,0 @@
---
alwaysApply: true
---
# Rules for peaceful co-existence with developers
do not add or commit for the user; let him control that process
the content of commit messages should be from the files awaiting staging
and those which have been staged. use the differences in those files
to inform the content of the commit message
always preview changes and commit message to use and allow me to copy and paste
✅ Preferred Commit Message Format
Short summary in the first line (concise and high-level).
Avoid long commit bodies unless truly necessary.
✅ Valued Content in Commit Messages
Specific fixes or features.
Symptoms or problems that were fixed.
Notes about tests passing or TS/linting errors being resolved (briefly).
❌ Avoid in Commit Messages
Vague terms: “improved”, “enhanced”, “better” — especially from AI.
Minor changes: small doc tweaks, one-liners, cleanup, or lint fixes.
Redundant blurbs: repeated across files or too generic.
Multiple overlapping purposes in a single commit — prefer narrow, focused commits.
Long explanations of what can be deduced from good in-line code comments.
Guiding Principle
Let code and inline documentation speak for themselves. Use commits to highlight what isn't obvious from reading the code.

View File

@@ -15,7 +15,7 @@ yarn-debug.log*
yarn-error.log*
# Build outputs
# dist - Allow dist directory for Docker builds (contains pre-built assets)
dist
dist-*
build
*.tsbuildinfo

31
.gitignore vendored
View File

@@ -55,23 +55,7 @@ build_logs/
icons
*.log
# Generated Android assets and resources (should be generated during build)
android/app/src/main/assets/public/
# Generated Android resources (icons, splash screens, etc.)
android/app/src/main/res/drawable*/
android/app/src/main/res/mipmap*/
android/app/src/main/res/values/ic_launcher_background.xml
# Keep these Android configuration files in version control:
# - android/app/src/main/assets/capacitor.plugins.json
# - android/app/src/main/res/values/strings.xml
# - android/app/src/main/res/values/styles.xml
# - android/app/src/main/res/layout/activity_main.xml
# - android/app/src/main/res/xml/config.xml
# - android/app/src/main/res/xml/file_paths.xml
android/app/src/main/res/
sql-wasm.wasm
# Temporary and generated files
@@ -99,7 +83,11 @@ ios/App/App/public/assets/
ios/App/App/build/
ios/App/build/
# Capacitor build artifacts (covered by android/app/build/ above)
# Capacitor generated configs (keep source configs)
android/app/build/intermediates/assets/debug/mergeDebugAssets/capacitor.*.json
android/app/build/intermediates/compressed_assets/debug/compressDebugAssets/out/assets/capacitor.*.json.jar
android/app/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-capacitor-cordova-android-plugins.jar
android/app/build/outputs/aar/capacitor-cordova-android-plugins-debug.aar
# Keep these Capacitor files in version control:
# - capacitor.config.json (root, electron, ios)
@@ -123,9 +111,4 @@ electron/out/
# - electron/electron-builder.config.json
# - electron/build-packages.sh
# - electron/live-runner.js
# - electron/resources/electron-publisher-custom.js
# Gradle cache files
android/.gradle/file-system.probe
android/.gradle/caches/
coverage
# - electron/resources/electron-publisher-custom.js

File diff suppressed because it is too large Load Diff

View File

@@ -36,10 +36,6 @@
# Environment Variables:
# NODE_ENV: Build environment (development/production)
# BUILD_MODE: Build mode for asset selection (development/test/production)
#
# Build Context:
# This Dockerfile is designed to work when the build context is set to
# ./crowd-funder-for-time-pwa from the parent directory (where docker-compose.yml is located)
# =============================================================================
# BASE STAGE - Common dependencies and setup
@@ -66,7 +62,6 @@ RUN addgroup -g 1001 -S nodejs && \
WORKDIR /app
# Copy package files for dependency installation
# Note: These files are in the project root (crowd-funder-for-time-pwa directory)
COPY package*.json ./
# Install dependencies with security audit
@@ -87,7 +82,6 @@ ENV BUILD_MODE=${BUILD_MODE}
ENV NODE_ENV=${NODE_ENV}
# Copy pre-built assets from host
# Note: dist/ directory is in the project root (crowd-funder-for-time-pwa directory)
COPY dist/ ./dist/
# Verify build output exists
@@ -113,21 +107,23 @@ RUN apk update && \
curl \
&& rm -rf /var/cache/apk/*
# Use existing nginx user from base image (nginx user and group already exist)
# No need to create new user as nginx:alpine already has nginx user
# Create non-root user for nginx
RUN addgroup -g 1001 -S nginx && \
adduser -S nginx -u 1001 -G nginx
# Copy main nginx configuration
# Copy appropriate nginx configuration based on build mode
COPY docker/nginx.conf /etc/nginx/nginx.conf
# Copy production nginx configuration
COPY docker/default.conf /etc/nginx/conf.d/default.conf
# Copy staging configuration if needed
COPY docker/staging.conf /etc/nginx/conf.d/staging.conf
# Copy built assets from builder stage
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
# Create necessary directories with proper permissions
RUN mkdir -p /var/cache/nginx /var/log/nginx /tmp && \
chown -R nginx:nginx /var/cache/nginx /var/log/nginx /tmp && \
RUN mkdir -p /var/cache/nginx /var/log/nginx /var/run && \
chown -R nginx:nginx /var/cache/nginx /var/log/nginx /var/run && \
chown -R nginx:nginx /usr/share/nginx/html
# Switch to non-root user
@@ -143,6 +139,8 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
# Start nginx with proper signal handling
CMD ["nginx", "-g", "daemon off;"]
# =============================================================================
# TEST STAGE - For test environment testing
# =============================================================================

View File

@@ -1,4 +1,5 @@
source "https://rubygems.org"
gem "fastlane"
gem "cocoapods"

View File

@@ -22,7 +22,26 @@ GEM
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.2)
aws-partitions (1.1066.0)
aws-sdk-core (3.220.1)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.99.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.182.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.11.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
benchmark (0.4.0)
bigdecimal (3.1.9)
@@ -64,13 +83,96 @@ GEM
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
concurrent-ruby (1.3.5)
connection_pool (2.5.0)
declarative (0.0.20)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
drb (2.2.1)
emoji_regex (3.2.3)
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
excon (0.112.0)
faraday (1.10.4)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.1.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
fastlane (2.227.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored (~> 1.2)
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
fastlane-sirp (>= 1.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-env (>= 1.6.0, < 2.0.0)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
http-cookie (~> 1.0.5)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
optparse (>= 0.1.1, < 1.0.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.5)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (~> 3)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.4.0)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
ffi (1.17.1)
ffi (1.17.1-aarch64-linux-gnu)
ffi (1.17.1-aarch64-linux-musl)
@@ -85,27 +187,107 @@ GEM
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.3)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.5.0)
google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.31.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.8.1)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.8)
domain_name (~> 0.5)
httpclient (2.9.0)
mutex_m
i18n (1.14.7)
concurrent-ruby (~> 1.0)
jmespath (1.6.2)
json (2.10.2)
jwt (2.10.1)
base64
logger (1.6.6)
mini_magick (4.13.2)
mini_mime (1.1.5)
minitest (5.25.5)
molinillo (0.8.0)
multi_json (1.15.0)
multipart-post (2.4.1)
mutex_m (0.3.0)
nanaimo (0.4.0)
nap (1.1.0)
naturally (2.2.1)
netrc (0.11.0)
nkf (0.2.0)
optparse (0.6.0)
os (1.1.4)
plist (3.7.2)
public_suffix (4.0.7)
rake (13.2.1)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.4.1)
rouge (3.28.0)
ruby-macho (2.5.1)
ruby2_keywords (0.0.5)
rubyzip (2.4.1)
securerandom (0.4.1)
security (0.1.5)
signet (0.19.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.10)
CFPropertyList
naturally
sysrandom (1.0.5)
terminal-notifier (2.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.2)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uber (0.1.0)
unicode-display_width (2.6.0)
word_wrap (1.0.0)
xcodeproj (1.27.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
@@ -113,6 +295,10 @@ GEM
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.4.0)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
aarch64-linux-gnu
@@ -129,6 +315,7 @@ PLATFORMS
DEPENDENCIES
cocoapods
fastlane
BUNDLED WITH
2.6.5

View File

@@ -97,37 +97,15 @@ rmdir /s /q %APPDATA%\TimeSafari
```bash
# Create isolated browser profile
mkdir ~/timesafari-dev-data
# Start browser with custom profile
google-chrome --user-data-dir=~/timesafari-dev-data
# Clear when needed
rm -rf ~/timesafari-dev-data
```
## Domain Configuration
TimeSafari uses a centralized domain configuration system to ensure consistent
URL generation across all environments. This prevents localhost URLs from
appearing in shared links during development.
### Key Features
-**Production URLs for Sharing**: All copy link buttons use production domain
-**Environment-Specific Internal URLs**: Internal operations use appropriate
environment URLs
-**Single Point of Control**: Change domain in one place for entire app
-**Type-Safe Configuration**: Full TypeScript support
### Quick Reference
```typescript
// For sharing functionality (environment-specific)
import { APP_SERVER } from "@/constants/app";
const shareLink = `${APP_SERVER}/deep-link/claim/123`;
// For internal operations (environment-specific)
import { APP_SERVER } from "@/constants/app";
const apiUrl = `${APP_SERVER}/api/claim/123`;
```
### Documentation
- [Domain Configuration System](docs/domain-configuration.md) - Complete guide
- [Constants and Configuration](src/constants/app.ts) - Core constants
See the script for complete platform-specific instructions.
## Tests

7
android/.gitignore vendored
View File

@@ -84,6 +84,13 @@ freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml

View File

@@ -64,14 +64,6 @@ android {
}
}
}
packagingOptions {
jniLibs {
pickFirsts += ['**/lib/x86_64/libbarhopper_v3.so', '**/lib/x86_64/libimage_processing_util_jni.so', '**/lib/x86_64/libsqlcipher.so']
}
}
// Configure for 16 KB page size compatibility
// Enable bundle builds (without which it doesn't work right for bundleDebug vs bundleRelease)
bundle {

View File

@@ -57,14 +57,13 @@
]
},
"android": {
"allowMixedContent": true,
"allowMixedContent": false,
"captureInput": true,
"webContentsDebuggingEnabled": false,
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch",
"10.0.2.2:3000"
"api.endorser.ch"
]
},
"electron": {

View File

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2480 4005 c-25 -7 -58 -20 -75 -29 -16 -9 -40 -16 -52 -16 -17 0
-24 -7 -28 -27 -3 -16 -14 -45 -24 -65 -21 -41 -13 -55 18 -38 25 13 67 13 92
-1 15 -8 35 -4 87 17 99 39 130 41 197 10 64 -29 77 -31 107 -15 20 11 20 11
-3 35 -12 13 -30 24 -38 24 -24 1 -132 38 -148 51 -8 7 -11 20 -7 32 12 37
-40 47 -126 22z"/>
<path d="M1450 3775 c-7 -8 -18 -15 -24 -15 -7 0 -31 -14 -54 -32 -29 -22 -38
-34 -29 -40 17 -11 77 -10 77 1 0 5 16 16 35 25 60 29 220 19 290 -18 17 -9
33 -16 37 -16 4 0 31 -15 60 -34 108 -70 224 -215 282 -353 30 -71 53 -190 42
-218 -10 -27 -23 -8 -52 75 -30 90 -88 188 -120 202 -13 6 -26 9 -29 6 -3 -2
11 -51 30 -108 28 -83 35 -119 35 -179 0 -120 -22 -127 -54 -17 -11 37 -13 21
-18 -154 -5 -180 -8 -200 -32 -264 -51 -132 -129 -245 -199 -288 -21 -12 -79
-49 -129 -80 -161 -102 -294 -141 -473 -141 -228 0 -384 76 -535 259 -81 99
-118 174 -154 312 -31 121 -35 273 -11 437 19 127 19 125 -4 125 -23 0 -51
-34 -87 -104 -14 -28 -33 -64 -41 -81 -19 -34 -22 -253 -7 -445 9 -106 12
-119 44 -170 19 -30 42 -67 50 -81 64 -113 85 -140 130 -169 28 -18 53 -44 61
-62 8 -20 36 -45 83 -76 62 -39 80 -46 151 -54 44 -5 96 -13 115 -18 78 -20
238 -31 282 -19 24 6 66 8 95 5 76 -9 169 24 319 114 32 19 80 56 106 82 27
26 52 48 58 48 5 0 27 26 50 58 48 66 56 70 132 71 62 1 165 29 238 64 112 55
177 121 239 245 37 76 39 113 10 267 -12 61 -23 131 -26 156 -5 46 -5 47 46
87 92 73 182 70 263 -8 l51 -49 -6 -61 c-4 -34 -13 -85 -21 -113 -28 -103 -30
-161 -4 -228 16 -44 32 -67 55 -83 18 -11 39 -37 47 -58 10 -23 37 -53 73 -81
32 -25 69 -57 82 -71 14 -14 34 -26 47 -26 12 0 37 -7 56 -15 20 -8 66 -17
104 -20 107 -10 110 -11 150 -71 50 -75 157 -177 197 -187 18 -5 53 -24 78
-42 71 -51 176 -82 304 -89 61 -4 127 -12 147 -18 29 -9 45 -8 77 6 23 9 50
16 60 16 31 0 163 46 216 76 28 15 75 46 105 69 30 23 69 49 85 58 17 8 46 31
64 51 19 20 40 36 47 36 18 0 77 70 100 120 32 66 45 108 55 173 5 32 16 71
24 87 43 84 43 376 0 549 -27 105 -43 127 -135 188 -30 21 -65 46 -77 57 -13
11 -23 17 -23 14 0 -3 21 -46 47 -94 79 -151 85 -166 115 -263 25 -83 28 -110
28 -226 0 -144 -17 -221 -75 -335 -39 -77 -208 -244 -304 -299 -451 -263 -975
-67 -1138 426 -23 70 -26 95 -28 254 -1 108 -7 183 -14 196 -6 12 -11 31 -11
43 0 32 31 122 52 149 10 13 18 28 18 34 0 5 25 40 56 78 60 73 172 170 219
190 30 12 30 13 6 17 -15 2 -29 -2 -37 -12 -6 -9 -16 -16 -22 -16 -6 0 -23
-11 -39 -24 -15 -12 -33 -25 -40 -27 -17 -6 -82 -60 -117 -97 -65 -70 -75 -82
-107 -133 -23 -34 -35 -46 -37 -35 -3 16 20 87 44 134 6 12 9 34 6 48 -4 22
-8 25 -31 19 -14 -3 -38 -15 -53 -26 -34 -24 -34 -21 -6 28 65 112 184 206
291 227 15 3 39 9 55 12 l27 6 -24 9 c-90 35 -304 -66 -478 -225 -39 -36 -74
-66 -77 -66 -22 0 18 82 72 148 19 23 32 46 28 49 -4 4 -26 13 -49 19 -73 21
-161 54 -171 64 -6 6 -20 10 -32 10 -21 0 -21 -1 -8 -40 45 -130 8 -247 -93
-299 -25 -13 -31 0 -14 29 15 22 1 33 -22 17 -56 -36 -117 -22 -117 28 0 13
-16 47 -35 76 -22 34 -33 60 -29 73 4 16 -3 26 -26 39 -16 10 -30 21 -30 25 1
18 54 64 87 76 l38 13 -33 5 c-30 4 -115 -18 -154 -42 -13 -7 -20 -5 -27 8 -9
16 -12 16 -53 1 -160 -61 -258 -104 -258 -114 0 -7 10 -20 21 -31 103 -91 217
-297 249 -449 28 -135 41 -237 35 -276 -14 -91 -48 -170 -97 -220 -44 -47 -68
-60 -68 -40 0 6 4 12 8 15 5 3 24 35 42 72 l33 67 -6 141 c-4 103 -11 158 -26
205 -12 35 -21 70 -21 77 0 7 -20 56 -45 108 -82 173 -227 322 -392 401 -67
33 -90 39 -163 42 -108 5 -130 10 -130 28 0 20 -63 20 -80 0z"/>
<path d="M3710 3765 c0 -20 8 -28 39 -41 22 -8 42 -22 45 -30 5 -14 42 -19 70
-8 10 4 -7 21 -58 55 -41 27 -79 49 -85 49 -6 0 -11 -11 -11 -25z"/>
<path d="M3173 3734 c-9 -25 10 -36 35 -18 12 8 22 19 22 25 0 16 -50 10 -57
-7z"/>
<path d="M1982 3728 c6 -16 36 -34 44 -26 3 4 4 14 1 23 -7 17 -51 21 -45 3z"/>
<path d="M1540 3620 c0 -5 7 -10 16 -10 8 0 12 5 9 10 -3 6 -10 10 -16 10 -5
0 -9 -4 -9 -10z"/>
<path d="M4467 3624 c-4 -4 23 -27 60 -50 84 -56 99 -58 67 -9 -28 43 -107 79
-127 59z"/>
<path d="M655 3552 c-11 -2 -26 -9 -33 -14 -7 -6 -27 -18 -45 -27 -36 -18 -58
-64 -39 -83 9 -9 25 1 70 43 53 48 78 78 70 84 -2 1 -12 -1 -23 -3z"/>
<path d="M1015 3460 c-112 -24 -247 -98 -303 -165 -53 -65 -118 -214 -136
-311 -20 -113 -20 -145 -1 -231 20 -88 49 -153 102 -230 79 -113 186 -182 331
-214 108 -24 141 -24 247 1 130 30 202 72 316 181 102 100 153 227 152 384 0
142 -58 293 -150 395 -60 67 -180 145 -261 171 -75 23 -232 34 -297 19z m340
-214 c91 -43 174 -154 175 -234 0 -18 -9 -51 -21 -73 -19 -37 -19 -42 -5 -64
35 -54 12 -121 -48 -142 -22 -7 -47 -19 -55 -27 -9 -8 -41 -27 -71 -42 -50
-26 -64 -29 -155 -29 -111 0 -152 14 -206 68 -49 49 -63 85 -64 162 0 59 4 78
28 118 31 52 96 105 141 114 23 5 33 17 56 68 46 103 121 130 225 81z"/>
<path d="M3985 3464 c-44 -7 -154 -44 -200 -67 -55 -28 -138 -96 -162 -132
-10 -16 -39 -75 -64 -130 l-44 -100 0 -160 0 -160 45 -90 c53 -108 152 -214
245 -264 59 -31 215 -71 281 -71 53 0 206 40 255 67 98 53 203 161 247 253 53
113 74 193 74 280 -1 304 -253 564 -557 575 -49 2 -103 1 -120 -1z m311 -220
c129 -68 202 -209 160 -309 -15 -35 -15 -42 -1 -72 26 -55 -3 -118 -59 -129
-19 -3 -43 -15 -53 -26 -26 -29 -99 -64 -165 -78 -45 -10 -69 -10 -120 -1 -74
15 -113 37 -161 91 -110 120 -50 331 109 385 24 8 44 23 52 39 6 14 18 38 25
53 33 72 127 93 213 47z"/>
<path d="M487 3394 c-21 -12 -27 -21 -25 -40 2 -14 7 -26 12 -27 14 -3 48 48
44 66 -3 14 -6 14 -31 1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 KiB

View File

@@ -0,0 +1,11 @@
Model Information:
* title: Lupine Plant
* source: https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439
* author: rufusrockwell (https://sketchfab.com/rufusrockwell)
Model License:
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
* requirements: Author must be credited. Commercial use is allowed.
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
This work is based on "Lupine Plant" (https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439) by rufusrockwell (https://sketchfab.com/rufusrockwell) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)

View File

@@ -0,0 +1,229 @@
{
"accessors": [
{
"bufferView": 2,
"componentType": 5126,
"count": 2759,
"max": [
41.3074951171875,
40.37548828125,
87.85917663574219
],
"min": [
-35.245540618896484,
-36.895416259765625,
-0.9094290137290955
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 33108,
"componentType": 5126,
"count": 2759,
"max": [
0.9999382495880127,
0.9986748695373535,
0.9985831379890442
],
"min": [
-0.9998949766159058,
-0.9975876212120056,
-0.411094069480896
],
"type": "VEC3"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 2759,
"max": [
0.9987699389457703,
0.9998998045921326,
0.9577858448028564,
1.0
],
"min": [
-0.9987726807594299,
-0.9990445971488953,
-0.999801516532898,
1.0
],
"type": "VEC4"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 2759,
"max": [
1.0061479806900024,
0.9993550181388855
],
"min": [
0.00279300007969141,
0.0011620000004768372
],
"type": "VEC2"
},
{
"bufferView": 0,
"componentType": 5125,
"count": 6378,
"type": "SCALAR"
}
],
"asset": {
"extras": {
"author": "rufusrockwell (https://sketchfab.com/rufusrockwell)",
"license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)",
"source": "https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439",
"title": "Lupine Plant"
},
"generator": "Sketchfab-12.68.0",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 25512,
"name": "floatBufferViews",
"target": 34963
},
{
"buffer": 0,
"byteLength": 22072,
"byteOffset": 25512,
"byteStride": 8,
"name": "floatBufferViews",
"target": 34962
},
{
"buffer": 0,
"byteLength": 66216,
"byteOffset": 47584,
"byteStride": 12,
"name": "floatBufferViews",
"target": 34962
},
{
"buffer": 0,
"byteLength": 44144,
"byteOffset": 113800,
"byteStride": 16,
"name": "floatBufferViews",
"target": 34962
}
],
"buffers": [
{
"byteLength": 157944,
"uri": "scene.bin"
}
],
"images": [
{
"uri": "textures/lambert2SG_baseColor.png"
},
{
"uri": "textures/lambert2SG_normal.png"
}
],
"materials": [
{
"alphaCutoff": 0.2,
"alphaMode": "MASK",
"doubleSided": true,
"name": "lambert2SG",
"normalTexture": {
"index": 1
},
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicFactor": 0.0
}
}
],
"meshes": [
{
"name": "Object_0",
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 0,
"TANGENT": 2,
"TEXCOORD_0": 3
},
"indices": 4,
"material": 0,
"mode": 4
}
]
}
],
"nodes": [
{
"children": [
1
],
"matrix": [
1.0,
0.0,
0.0,
0.0,
0.0,
2.220446049250313e-16,
-1.0,
0.0,
0.0,
1.0,
2.220446049250313e-16,
0.0,
0.0,
0.0,
0.0,
1.0
],
"name": "Sketchfab_model"
},
{
"children": [
2
],
"name": "LupineSF.obj.cleaner.materialmerger.gles"
},
{
"mesh": 0,
"name": "Object_2"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9987,
"wrapS": 10497,
"wrapT": 10497
}
],
"scene": 0,
"scenes": [
{
"name": "Sketchfab_Scene",
"nodes": [
0
]
}
],
"textures": [
{
"sampler": 0,
"source": 0
},
{
"sampler": 0,
"source": 1
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background>
<inset android:drawable="@mipmap/ic_launcher_background" android:inset="16.7%" />
</background>
<foreground>
<inset android:drawable="@mipmap/ic_launcher_foreground" android:inset="16.7%" />
</foreground>
</adaptive-icon>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background>
<inset android:drawable="@mipmap/ic_launcher_background" android:inset="16.7%" />
</background>
<foreground>
<inset android:drawable="@mipmap/ic_launcher_foreground" android:inset="16.7%" />
</foreground>
</adaptive-icon>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View File

@@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.12.0'
classpath 'com.android.tools.build:gradle:8.11.0'
classpath 'com.google.gms:google-services:4.4.0'
// NOTE: Do not place your application dependencies here; they belong

View File

@@ -20,4 +20,4 @@ org.gradle.jvmargs=-Xmx1536m
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
android.suppressUnsupportedCompileSdk=36
android.suppressUnsupportedCompileSdk=34

View File

@@ -1,7 +1,7 @@
ext {
minSdkVersion = 22
compileSdkVersion = 36
targetSdkVersion = 36
compileSdkVersion = 34
targetSdkVersion = 34
androidxActivityVersion = '1.8.0'
androidxAppCompatVersion = '1.6.1'
androidxCoordinatorLayoutVersion = '1.2.0'

View File

@@ -1,36 +0,0 @@
{
"icon": {
"ios": {
"source": "resources/ios/icon/icon.png",
"target": "ios/App/App/Assets.xcassets/AppIcon.appiconset"
},
"android": {
"source": "resources/android/icon/icon.png",
"target": "android/app/src/main/res"
},
"web": {
"source": "resources/web/icon/icon.png",
"target": "public/img/icons"
}
},
"splash": {
"ios": {
"source": "resources/ios/splash/splash.png",
"target": "ios/App/App/Assets.xcassets/Splash.imageset"
},
"android": {
"source": "resources/android/splash/splash.png",
"target": "android/app/src/main/res"
}
},
"splashDark": {
"ios": {
"source": "resources/ios/splash/splash_dark.png",
"target": "ios/App/App/Assets.xcassets/SplashDark.imageset"
},
"android": {
"source": "resources/android/splash/splash_dark.png",
"target": "android/app/src/main/res"
}
}
}

View File

@@ -57,14 +57,13 @@
]
},
"android": {
"allowMixedContent": true,
"allowMixedContent": false,
"captureInput": true,
"webContentsDebuggingEnabled": false,
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch",
"10.0.2.2:3000"
"api.endorser.ch"
]
},
"electron": {

View File

@@ -54,16 +54,14 @@ server {
}
# Handle API requests (if needed)
# Note: Backend API is not currently deployed
# Uncomment and configure when backend service is available
# location /api/ {
# limit_req zone=api burst=20 nodelay;
# proxy_pass http://backend:3000;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# }
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Handle health check
location /health {

View File

@@ -9,10 +9,10 @@
# - Static file caching optimization
# - Security hardening
# user nginx; # Commented out - nginx runs as non-root user in container
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /tmp/nginx.pid; # Use /tmp for PID file to avoid permission issues
pid /var/run/nginx.pid;
events {
worker_connections 1024;

View File

@@ -54,16 +54,14 @@ server {
}
# Handle API requests (if needed)
# Note: Backend API is not currently deployed
# Uncomment and configure when backend service is available
# location /api/ {
# limit_req zone=api burst=20 nodelay;
# proxy_pass http://backend:3000;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# }
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Handle health check
location /health {

View File

@@ -1,115 +0,0 @@
# TimeSafari Documentation
**Author**: Matthew Raymer
**Date**: 2025-01-27
**Status**: 🎯 **COMPLETE** - Documentation organized and structured
## Documentation Structure
This documentation is organized into logical categories to ensure easy navigation and maintenance. Each folder contains no more than 7 items to maintain clarity and usability.
### 📚 User Guides (`user-guides/`)
Documentation for end users and potential users of TimeSafari:
- User Guide - Comprehensive explanation of TimeSafari's purpose and features
- Quick Start Guide - Immediate actionable steps for new users
- Real-World Examples - Concrete stories of community transformation
### 🔧 Build System (`build-system/`)
Documentation for building and deploying TimeSafari across platforms:
- Build Systems Overview - Complete architecture of build processes
- Build Troubleshooting - Common issues and solutions
- Platform-specific build scripts and configurations
- Auto-run and automation guides
### 🔄 Migration (`migration/`)
Documentation for the database migration from Dexie to SQLite:
- Migration progress tracking and assessments
- Migration templates and best practices
- Component migration testing and validation
- Migration tools and utilities
### 💻 Development (`development/`)
Documentation for developers working on TimeSafari:
- Domain configuration and setup
- Development tools and utilities
- Code standards and templates
- Testing frameworks and practices
### 🏗️ Architecture (`architecture/`)
High-level system design and architectural decisions:
- System architecture overview
- Design patterns and principles
- Integration guides
- Performance considerations
### 🧪 Testing (`testing/`)
Testing documentation and procedures:
- Test frameworks and tools
- Testing strategies and methodologies
- Quality assurance processes
- Performance testing guidelines
### 📖 Examples (`examples/`)
Code examples and implementation patterns:
- Implementation examples
- Best practice demonstrations
- Integration examples
- Troubleshooting examples
## Documentation Standards
### File Organization
- **Maximum 7 items per folder**: Ensures easy navigation and maintenance
- **Logical grouping**: Related documents are grouped together
- **Clear naming**: File names clearly indicate content and purpose
- **Version control**: All changes are tracked in git with proper commit messages
### Documentation Quality
- **Rich documentation**: Comprehensive coverage at file, class, and method levels
- **Consistent formatting**: Follows established markdown standards
- **Regular updates**: Documentation is updated as code changes
- **User-focused**: Content is written for the intended audience
### Maintenance
- **Regular reviews**: Documentation is reviewed and updated regularly
- **Feedback integration**: User feedback is incorporated into documentation
- **Cross-references**: Related documents are properly linked
- **Searchability**: Content is organized for easy discovery
## Getting Started
### For Users
1. Start with the [Quick Start Guide](user-guides/quick-start-guide.md)
2. Read the [User Guide](user-guides/user-guide.md) for comprehensive understanding
3. Explore [Real-World Examples](user-guides/real-world-examples.md) for inspiration
### For Developers
1. Review the [Build System Overview](build-system/build-systems-overview.md)
2. Check [Development Setup](development/) for environment configuration
3. Understand the [Migration Process](migration/) if working on database changes
### For Contributors
1. Read the [Development Guidelines](development/)
2. Review [Testing Procedures](testing/)
3. Check [Architecture Decisions](architecture/)
## Contributing to Documentation
When adding or updating documentation:
1. **Choose the right folder**: Place documents in the most appropriate category
2. **Follow naming conventions**: Use clear, descriptive file names
3. **Maintain folder limits**: Create sub-folders if a folder exceeds 7 items
4. **Update this README**: Add new categories or reorganize as needed
5. **Version in git**: Commit documentation changes with clear messages
## Documentation Tools
- **Markdown**: All documentation uses markdown format
- **Git**: Version control for all documentation changes
- **Linting**: Markdown linting ensures consistent formatting
- **Validation**: Regular checks ensure documentation accuracy
---
*This documentation structure is designed to scale with the project while maintaining clarity and usability.*

View File

@@ -1,338 +0,0 @@
# Environment Variable Precedence and API Configuration
**Date:** August 4, 2025
**Author:** Matthew Raymer
## Overview
This document explains the order of precedence for environment variables in the
TimeSafari project, how `.env` files are used, and the API configuration scheme
for different environments.
## Order of Precedence (Highest to Lowest)
### 1. Shell Script Overrides (Highest Priority)
Shell scripts can override environment variables for platform-specific needs:
```bash
# scripts/common.sh - setup_build_env()
if [ "$BUILD_MODE" = "development" ]; then
export VITE_DEFAULT_ENDORSER_API_SERVER="http://localhost:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://localhost:3000"
fi
```
### 2. Platform-Specific Overrides (High Priority)
Platform-specific build scripts can override for mobile development:
```bash
# scripts/build-android.sh
if [ "$BUILD_MODE" = "development" ]; then
export VITE_DEFAULT_ENDORSER_API_SERVER="http://10.0.2.2:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://10.0.2.2:3000"
fi
```
### 3. Environment-Specific .env Files (Medium Priority)
Environment-specific `.env` files provide environment-specific defaults:
```bash
# .env.development, .env.test, .env.production
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000
VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000
```
### 4. Fallback .env File (Low Priority)
General `.env` file provides project-wide defaults:
```bash
# .env (if exists)
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000
```
### 5. app.ts Constants (Lowest Priority - Fallback)
Hardcoded constants in `src/constants/app.ts` provide safety nets:
```typescript
export const DEFAULT_ENDORSER_API_SERVER =
import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER ||
AppString.PROD_ENDORSER_API_SERVER;
```
## Build Process Flow
### 1. Shell Scripts Set Base Values
```bash
# scripts/common.sh
setup_build_env() {
if [ "$BUILD_MODE" = "development" ]; then
export VITE_DEFAULT_ENDORSER_API_SERVER="http://localhost:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://localhost:3000"
fi
}
```
### 2. Platform-Specific Overrides
```bash
# scripts/build-android.sh
if [ "$BUILD_MODE" = "development" ]; then
export VITE_DEFAULT_ENDORSER_API_SERVER="http://10.0.2.2:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://10.0.2.2:3000"
fi
```
### 3. Load .env Files
```bash
# scripts/build-web.sh
local env_file=".env.$BUILD_MODE" # .env.development, .env.test, .env.production
if [ -f "$env_file" ]; then
load_env_file "$env_file"
fi
# Fallback to .env
if [ -f ".env" ]; then
load_env_file ".env"
fi
```
### 4. Vite Processes Environment
```typescript
// vite.config.common.mts
dotenv.config(); // Loads .env files
```
### 5. Application Uses Values
```typescript
// src/constants/app.ts
export const DEFAULT_ENDORSER_API_SERVER =
import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER ||
AppString.PROD_ENDORSER_API_SERVER;
```
## API Configuration Scheme
### Environment Configuration Summary
| Environment | Endorser API (Claims) | Partner API | Image API |
|-------------|----------------------|-------------|-----------|
| **Development** | `http://localhost:3000` | `http://localhost:3000` | `https://image-api.timesafari.app` |
| **Test** | `https://test-api.endorser.ch` | `https://test-partner-api.endorser.ch` | `https://image-api.timesafari.app` |
| **Production** | `https://api.endorser.ch` | `https://partner-api.endorser.ch` | `https://image-api.timesafari.app` |
### Mobile Development Overrides
#### Android Development
- **Emulator**: `http://10.0.2.2:3000` (Android emulator default)
- **Physical Device**: `http://{CUSTOM_IP}:3000` (Custom IP for physical device)
#### iOS Development
- **Simulator**: `http://localhost:3000` (iOS simulator default)
- **Physical Device**: `http://{CUSTOM_IP}:3000` (Custom IP for physical device)
## .env File Structure
### .env.development
```bash
# ==========================================
# DEVELOPMENT ENVIRONMENT CONFIGURATION
# ==========================================
# API Server Configuration:
# - Endorser API (Claims): Local development server
# - Partner API: Local development server (aligned with claims)
# - Image API: Test server (shared for development)
# ==========================================
# Only the variables that start with VITE_ are seen in the application import.meta.env in Vue.
# iOS doesn't like spaces in the app title.
TIME_SAFARI_APP_TITLE="TimeSafari_Dev"
VITE_APP_SERVER=http://localhost:8080
# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production).
VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F
# API Servers (Development - Local)
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000
VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000
# Image API (Test server for development)
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app
# Push Server (disabled for localhost)
#VITE_DEFAULT_PUSH_SERVER... can't be set up with localhost domain
# Feature Flags
VITE_PASSKEYS_ENABLED=true
```
### .env.test
```bash
# ==========================================
# TEST ENVIRONMENT CONFIGURATION
# ==========================================
# API Server Configuration:
# - Endorser API (Claims): Test server
# - Partner API: Test server (aligned with claims)
# - Image API: Test server
# ==========================================
# Only the variables that start with VITE_ are seen in the application import.meta.env in Vue.
# iOS doesn't like spaces in the app title.
TIME_SAFARI_APP_TITLE="TimeSafari_Test"
VITE_APP_SERVER=https://test.timesafari.app
# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production).
VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F
# API Servers (Test Environment)
VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch
VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch
# Image API (Test server)
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app
# Push Server (Test)
VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app
# Feature Flags
VITE_PASSKEYS_ENABLED=true
```
### .env.production
```bash
# ==========================================
# PRODUCTION ENVIRONMENT CONFIGURATION
# ==========================================
# API Server Configuration:
# - Endorser API (Claims): Production server
# - Partner API: Production server (aligned with claims)
# - Image API: Production server
# ==========================================
# Only the variables that start with VITE_ are seen in the application import.meta.env in Vue.
# App Server
VITE_APP_SERVER=https://timesafari.app
# This is the claim ID for actions in the BVC project.
VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01GXYPFF7FA03NXKPYY142PY4H
# API Servers (Production Environment)
VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch
VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch
# Image API (Production server)
VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app
# Push Server (Production)
VITE_DEFAULT_PUSH_SERVER=https://timesafari.app
```
## Key Principles
### 1. API Alignment
- **Partner API** values follow the same pattern as **Claim API** (Endorser API)
- Both APIs use the same environment-specific endpoints
- This ensures consistency across the application
### 2. Platform Flexibility
- Shell scripts can override for platform-specific needs
- Android emulator uses `10.0.2.2:3000`
- iOS simulator uses `localhost:3000`
- Physical devices use custom IP addresses
### 3. Environment Isolation
- Each environment has its own `.env` file
- Test environment uses test APIs
- Development environment uses local APIs
- Production environment uses production APIs
### 4. Safety Nets
- Hardcoded constants in `app.ts` provide fallbacks
- Multiple layers of configuration prevent failures
- Clear precedence order ensures predictable behavior
## Usage Examples
### Development Build
```bash
# Uses .env.development + shell script overrides
npm run build:web -- --mode development
```
### Test Build
```bash
# Uses .env.test + shell script overrides
npm run build:web -- --mode test
```
### Production Build
```bash
# Uses .env.production + shell script overrides
npm run build:web -- --mode production
```
### Android Development
```bash
# Uses .env.development + Android-specific overrides
./scripts/build-android.sh --dev
```
### iOS Development
```bash
# Uses .env.development + iOS-specific overrides
./scripts/build-ios.sh --dev
```
## Troubleshooting
### Environment Variable Debugging
```bash
# Show current environment variables
./scripts/build-web.sh --env
# Check specific variable
echo $VITE_DEFAULT_ENDORSER_API_SERVER
```
### Common Issues
1. **Wrong API Server**: Check if shell script overrides are correct
2. **Missing .env File**: Ensure environment-specific .env file exists
3. **Platform-Specific Issues**: Verify platform overrides in build scripts
4. **Vite Not Loading**: Check if `dotenv.config()` is called
### Validation
```bash
# Validate environment configuration
npm run test-env
```
## Best Practices
1. **Always use environment-specific .env files** for different environments
2. **Keep shell script overrides minimal** and platform-specific
3. **Document API alignment** in .env file headers
4. **Use hardcoded fallbacks** in `app.ts` for safety
5. **Test all environments** before deployment
6. **Validate configuration** with test scripts
## Related Documentation
- [Build System Overview](../build-system/README.md)
- [Android Custom API IP](../platforms/android-custom-api-ip.md)
- [API Configuration](../api-configuration.md)
- [Environment Setup](../environment-setup.md)

View File

@@ -1,322 +0,0 @@
# Mobile Custom API IP Configuration
**Author**: Matthew Raymer
**Date**: 2025-01-27
**Status**: ✅ **COMPLETE** - Custom API IP support for physical device development
## Overview
When deploying TimeSafari to physical Android devices during development, you may need to specify a custom IP address for the claim API server. This is necessary because physical devices cannot access `localhost` or `10.0.2.2` (Android emulator IP) to reach your local development server.
## Problem
During mobile development:
- **Android Emulator**: Uses `10.0.2.2:3000` to access host machine's localhost (Android emulator default)
- **iOS Simulator**: Uses `localhost:3000` to access host machine's localhost (iOS simulator default)
- **Physical Devices**: Cannot access `localhost` or `10.0.2.2` - needs actual IP address for network access
## Solution
The mobile build system uses platform-appropriate defaults and supports specifying a custom IP address for the claim API server when building for physical devices:
- **Android**: Defaults to `10.0.2.2:3000` for emulator development
- **iOS**: Uses Capacitor default (`localhost:3000`) for simulator development
## Usage
### Command Line Usage
```bash
# Android - Default behavior (uses 10.0.2.2 for emulator)
./scripts/build-android.sh --dev
# Android - Custom IP for physical device
./scripts/build-android.sh --dev --api-ip 192.168.1.100
# iOS - Default behavior (uses localhost for simulator)
./scripts/build-ios.sh --dev
# iOS - Custom IP for physical device
./scripts/build-ios.sh --dev --api-ip 192.168.1.100
# Test environment with custom IP
./scripts/build-android.sh --test --api-ip 192.168.1.100
./scripts/build-ios.sh --test --api-ip 192.168.1.100
# Build and auto-run with custom IP
./scripts/build-android.sh --dev --api-ip 192.168.1.100 --auto-run
./scripts/build-ios.sh --dev --api-ip 192.168.1.100 --auto-run
```
### NPM Scripts
```bash
# Android - Default development build (uses 10.0.2.2 for emulator)
npm run build:android:dev
# Android - Development build with custom IP (requires IP parameter)
npm run build:android:dev:custom 192.168.1.100
# iOS - Default development build (uses localhost for simulator)
npm run build:ios:dev
# iOS - Development build with custom IP (requires IP parameter)
npm run build:ios:dev:custom 192.168.1.100
# Test builds with custom IP (requires IP parameter)
npm run build:android:test:custom 192.168.1.100
npm run build:ios:test:custom 192.168.1.100
# Development build + auto-run with custom IP
npm run build:android:dev:run:custom 192.168.1.100
npm run build:ios:dev:run:custom 192.168.1.100
# Test build + auto-run with custom IP
npm run build:android:test:run:custom 192.168.1.100
npm run build:ios:test:run:custom 192.168.1.100
```
## Examples
### Scenario 1: Development on Simulator/Emulator (Default)
```bash
# Android - Default behavior - uses 10.0.2.2 for emulator
npm run build:android:dev
# iOS - Default behavior - uses localhost for simulator
npm run build:ios:dev
# Build and immediately run on simulator/emulator
npm run build:android:dev:run
npm run build:ios:dev:run
```
### Scenario 2: Development on Physical Device
```bash
# Your development server is running on 192.168.1.50:3000
npm run build:android:dev:custom 192.168.1.50
npm run build:ios:dev:custom 192.168.1.50
# Build and immediately run on device
npm run build:android:dev:run:custom 192.168.1.50
npm run build:ios:dev:run:custom 192.168.1.50
```
### Scenario 3: Testing on Physical Device
```bash
# Your test server is running on 192.168.1.75:3000
npm run build:android:test:custom 192.168.1.75
npm run build:ios:test:custom 192.168.1.75
# Build and immediately run on device
npm run build:android:test:run:custom 192.168.1.75
npm run build:ios:test:run:custom 192.168.1.75
```
### Scenario 4: Direct Script Usage
```bash
# Default behavior (uses platform-appropriate defaults)
./scripts/build-android.sh --dev --studio
./scripts/build-ios.sh --dev --studio
# Custom IP for physical device
./scripts/build-android.sh --dev --api-ip 192.168.1.100 --studio
./scripts/build-ios.sh --dev --api-ip 192.168.1.100 --studio
```
## How It Works
### Environment Variable Override
The build system handles API server configuration as follows:
1. **Android default**: Uses Android emulator default (`http://10.0.2.2:3000`)
2. **iOS default**: Uses Capacitor default (`http://localhost:3000`)
3. **Custom IP specified**: Overrides with `http://<custom-ip>:3000` for physical device development
4. **Maintains other APIs**: Image and Partner APIs remain at production URLs
5. **Logs the configuration**: Shows which IP is being used in build logs
### Build Process
```bash
# Development mode with Android emulator default (10.0.2.2)
export VITE_DEFAULT_ENDORSER_API_SERVER="http://10.0.2.2:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://10.0.2.2:3000"
npm run build:capacitor -- --mode development
# Development mode with iOS simulator default (localhost)
export VITE_DEFAULT_ENDORSER_API_SERVER="http://localhost:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://localhost:3000"
npm run build:capacitor -- --mode development
# Development mode with custom IP
export VITE_DEFAULT_ENDORSER_API_SERVER="http://192.168.1.100:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://192.168.1.100:3000"
npm run build:capacitor -- --mode development
```
### Default Behavior
- **Android (no `--api-ip`)**: Uses Android emulator default (`10.0.2.2:3000`)
- **iOS (no `--api-ip`)**: Uses Capacitor default (`localhost:3000`)
- **Custom IP specified**: Uses provided IP address for physical device development
- **Invalid IP format**: Build will fail with clear error message
- **Network unreachable**: App will show connection errors at runtime
## Finding Your IP Address
### On Linux/macOS
```bash
# Find your local IP address
ifconfig | grep "inet " | grep -v 127.0.0.1
# or
ip addr show | grep "inet " | grep -v 127.0.0.1
```
### On Windows
```bash
# Find your local IP address
ipconfig | findstr "IPv4"
```
### Common Network Patterns
- **Home WiFi**: Usually `192.168.1.x` or `192.168.0.x`
- **Office Network**: May be `10.x.x.x` or `172.16.x.x`
- **Mobile Hotspot**: Often `192.168.43.x`
## Troubleshooting
### Common Issues
#### 1. Device Cannot Connect to API
```bash
# Check if your IP is accessible
ping 192.168.1.100
# Check if port 3000 is open
telnet 192.168.1.100 3000
```
#### 2. Build Fails with Invalid IP
```bash
# Ensure IP format is correct
./scripts/build-android.sh --dev --api-ip 192.168.1.100 # ✅ Correct
./scripts/build-android.sh --dev --api-ip localhost # ❌ Wrong
```
#### 3. Firewall Blocking Connection
```bash
# Check firewall settings
sudo ufw status # Ubuntu/Debian
sudo firewall-cmd --list-all # CentOS/RHEL
```
### Debug Mode
```bash
# Enable verbose logging
./scripts/build-android.sh --dev --api-ip 192.168.1.100 --verbose
```
## Best Practices
### 1. Use Consistent IP Addresses
```bash
# Create aliases for common development scenarios
alias build-dev="npm run build:android:dev:custom 192.168.1.100"
alias build-test="npm run build:android:test:custom 192.168.1.100"
```
### 2. Document Your Setup
```bash
# Create a development setup file
echo "DEV_API_IP=192.168.1.100" > .env.development
echo "TEST_API_IP=192.168.1.100" >> .env.development
```
### 3. Network Security
- Ensure your development server is only accessible on your local network
- Use HTTPS in production environments
- Consider VPN for remote development scenarios
### 4. Team Development
```bash
# Share IP configuration with team
# Add to .env.example
DEV_API_IP=192.168.1.100
TEST_API_IP=192.168.1.100
```
## Integration with CI/CD
### Environment Variables
```yaml
# Example CI/CD configuration
variables:
DEV_API_IP: "192.168.1.100"
TEST_API_IP: "192.168.1.100"
build:
script:
- npm run build:android:dev:custom $DEV_API_IP
```
### Automated Testing
```bash
# Test with different IP configurations
npm run build:android:test:custom 192.168.1.100
npm run build:android:test:custom 10.0.0.100
```
## Migration from Legacy
### Previous Workarounds
Before this feature, developers had to:
1. Manually edit environment files
2. Use different build configurations
3. Modify source code for IP addresses
### New Approach
```bash
# Simple one-liner
npm run build:android:dev:custom 192.168.1.100
```
## Future Enhancements
### Planned Features
1. **IP Validation**: Automatic IP format validation
2. **Network Discovery**: Auto-detect available IP addresses
3. **Port Configuration**: Support for custom ports
4. **Multiple APIs**: Support for custom IPs for all API endpoints
### Integration Opportunities
1. **Docker Integration**: Automatic IP detection in containerized environments
2. **Network Profiles**: Save and reuse common network configurations
3. **Hot Reload**: Automatic rebuild when IP changes
---
**Status**: Complete and ready for production use
**Last Updated**: 2025-01-27
**Version**: 1.0
**Maintainer**: Matthew Raymer

View File

@@ -1,84 +0,0 @@
# Contact Sharing - URL Solution
## Overview
Simple implementation to switch ContactQRScanShowView from copying QR value (CSV) to copying a URL for better user experience.
## Problem
The ContactQRScanShowView was copying QR value (CSV content) to clipboard instead of a URL, making contact sharing less user-friendly.
## Solution
Updated the `onCopyUrlToClipboard()` method in ContactQRScanShowView.vue to generate and copy a URL instead of the QR value.
## Changes Made
### ContactQRScanShowView.vue
**Added Imports:**
```typescript
import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer";
import { Account } from "@/db/tables/accounts";
```
**Updated Method:**
```typescript
async onCopyUrlToClipboard() {
try {
// Generate URL for sharing
const account = (await libsUtil.retrieveFullyDecryptedAccount(
this.activeDid,
)) as Account;
const jwtUrl = await generateEndorserJwtUrlForAccount(
account,
this.isRegistered,
this.givenName,
this.profileImageUrl,
true,
);
// Copy the URL to clipboard
useClipboard()
.copy(jwtUrl)
.then(() => {
this.notify.toast(
"Copied",
NOTIFY_QR_URL_COPIED.message,
QR_TIMEOUT_MEDIUM,
);
});
} catch (error) {
logger.error("Failed to generate contact URL:", error);
this.notify.error("Failed to generate contact URL. Please try again.");
}
}
```
## Benefits
1. **Better UX**: Recipients can click the URL to add contact directly
2. **Consistency**: Both ContactQRScanShowView and ContactQRScanFullView now use URL format
3. **Error Handling**: Graceful fallback if URL generation fails
4. **Simple**: Minimal changes, no new components needed
## User Experience
**Before:**
- Click QR code → Copy CSV data to clipboard
- Recipient must paste CSV into input field
**After:**
- Click QR code → Copy URL to clipboard
- Recipient clicks URL → Contact added automatically
## Testing
- ✅ Linting passes
- ✅ Error handling implemented
- ✅ Consistent with ContactQRScanFullView behavior
- ✅ Maintains existing notification system
## Deployment
Ready for deployment. No breaking changes, maintains backward compatibility.

View File

@@ -1,221 +0,0 @@
# Domain Configuration System
**Author**: Matthew Raymer
**Date**: 2025-01-27
**Status**: ✅ **UPDATED** - Simplified to use APP_SERVER for all functionality
## Overview
TimeSafari uses a centralized domain configuration system to ensure consistent
URL generation across all environments. This system provides a single point of
control for domain changes and uses environment-specific configuration for all
functionality including sharing.
## Problem Solved
### Issue: Inconsistent Domain Usage
Previously, the system used separate constants for different types of URLs:
- **Internal Operations**: Used `APP_SERVER` (environment-specific)
- **Sharing**: Used separate constants (removed)
This created complexity and confusion about when to use which constant.
### Solution: Unified Domain Configuration
All functionality now uses the `APP_SERVER` constant, which provides
environment-specific URLs that can be configured per environment.
## Implementation
### Core Configuration
The domain configuration is centralized in `src/constants/app.ts`:
```typescript
export enum AppString {
// ... other constants ...
PROD_PUSH_SERVER = "https://timesafari.app",
// ... other constants ...
}
// Environment-specific server URL for all functionality
export const APP_SERVER =
import.meta.env.VITE_APP_SERVER || "https://timesafari.app";
```
### Usage Pattern
All components that generate URLs follow this pattern:
```typescript
import { APP_SERVER } from "@/constants/app";
// In component class
APP_SERVER = APP_SERVER;
// In methods
const deepLink = `${APP_SERVER}/deep-link/claim/${claimId}`;
```
### Components Updated
The following components and services use `APP_SERVER`:
#### Views
- `ClaimView.vue` - Claim and certificate links
- `ProjectViewView.vue` - Project copy links
- `ConfirmGiftView.vue` - Confirm gift deep links
- `UserProfileView.vue` - Profile copy links
- `InviteOneView.vue` - Invite link generation
- `ContactsView.vue` - Contact import links
- `OnboardMeetingSetupView.vue` - Meeting members links
#### Components
- `HiddenDidDialog.vue` - Hidden DID dialog links
#### Services
- `endorserServer.ts` - Contact import confirm links
## Configuration Management
### Environment-Specific Configuration
The system uses environment variables to configure domains:
```bash
# Development
VITE_APP_SERVER=http://localhost:8080
# Test
VITE_APP_SERVER=https://test.timesafari.app
# Production
VITE_APP_SERVER=https://timesafari.app
```
### Changing the Domain
To change the domain for all functionality:
1. **Update environment variables** for the target environment:
```bash
VITE_APP_SERVER=https://your-new-domain.com
```
2. **Rebuild the application** for all platforms:
```bash
npm run build:web
npm run build:capacitor
npm run build:electron
```
## Benefits
### ✅ Simplified Configuration
- Single constant for all URL generation
- No confusion about which constant to use
- Consistent behavior across all functionality
### ✅ Environment Flexibility
- Easy to configure different domains per environment
- Support for development, test, and production environments
- Environment-specific sharing URLs when needed
### ✅ Maintainability
- Single source of truth for domain configuration
- Easy to change domain across entire application
- Clear pattern for implementing new URL functionality
### ✅ Developer Experience
- Simple, consistent pattern for URL generation
- Clear documentation and examples
- Type-safe configuration with TypeScript
## Testing
### Manual Testing
1. **Development Environment**:
```bash
npm run dev
# Navigate to any page with copy link buttons
# Verify links use configured domain
```
2. **Production Build**:
```bash
npm run build:web
# Deploy and test sharing functionality
# Verify all links work correctly
```
### Automated Testing
The implementation includes comprehensive linting to ensure:
- All components properly import `APP_SERVER`
- No hardcoded URLs in functionality
- Consistent usage patterns across the codebase
## Implementation Pattern
### Current Approach
```typescript
// ✅ Single constant for all functionality
import { APP_SERVER } from "@/constants/app";
const shareLink = `${APP_SERVER}/deep-link/claim/123`;
const apiUrl = `${APP_SERVER}/api/claim/123`;
```
## Future Enhancements
### Potential Improvements
1. **Environment-Specific Sharing Domains**:
```typescript
export const getShareDomain = () => {
if (import.meta.env.PROD) {
return AppString.PROD_PUSH_SERVER;
}
return AppString.TEST1_PUSH_SERVER; // Use test domain for dev sharing
};
```
2. **Dynamic Domain Detection**:
```typescript
export const SHARE_DOMAIN =
import.meta.env.VITE_SHARE_DOMAIN || AppString.PROD_PUSH_SERVER;
```
3. **Platform-Specific Domains**:
```typescript
export const getPlatformShareDomain = () => {
const platform = process.env.VITE_PLATFORM;
switch (platform) {
case 'web': return AppString.PROD_PUSH_SERVER;
case 'capacitor': return AppString.PROD_PUSH_SERVER;
case 'electron': return AppString.PROD_PUSH_SERVER;
default: return AppString.PROD_PUSH_SERVER;
}
};
```
## Related Documentation
- [Build Systems Overview](build-systems-overview.md) - Environment configuration
- [Constants and Configuration](src/constants/app.ts) - Core constants
- [Migration Guide](doc/migration-to-wa-sqlite.md) - Database migration context
---
**Last Updated**: 2025-01-27
**Version**: 2.0
**Maintainer**: Matthew Raymer

File diff suppressed because it is too large Load Diff

View File

@@ -1,113 +0,0 @@
# $updateSettings to $saveSettings Consolidation Plan
## Overview
Consolidate `$updateSettings` method into `$saveSettings` to eliminate code duplication and improve maintainability. The `$updateSettings` method is currently just a thin wrapper around `$saveSettings` and `$saveUserSettings`, providing no additional functionality.
## Current State Analysis
### Current Implementation
```typescript
// Current $updateSettings - just a wrapper
async $updateSettings(changes: Partial<Settings>, did?: string): Promise<boolean> {
try {
if (did) {
return await this.$saveUserSettings(did, changes);
} else {
return await this.$saveSettings(changes);
}
} catch (error) {
logger.error("[PlatformServiceMixin] Error updating settings:", error);
return false;
}
}
```
### Usage Statistics
- **$updateSettings**: 42 references across codebase
- **$saveSettings**: 38 references across codebase
- **$saveUserSettings**: 12 references across codebase
## Migration Strategy
### Phase 1: Documentation and Planning ✅
- [x] Document current usage patterns
- [x] Identify all call sites
- [x] Create migration plan
### Phase 2: Implementation
- [ ] Update `$saveSettings` to accept optional `did` parameter
- [ ] Add error handling to `$saveSettings` (currently missing)
- [ ] Deprecate `$updateSettings` with migration notice
- [ ] Update all call sites to use `$saveSettings` directly
### Phase 3: Cleanup
- [ ] Remove `$updateSettings` method
- [ ] Update documentation
- [ ] Update tests
## Implementation Details
### Enhanced $saveSettings Method
```typescript
async $saveSettings(changes: Partial<Settings>, did?: string): Promise<boolean> {
try {
// Convert settings for database storage
const convertedChanges = this._convertSettingsForStorage(changes);
if (did) {
// User-specific settings
return await this.$saveUserSettings(did, convertedChanges);
} else {
// Default settings
return await this.$saveSettings(convertedChanges);
}
} catch (error) {
logger.error("[PlatformServiceMixin] Error saving settings:", error);
return false;
}
}
```
### Migration Benefits
1. **Reduced Code Duplication**: Single method handles both use cases
2. **Improved Maintainability**: One place to fix issues
3. **Consistent Error Handling**: Unified error handling approach
4. **Better Type Safety**: Single method signature to maintain
### Risk Assessment
- **Low Risk**: `$updateSettings` is just a wrapper, no complex logic
- **Backward Compatible**: Can maintain both methods during transition
- **Testable**: Existing tests can be updated incrementally
## Call Site Migration Examples
### Before (using $updateSettings)
```typescript
await this.$updateSettings({ searchBoxes: [newSearchBox] });
await this.$updateSettings({ filterFeedByNearby: false }, userDid);
```
### After (using $saveSettings)
```typescript
await this.$saveSettings({ searchBoxes: [newSearchBox] });
await this.$saveSettings({ filterFeedByNearby: false }, userDid);
```
## Testing Strategy
1. **Unit Tests**: Update existing tests to use `$saveSettings`
2. **Integration Tests**: Verify both default and user-specific settings work
3. **Migration Tests**: Ensure searchBoxes conversion still works
4. **Performance Tests**: Verify no performance regression
## Timeline
- **Phase 1**: ✅ Complete
- **Phase 2**: 1-2 days
- **Phase 3**: 1 day
- **Total**: 2-3 days
## Success Criteria
- [ ] All existing functionality preserved
- [ ] No performance regression
- [ ] All tests passing
- [ ] Reduced code duplication
- [ ] Improved maintainability

Some files were not shown because too many files have changed in this diff Show More