Compare commits
No commits in common. 'master' and 'vite-version' have entirely different histories.
master
...
vite-versi
@ -1,4 +0,0 @@ |
|||
> 1% |
|||
last 2 versions |
|||
not dead |
|||
not ie 11 |
@ -1,153 +0,0 @@ |
|||
--- |
|||
description: |
|||
globs: |
|||
alwaysApply: true |
|||
--- |
|||
# Absurd SQL - Cursor Development Guide |
|||
|
|||
## Project Overview |
|||
Absurd SQL is a backend implementation for sql.js that enables persistent SQLite databases in the browser by using IndexedDB as a block storage system. This guide provides rules and best practices for developing with this project in Cursor. |
|||
|
|||
## Project Structure |
|||
``` |
|||
absurd-sql/ |
|||
├── src/ # Source code |
|||
├── dist/ # Built files |
|||
├── package.json # Dependencies and scripts |
|||
├── rollup.config.js # Build configuration |
|||
└── jest.config.js # Test configuration |
|||
``` |
|||
|
|||
## Development Rules |
|||
|
|||
### 1. Worker Thread Requirements |
|||
- All SQL operations MUST be performed in a worker thread |
|||
- Main thread should only handle worker initialization and communication |
|||
- Never block the main thread with database operations |
|||
|
|||
### 2. Code Organization |
|||
- Keep worker code in separate files (e.g., `*.worker.js`) |
|||
- Use ES modules for imports/exports |
|||
- Follow the project's existing module structure |
|||
|
|||
### 3. Required Headers |
|||
When developing locally or deploying, ensure these headers are set: |
|||
``` |
|||
Cross-Origin-Opener-Policy: same-origin |
|||
Cross-Origin-Embedder-Policy: require-corp |
|||
``` |
|||
|
|||
### 4. Browser Compatibility |
|||
- Primary target: Modern browsers with SharedArrayBuffer support |
|||
- Fallback mode: Safari (with limitations) |
|||
- Always test in both modes |
|||
|
|||
### 5. Database Configuration |
|||
Recommended database settings: |
|||
```sql |
|||
PRAGMA journal_mode=MEMORY; |
|||
PRAGMA page_size=8192; -- Optional, but recommended |
|||
``` |
|||
|
|||
### 6. Development Workflow |
|||
1. Install dependencies: |
|||
```bash |
|||
yarn add @jlongster/sql.js absurd-sql |
|||
``` |
|||
|
|||
2. Development commands: |
|||
- `yarn build` - Build the project |
|||
- `yarn jest` - Run tests |
|||
- `yarn serve` - Start development server |
|||
|
|||
### 7. Testing Guidelines |
|||
- Write tests for both SharedArrayBuffer and fallback modes |
|||
- Use Jest for testing |
|||
- Include performance benchmarks for critical operations |
|||
|
|||
### 8. Performance Considerations |
|||
- Use bulk operations when possible |
|||
- Monitor read/write performance |
|||
- Consider using transactions for multiple operations |
|||
- Avoid unnecessary database connections |
|||
|
|||
### 9. Error Handling |
|||
- Implement proper error handling for: |
|||
- Worker initialization failures |
|||
- Database connection issues |
|||
- Concurrent access conflicts (in fallback mode) |
|||
- Storage quota exceeded scenarios |
|||
|
|||
### 10. Security Best Practices |
|||
- Never expose database operations directly to the client |
|||
- Validate all SQL queries |
|||
- Implement proper access controls |
|||
- Handle sensitive data appropriately |
|||
|
|||
### 11. Code Style |
|||
- Follow ESLint configuration |
|||
- Use async/await for asynchronous operations |
|||
- Document complex database operations |
|||
- Include comments for non-obvious optimizations |
|||
|
|||
### 12. Debugging |
|||
- Use `jest-debug` for debugging tests |
|||
- Monitor IndexedDB usage in browser dev tools |
|||
- Check worker communication in console |
|||
- Use performance monitoring tools |
|||
|
|||
## Common Patterns |
|||
|
|||
### Worker Initialization |
|||
```javascript |
|||
// Main thread |
|||
import { initBackend } from 'absurd-sql/dist/indexeddb-main-thread'; |
|||
|
|||
function init() { |
|||
let worker = new Worker(new URL('./index.worker.js', import.meta.url)); |
|||
initBackend(worker); |
|||
} |
|||
``` |
|||
|
|||
### Database Setup |
|||
```javascript |
|||
// Worker thread |
|||
import initSqlJs from '@jlongster/sql.js'; |
|||
import { SQLiteFS } from 'absurd-sql'; |
|||
import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend'; |
|||
|
|||
async function setupDatabase() { |
|||
let SQL = await initSqlJs({ locateFile: file => file }); |
|||
let sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend()); |
|||
SQL.register_for_idb(sqlFS); |
|||
|
|||
SQL.FS.mkdir('/sql'); |
|||
SQL.FS.mount(sqlFS, {}, '/sql'); |
|||
|
|||
return new SQL.Database('/sql/db.sqlite', { filename: true }); |
|||
} |
|||
``` |
|||
|
|||
## Troubleshooting |
|||
|
|||
### Common Issues |
|||
1. SharedArrayBuffer not available |
|||
- Check COOP/COEP headers |
|||
- Verify browser support |
|||
- Test fallback mode |
|||
|
|||
2. Worker initialization failures |
|||
- Check file paths |
|||
- Verify module imports |
|||
- Check browser console for errors |
|||
|
|||
3. Performance issues |
|||
- Monitor IndexedDB usage |
|||
- Check for unnecessary operations |
|||
- Verify transaction usage |
|||
|
|||
## Resources |
|||
- [Project Demo](https://priceless-keller-d097e5.netlify.app/) |
|||
- [Example Project](https://github.com/jlongster/absurd-example-project) |
|||
- [Blog Post](https://jlongster.com/future-sql-web) |
|||
- [SQL.js Documentation](https://github.com/sql-js/sql.js/) |
@ -1,292 +0,0 @@ |
|||
--- |
|||
description: |
|||
globs: |
|||
alwaysApply: true |
|||
--- |
|||
# TimeSafari Cross-Platform Architecture Guide |
|||
|
|||
## 1. Platform Support Matrix |
|||
|
|||
| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) | PyWebView (Desktop) | |
|||
|---------|-----------|-------------------|-------------------|-------------------| |
|||
| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented | Not Implemented | |
|||
| Deep Linking | URL Parameters | App URL Open Events | Not Implemented | Not Implemented | |
|||
| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs | PyWebView Python Bridge | |
|||
| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented | Not Implemented | |
|||
| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks | process.env checks | |
|||
|
|||
## 2. Project Structure |
|||
|
|||
### 2.1 Core Directories |
|||
``` |
|||
src/ |
|||
├── components/ # Vue components |
|||
├── services/ # Platform services and business logic |
|||
├── views/ # Page components |
|||
├── router/ # Vue router configuration |
|||
├── types/ # TypeScript type definitions |
|||
├── utils/ # Utility functions |
|||
├── lib/ # Core libraries |
|||
├── platforms/ # Platform-specific implementations |
|||
├── electron/ # Electron-specific code |
|||
├── constants/ # Application constants |
|||
├── db/ # Database related code |
|||
├── interfaces/ # TypeScript interfaces and type definitions |
|||
└── assets/ # Static assets |
|||
``` |
|||
|
|||
### 2.2 Entry Points |
|||
``` |
|||
src/ |
|||
├── main.ts # Base entry |
|||
├── main.common.ts # Shared initialization |
|||
├── main.capacitor.ts # Mobile entry |
|||
├── main.electron.ts # Electron entry |
|||
├── main.pywebview.ts # PyWebView entry |
|||
└── main.web.ts # Web/PWA entry |
|||
``` |
|||
|
|||
### 2.3 Build Configurations |
|||
``` |
|||
root/ |
|||
├── vite.config.common.mts # Shared config |
|||
├── vite.config.capacitor.mts # Mobile build |
|||
├── vite.config.electron.mts # Electron build |
|||
├── vite.config.pywebview.mts # PyWebView build |
|||
├── vite.config.web.mts # Web/PWA build |
|||
└── vite.config.utils.mts # Build utilities |
|||
``` |
|||
|
|||
## 3. Service Architecture |
|||
|
|||
### 3.1 Service Organization |
|||
``` |
|||
services/ |
|||
├── QRScanner/ # QR code scanning service |
|||
│ ├── WebInlineQRScanner.ts |
|||
│ └── interfaces.ts |
|||
├── platforms/ # Platform-specific services |
|||
│ ├── WebPlatformService.ts |
|||
│ ├── CapacitorPlatformService.ts |
|||
│ ├── ElectronPlatformService.ts |
|||
│ └── PyWebViewPlatformService.ts |
|||
└── factory/ # Service factories |
|||
└── PlatformServiceFactory.ts |
|||
``` |
|||
|
|||
### 3.2 Service Factory Pattern |
|||
```typescript |
|||
// PlatformServiceFactory.ts |
|||
export class PlatformServiceFactory { |
|||
private static instance: PlatformService | null = null; |
|||
|
|||
public static getInstance(): PlatformService { |
|||
if (!PlatformServiceFactory.instance) { |
|||
const platform = process.env.VITE_PLATFORM || "web"; |
|||
PlatformServiceFactory.instance = createPlatformService(platform); |
|||
} |
|||
return PlatformServiceFactory.instance; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## 4. Feature Implementation Guidelines |
|||
|
|||
### 4.1 QR Code Scanning |
|||
|
|||
1. **Service Interface** |
|||
```typescript |
|||
interface QRScannerService { |
|||
checkPermissions(): Promise<boolean>; |
|||
requestPermissions(): Promise<boolean>; |
|||
isSupported(): Promise<boolean>; |
|||
startScan(): Promise<void>; |
|||
stopScan(): Promise<void>; |
|||
addListener(listener: ScanListener): void; |
|||
onStream(callback: (stream: MediaStream | null) => void): void; |
|||
cleanup(): Promise<void>; |
|||
} |
|||
``` |
|||
|
|||
2. **Platform-Specific Implementation** |
|||
```typescript |
|||
// WebInlineQRScanner.ts |
|||
export class WebInlineQRScanner implements QRScannerService { |
|||
private scanListener: ScanListener | null = null; |
|||
private isScanning = false; |
|||
private stream: MediaStream | null = null; |
|||
private events = new EventEmitter(); |
|||
|
|||
// Implementation of interface methods |
|||
} |
|||
``` |
|||
|
|||
### 4.2 Deep Linking |
|||
|
|||
1. **URL Structure** |
|||
```typescript |
|||
// Format: timesafari://<route>[/<param>][?queryParam1=value1] |
|||
interface DeepLinkParams { |
|||
route: string; |
|||
params?: Record<string, string>; |
|||
query?: Record<string, string>; |
|||
} |
|||
``` |
|||
|
|||
2. **Platform Handlers** |
|||
```typescript |
|||
// Capacitor |
|||
App.addListener("appUrlOpen", handleDeepLink); |
|||
|
|||
// Web |
|||
router.beforeEach((to, from, next) => { |
|||
handleWebDeepLink(to.query); |
|||
}); |
|||
``` |
|||
|
|||
## 5. Build Process |
|||
|
|||
### 5.1 Environment Configuration |
|||
```typescript |
|||
// vite.config.common.mts |
|||
export function createBuildConfig(mode: string) { |
|||
return { |
|||
define: { |
|||
'process.env.VITE_PLATFORM': JSON.stringify(mode), |
|||
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isNative), |
|||
__IS_MOBILE__: JSON.stringify(isCapacitor), |
|||
__USE_QR_READER__: JSON.stringify(!isCapacitor) |
|||
} |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
### 5.2 Platform-Specific Builds |
|||
|
|||
```bash |
|||
# Build commands from package.json |
|||
"build:web": "vite build --config vite.config.web.mts", |
|||
"build:capacitor": "vite build --config vite.config.capacitor.mts", |
|||
"build:electron": "vite build --config vite.config.electron.mts", |
|||
"build:pywebview": "vite build --config vite.config.pywebview.mts" |
|||
``` |
|||
|
|||
## 6. Testing Strategy |
|||
|
|||
### 6.1 Test Configuration |
|||
```typescript |
|||
// playwright.config-local.ts |
|||
const config: PlaywrightTestConfig = { |
|||
projects: [ |
|||
{ |
|||
name: 'web', |
|||
use: { browserName: 'chromium' } |
|||
}, |
|||
{ |
|||
name: 'mobile', |
|||
use: { ...devices['Pixel 5'] } |
|||
} |
|||
] |
|||
}; |
|||
``` |
|||
|
|||
### 6.2 Platform-Specific Tests |
|||
```typescript |
|||
test('QR scanning works on mobile', async ({ page }) => { |
|||
test.skip(!process.env.MOBILE_TEST, 'Mobile-only test'); |
|||
// Test implementation |
|||
}); |
|||
``` |
|||
|
|||
## 7. Error Handling |
|||
|
|||
### 7.1 Global Error Handler |
|||
```typescript |
|||
function setupGlobalErrorHandler(app: VueApp) { |
|||
app.config.errorHandler = (err, instance, info) => { |
|||
logger.error("[App Error]", { |
|||
error: err, |
|||
info, |
|||
component: instance?.$options.name |
|||
}); |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
### 7.2 Platform-Specific Error Handling |
|||
```typescript |
|||
// API error handling for Capacitor |
|||
if (process.env.VITE_PLATFORM === 'capacitor') { |
|||
logger.error(`[Capacitor API Error] ${endpoint}:`, { |
|||
message: error.message, |
|||
status: error.response?.status |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
## 8. Best Practices |
|||
|
|||
### 8.1 Code Organization |
|||
- Use platform-specific directories for unique implementations |
|||
- Share common code through service interfaces |
|||
- Implement feature detection before using platform capabilities |
|||
- Keep platform-specific code isolated in dedicated directories |
|||
- Use TypeScript interfaces for cross-platform compatibility |
|||
|
|||
### 8.2 Platform Detection |
|||
```typescript |
|||
const platformService = PlatformServiceFactory.getInstance(); |
|||
const capabilities = platformService.getCapabilities(); |
|||
|
|||
if (capabilities.hasCamera) { |
|||
// Implement camera features |
|||
} |
|||
``` |
|||
|
|||
### 8.3 Feature Implementation |
|||
1. Define platform-agnostic interface |
|||
2. Create platform-specific implementations |
|||
3. Use factory pattern for instantiation |
|||
4. Implement graceful fallbacks |
|||
5. Add comprehensive error handling |
|||
6. Use dependency injection for better testability |
|||
|
|||
## 9. Dependency Management |
|||
|
|||
### 9.1 Platform-Specific Dependencies |
|||
```json |
|||
{ |
|||
"dependencies": { |
|||
"@capacitor/core": "^6.2.0", |
|||
"electron": "^33.2.1", |
|||
"vue": "^3.4.0" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### 9.2 Conditional Loading |
|||
```typescript |
|||
if (process.env.VITE_PLATFORM === 'capacitor') { |
|||
await import('@capacitor/core'); |
|||
} |
|||
``` |
|||
|
|||
## 10. Security Considerations |
|||
|
|||
### 10.1 Permission Handling |
|||
```typescript |
|||
async checkPermissions(): Promise<boolean> { |
|||
if (platformService.isCapacitor()) { |
|||
return await checkNativePermissions(); |
|||
} |
|||
return await checkWebPermissions(); |
|||
} |
|||
``` |
|||
|
|||
### 10.2 Data Storage |
|||
- Use secure storage mechanisms for sensitive data |
|||
- Implement proper encryption for stored data |
|||
- Follow platform-specific security guidelines |
|||
- Regular security audits and updates |
|||
|
|||
This document should be updated as new features are added or platform-specific implementations change. Regular reviews ensure it remains current with the codebase. |
@ -1,222 +0,0 @@ |
|||
--- |
|||
description: |
|||
globs: |
|||
alwaysApply: false |
|||
--- |
|||
# Camera Implementation Documentation |
|||
|
|||
## Overview |
|||
|
|||
This document describes how camera functionality is implemented across the TimeSafari application. The application uses cameras for two main purposes: |
|||
|
|||
1. QR Code scanning |
|||
2. Photo capture |
|||
|
|||
## Components |
|||
|
|||
### QRScannerDialog.vue |
|||
|
|||
Primary component for QR code scanning in web browsers. |
|||
|
|||
**Key Features:** |
|||
|
|||
- Uses `qrcode-stream` for web-based QR scanning |
|||
- Supports both front and back cameras |
|||
- Provides real-time camera status feedback |
|||
- Implements error handling with user-friendly messages |
|||
- Includes camera switching functionality |
|||
|
|||
**Camera Access Flow:** |
|||
|
|||
1. Checks for camera API availability |
|||
2. Enumerates available video devices |
|||
3. Requests camera permissions |
|||
4. Initializes camera stream with preferred settings |
|||
5. Handles various error conditions with specific messages |
|||
|
|||
### PhotoDialog.vue |
|||
|
|||
Component for photo capture and selection. |
|||
|
|||
**Key Features:** |
|||
|
|||
- Cross-platform photo capture interface |
|||
- Image cropping capabilities |
|||
- File selection fallback |
|||
- Unified interface for different platforms |
|||
|
|||
## Services |
|||
|
|||
### QRScanner Services |
|||
|
|||
#### WebDialogQRScanner |
|||
|
|||
Web-based implementation of QR scanning. |
|||
|
|||
**Key Methods:** |
|||
|
|||
- `checkPermissions()`: Verifies camera permission status |
|||
- `requestPermissions()`: Requests camera access |
|||
- `isSupported()`: Checks for camera API support |
|||
- Handles various error conditions with specific messages |
|||
|
|||
#### CapacitorQRScanner |
|||
|
|||
Native implementation using Capacitor's MLKit. |
|||
|
|||
**Key Features:** |
|||
|
|||
- Uses `@capacitor-mlkit/barcode-scanning` |
|||
- Supports both front and back cameras |
|||
- Implements permission management |
|||
- Provides continuous scanning capability |
|||
|
|||
### Platform Services |
|||
|
|||
#### WebPlatformService |
|||
|
|||
Web-specific implementation of platform features. |
|||
|
|||
**Camera Capabilities:** |
|||
|
|||
- Uses HTML5 file input with capture attribute |
|||
- Falls back to file selection if camera unavailable |
|||
- Processes captured images for consistent format |
|||
|
|||
#### CapacitorPlatformService |
|||
|
|||
Native implementation using Capacitor. |
|||
|
|||
**Camera Features:** |
|||
|
|||
- Uses `Camera.getPhoto()` for native camera access |
|||
- Supports image editing |
|||
- Configures high-quality image capture |
|||
- Handles base64 image processing |
|||
|
|||
#### ElectronPlatformService |
|||
|
|||
Desktop implementation (currently unimplemented). |
|||
|
|||
**Status:** |
|||
|
|||
- Camera functionality not yet implemented |
|||
- Planned to use Electron's media APIs |
|||
|
|||
## Platform-Specific Considerations |
|||
|
|||
### iOS |
|||
|
|||
- Requires `NSCameraUsageDescription` in Info.plist |
|||
- Supports both front and back cameras |
|||
- Implements proper permission handling |
|||
|
|||
### Android |
|||
|
|||
- Requires camera permissions in manifest |
|||
- Supports both front and back cameras |
|||
- Handles permission requests through Capacitor |
|||
|
|||
### Web |
|||
|
|||
- Requires HTTPS for camera access |
|||
- Implements fallback mechanisms |
|||
- Handles browser compatibility issues |
|||
|
|||
## Error Handling |
|||
|
|||
### Common Error Scenarios |
|||
|
|||
1. No camera found |
|||
2. Permission denied |
|||
3. Camera in use by another application |
|||
4. HTTPS required |
|||
5. Browser compatibility issues |
|||
|
|||
### Error Response |
|||
|
|||
- User-friendly error messages |
|||
- Troubleshooting tips |
|||
- Clear instructions for resolution |
|||
- Platform-specific guidance |
|||
|
|||
## Security Considerations |
|||
|
|||
### Permission Management |
|||
|
|||
- Explicit permission requests |
|||
- Permission state tracking |
|||
- Graceful handling of denied permissions |
|||
|
|||
### Data Handling |
|||
|
|||
- Secure image processing |
|||
- Proper cleanup of camera resources |
|||
- No persistent storage of camera data |
|||
|
|||
## Best Practices |
|||
|
|||
### Camera Access |
|||
|
|||
1. Always check for camera availability |
|||
2. Request permissions explicitly |
|||
3. Handle all error conditions |
|||
4. Provide clear user feedback |
|||
5. Implement proper cleanup |
|||
|
|||
### Performance |
|||
|
|||
1. Optimize camera resolution |
|||
2. Implement proper resource cleanup |
|||
3. Handle camera switching efficiently |
|||
4. Manage memory usage |
|||
|
|||
### User Experience |
|||
|
|||
1. Clear status indicators |
|||
2. Intuitive camera controls |
|||
3. Helpful error messages |
|||
4. Smooth camera switching |
|||
5. Responsive UI feedback |
|||
|
|||
## Future Improvements |
|||
|
|||
### Planned Enhancements |
|||
|
|||
1. Implement Electron camera support |
|||
2. Add advanced camera features |
|||
3. Improve error handling |
|||
4. Enhance user feedback |
|||
5. Optimize performance |
|||
|
|||
### Known Issues |
|||
|
|||
1. Electron camera implementation pending |
|||
2. Some browser compatibility limitations |
|||
3. Platform-specific quirks to address |
|||
|
|||
## Dependencies |
|||
|
|||
### Key Packages |
|||
|
|||
- `@capacitor-mlkit/barcode-scanning` |
|||
- `qrcode-stream` |
|||
- `vue-picture-cropper` |
|||
- Platform-specific camera APIs |
|||
|
|||
## Testing |
|||
|
|||
### Test Scenarios |
|||
|
|||
1. Permission handling |
|||
2. Camera switching |
|||
3. Error conditions |
|||
4. Platform compatibility |
|||
5. Performance metrics |
|||
|
|||
### Test Environment |
|||
|
|||
- Multiple browsers |
|||
- iOS and Android devices |
|||
- Desktop platforms |
|||
- Various network conditions |
@ -1,276 +0,0 @@ |
|||
--- |
|||
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. |
|||
|
|||
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. |
|||
|
|||
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: |
|||
|
|||
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. |
|||
|
|||
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. |
|||
|
|||
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: |
|||
|
|||
- **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. |
|||
|
|||
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. |
|||
|
|||
## 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. |
|||
|
|||
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. |
|||
|
|||
4. **Voluntary Collaboration**: The system should enable but never coerce participation. |
|||
|
|||
5. **Trust Building**: Features should help build verifiable trust between users. |
|||
|
|||
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. |
|||
|
|||
## 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. |
|||
|
|||
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. |
|||
|
|||
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. |
|||
|
|||
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. |
|||
|
|||
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. |
|||
|
|||
## Project Technologies |
|||
|
|||
- Typescript using ES6 classes using vue-facing-decorator |
|||
- TailwindCSS |
|||
- Vite Build Tool |
|||
- Playwright E2E testing |
|||
- IndexDB |
|||
- Camera, Image uploads, QR Code reader, ... |
|||
|
|||
## Mobile Features |
|||
|
|||
- Deep Linking |
|||
- Local Notifications via a custom Capacitor plugin |
|||
|
|||
## Project Architecture |
|||
|
|||
- 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 |
|||
- Implement service classes for shared business logic |
|||
- Use mixins for cross-cutting concerns |
|||
- Leverage TypeScript interfaces for shared type definitions |
|||
|
|||
- **Component Patterns** |
|||
- Create base components for common UI elements |
|||
- Implement higher-order components for shared behavior |
|||
- Use slot patterns for flexible component composition |
|||
- Create composable services for business logic |
|||
- Implement factory patterns for component creation |
|||
|
|||
- **State Management** |
|||
- Centralize state in Pinia stores |
|||
- Use computed properties for derived state |
|||
- Implement shared state selectors |
|||
- Create reusable state mutations |
|||
- Use action creators for common operations |
|||
|
|||
- **Error Handling** |
|||
- Implement centralized error handling |
|||
- Create reusable error components |
|||
- Use error boundary components |
|||
- Implement consistent error logging |
|||
- Create error type definitions |
|||
|
|||
- **Type Definitions** |
|||
- Create shared interfaces for common data structures |
|||
- Use type aliases for complex types |
|||
- Implement generic types for reusable components |
|||
- Create utility types for common patterns |
|||
- Use discriminated unions for state management |
|||
|
|||
- **API Integration** |
|||
- Create reusable API client classes |
|||
- Implement request/response interceptors |
|||
- Use consistent error handling patterns |
|||
- Create type-safe API endpoints |
|||
- Implement caching strategies |
|||
|
|||
- **Platform Services** |
|||
- Abstract platform-specific code behind interfaces |
|||
- Create platform-agnostic service layers |
|||
- Implement feature detection |
|||
- Use dependency injection for services |
|||
- Create service factories |
|||
|
|||
- **Testing** |
|||
- Create reusable test utilities |
|||
- Implement test factories |
|||
- Use shared test configurations |
|||
- Create reusable test helpers |
|||
- Implement consistent test patterns |
|||
|
|||
### SOLID Principles |
|||
- **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) |
|||
- Utilities should provide focused helper functions |
|||
|
|||
- **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 |
|||
|
|||
- **Liskov Substitution**: Objects should be replaceable with their subtypes |
|||
- Platform services should work consistently across web/mobile |
|||
- Authentication providers should be interchangeable |
|||
- Storage implementations should be swappable |
|||
|
|||
- **Interface Segregation**: Clients shouldn't depend on interfaces they don't use |
|||
- Break down large service interfaces into smaller, focused ones |
|||
- Component props should be minimal and purposeful |
|||
- Event emissions should be specific and targeted |
|||
|
|||
- **Dependency Inversion**: High-level modules shouldn't depend on low-level modules |
|||
- Use dependency injection for services |
|||
- Abstract platform-specific code behind interfaces |
|||
- 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 |
|||
- Implement facade patterns for subsystem access |
|||
- 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 |
|||
- Create higher-order components for common patterns |
|||
- 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 |
|||
- Use TypeScript interfaces for type definitions |
|||
- Implement role-based interfaces for different use cases |
|||
|
|||
### Fail Fast |
|||
- Validate inputs early in the process |
|||
- Use TypeScript strict mode |
|||
- Implement comprehensive error handling |
|||
- Add runtime checks for critical operations |
|||
- Use assertions for development-time validation |
|||
|
|||
### Principle of Least Astonishment |
|||
- Follow Vue.js conventions consistently |
|||
- Use familiar naming patterns |
|||
- Implement predictable component behaviors |
|||
- Maintain consistent error handling |
|||
- Keep UI interactions intuitive |
|||
|
|||
### Information Hiding |
|||
- Encapsulate implementation details |
|||
- Use private class members |
|||
- Implement proper access modifiers |
|||
- Hide complex logic behind simple interfaces |
|||
- Use TypeScript's access modifiers effectively |
|||
|
|||
### Single Source of Truth |
|||
- Use Pinia for state management |
|||
- Maintain one source for user data |
|||
- Centralize configuration management |
|||
- Use computed properties for derived state |
|||
- 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 |
|||
|
@ -1,267 +0,0 @@ |
|||
--- |
|||
description: |
|||
globs: |
|||
alwaysApply: true |
|||
--- |
|||
# wa-sqlite Usage Guide |
|||
|
|||
## Table of Contents |
|||
- [1. Overview](#1-overview) |
|||
- [2. Installation](#2-installation) |
|||
- [3. Basic Setup](#3-basic-setup) |
|||
- [3.1 Import and Initialize](#31-import-and-initialize) |
|||
- [3.2 Basic Database Operations](#32-basic-database-operations) |
|||
- [4. Virtual File Systems (VFS)](#4-virtual-file-systems-vfs) |
|||
- [4.1 Available VFS Options](#41-available-vfs-options) |
|||
- [4.2 Using a VFS](#42-using-a-vfs) |
|||
- [5. Best Practices](#5-best-practices) |
|||
- [5.1 Error Handling](#51-error-handling) |
|||
- [5.2 Transaction Management](#52-transaction-management) |
|||
- [5.3 Prepared Statements](#53-prepared-statements) |
|||
- [6. Performance Considerations](#6-performance-considerations) |
|||
- [7. Common Issues and Solutions](#7-common-issues-and-solutions) |
|||
- [8. TypeScript Support](#8-typescript-support) |
|||
|
|||
## 1. Overview |
|||
wa-sqlite is a WebAssembly build of SQLite that enables SQLite database operations in web browsers and JavaScript environments. It provides both synchronous and asynchronous builds, with support for custom virtual file systems (VFS) for persistent storage. |
|||
|
|||
## 2. Installation |
|||
```bash |
|||
npm install wa-sqlite |
|||
# or |
|||
yarn add wa-sqlite |
|||
``` |
|||
|
|||
## 3. Basic Setup |
|||
|
|||
### 3.1 Import and Initialize |
|||
```javascript |
|||
// Choose one of these imports based on your needs: |
|||
// - wa-sqlite.mjs: Synchronous build |
|||
// - wa-sqlite-async.mjs: Asynchronous build (required for async VFS) |
|||
// - wa-sqlite-jspi.mjs: JSPI-based async build (experimental, Chromium only) |
|||
import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite.mjs'; |
|||
import * as SQLite from 'wa-sqlite'; |
|||
|
|||
async function initDatabase() { |
|||
// Initialize SQLite module |
|||
const module = await SQLiteESMFactory(); |
|||
const sqlite3 = SQLite.Factory(module); |
|||
|
|||
// Open database (returns a Promise) |
|||
const db = await sqlite3.open_v2('myDatabase'); |
|||
return { sqlite3, db }; |
|||
} |
|||
``` |
|||
|
|||
### 3.2 Basic Database Operations |
|||
```javascript |
|||
async function basicOperations() { |
|||
const { sqlite3, db } = await initDatabase(); |
|||
|
|||
try { |
|||
// Create a table |
|||
await sqlite3.exec(db, ` |
|||
CREATE TABLE IF NOT EXISTS users ( |
|||
id INTEGER PRIMARY KEY, |
|||
name TEXT NOT NULL, |
|||
email TEXT UNIQUE |
|||
) |
|||
`); |
|||
|
|||
// Insert data |
|||
await sqlite3.exec(db, ` |
|||
INSERT INTO users (name, email) |
|||
VALUES ('John Doe', 'john@example.com') |
|||
`); |
|||
|
|||
// Query data |
|||
const results = []; |
|||
await sqlite3.exec(db, 'SELECT * FROM users', (row, columns) => { |
|||
results.push({ row, columns }); |
|||
}); |
|||
|
|||
return results; |
|||
} finally { |
|||
// Always close the database when done |
|||
await sqlite3.close(db); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## 4. Virtual File Systems (VFS) |
|||
|
|||
### 4.1 Available VFS Options |
|||
wa-sqlite provides several VFS implementations for persistent storage: |
|||
|
|||
1. **IDBBatchAtomicVFS** (Recommended for general use) |
|||
- Uses IndexedDB with batch atomic writes |
|||
- Works in all contexts (Window, Worker, Service Worker) |
|||
- Supports WAL mode |
|||
- Best performance with `PRAGMA synchronous=normal` |
|||
|
|||
2. **IDBMirrorVFS** |
|||
- Keeps files in memory, persists to IndexedDB |
|||
- Works in all contexts |
|||
- Good for smaller databases |
|||
|
|||
3. **OPFS-based VFS** (Origin Private File System) |
|||
- Various implementations available: |
|||
- AccessHandlePoolVFS |
|||
- OPFSAdaptiveVFS |
|||
- OPFSCoopSyncVFS |
|||
- OPFSPermutedVFS |
|||
- Better performance but limited to Worker contexts |
|||
|
|||
### 4.2 Using a VFS |
|||
```javascript |
|||
import { IDBBatchAtomicVFS } from 'wa-sqlite/src/examples/IDBBatchAtomicVFS.js'; |
|||
import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite-async.mjs'; |
|||
import * as SQLite from 'wa-sqlite'; |
|||
|
|||
async function initDatabaseWithVFS() { |
|||
const module = await SQLiteESMFactory(); |
|||
const sqlite3 = SQLite.Factory(module); |
|||
|
|||
// Register VFS |
|||
const vfs = await IDBBatchAtomicVFS.create('myApp', module); |
|||
sqlite3.vfs_register(vfs, true); |
|||
|
|||
// Open database with VFS |
|||
const db = await sqlite3.open_v2('myDatabase'); |
|||
|
|||
// Configure for better performance |
|||
await sqlite3.exec(db, 'PRAGMA synchronous = normal'); |
|||
await sqlite3.exec(db, 'PRAGMA journal_mode = WAL'); |
|||
|
|||
return { sqlite3, db }; |
|||
} |
|||
``` |
|||
|
|||
## 5. Best Practices |
|||
|
|||
### 5.1 Error Handling |
|||
```javascript |
|||
async function safeDatabaseOperation() { |
|||
const { sqlite3, db } = await initDatabase(); |
|||
|
|||
try { |
|||
await sqlite3.exec(db, 'SELECT * FROM non_existent_table'); |
|||
} catch (error) { |
|||
if (error.code === SQLite.SQLITE_ERROR) { |
|||
console.error('SQL error:', error.message); |
|||
} else { |
|||
console.error('Database error:', error); |
|||
} |
|||
} finally { |
|||
await sqlite3.close(db); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### 5.2 Transaction Management |
|||
```javascript |
|||
async function transactionExample() { |
|||
const { sqlite3, db } = await initDatabase(); |
|||
|
|||
try { |
|||
await sqlite3.exec(db, 'BEGIN TRANSACTION'); |
|||
|
|||
// Perform multiple operations |
|||
await sqlite3.exec(db, 'INSERT INTO users (name) VALUES (?)', ['Alice']); |
|||
await sqlite3.exec(db, 'INSERT INTO users (name) VALUES (?)', ['Bob']); |
|||
|
|||
await sqlite3.exec(db, 'COMMIT'); |
|||
} catch (error) { |
|||
await sqlite3.exec(db, 'ROLLBACK'); |
|||
throw error; |
|||
} finally { |
|||
await sqlite3.close(db); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### 5.3 Prepared Statements |
|||
```javascript |
|||
async function preparedStatementExample() { |
|||
const { sqlite3, db } = await initDatabase(); |
|||
|
|||
try { |
|||
// Prepare statement |
|||
const stmt = await sqlite3.prepare(db, 'SELECT * FROM users WHERE id = ?'); |
|||
|
|||
// Execute with different parameters |
|||
await sqlite3.bind(stmt, 1, 1); |
|||
while (await sqlite3.step(stmt) === SQLite.SQLITE_ROW) { |
|||
const row = sqlite3.row(stmt); |
|||
console.log(row); |
|||
} |
|||
|
|||
// Reset and reuse |
|||
await sqlite3.reset(stmt); |
|||
await sqlite3.bind(stmt, 1, 2); |
|||
// ... execute again |
|||
|
|||
await sqlite3.finalize(stmt); |
|||
} finally { |
|||
await sqlite3.close(db); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## 6. Performance Considerations |
|||
|
|||
1. **VFS Selection** |
|||
- Use IDBBatchAtomicVFS for general-purpose applications |
|||
- Consider OPFS-based VFS for better performance in Worker contexts |
|||
- Use MemoryVFS for temporary databases |
|||
|
|||
2. **Configuration** |
|||
- Set appropriate page size (default is usually fine) |
|||
- Use WAL mode for better concurrency |
|||
- Consider `PRAGMA synchronous=normal` for better performance |
|||
- Adjust cache size based on your needs |
|||
|
|||
3. **Concurrency** |
|||
- Use transactions for multiple operations |
|||
- Be aware of VFS-specific concurrency limitations |
|||
- Consider using Web Workers for heavy database operations |
|||
|
|||
## 7. Common Issues and Solutions |
|||
|
|||
1. **Database Locking** |
|||
- Use appropriate transaction isolation levels |
|||
- Implement retry logic for busy errors |
|||
- Consider using WAL mode |
|||
|
|||
2. **Storage Limitations** |
|||
- Be aware of browser storage quotas |
|||
- Implement cleanup strategies |
|||
- Monitor database size |
|||
|
|||
3. **Cross-Context Access** |
|||
- Use appropriate VFS for your context |
|||
- Consider message passing for cross-context communication |
|||
- Be aware of storage access limitations |
|||
|
|||
## 8. TypeScript Support |
|||
wa-sqlite includes TypeScript definitions. The main types are: |
|||
|
|||
```typescript |
|||
type SQLiteCompatibleType = number | string | Uint8Array | Array<number> | bigint | null; |
|||
|
|||
interface SQLiteAPI { |
|||
open_v2(filename: string, flags?: number, zVfs?: string): Promise<number>; |
|||
exec(db: number, sql: string, callback?: (row: any[], columns: string[]) => void): Promise<number>; |
|||
close(db: number): Promise<number>; |
|||
// ... other methods |
|||
} |
|||
``` |
|||
|
|||
## Additional Resources |
|||
|
|||
- [Official GitHub Repository](https://github.com/rhashimoto/wa-sqlite) |
|||
- [Online Demo](https://rhashimoto.github.io/wa-sqlite/demo/) |
|||
- [API Reference](https://rhashimoto.github.io/wa-sqlite/docs/) |
|||
- [FAQ](https://github.com/rhashimoto/wa-sqlite/issues?q=is%3Aissue+label%3Afaq+) |
|||
- [Discussion Forums](https://github.com/rhashimoto/wa-sqlite/discussions) |
@ -1,13 +0,0 @@ |
|||
# 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 |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 |
|||
# Using shared server by default to ease setup, which works for shared test users. |
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app |
|||
VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000 |
|||
#VITE_DEFAULT_PUSH_SERVER... can't be set up with localhost domain |
|||
VITE_PASSKEYS_ENABLED=true |
@ -1,12 +0,0 @@ |
|||
# Only the variables that start with VITE_ are seen in the application import.meta.env in Vue. |
|||
|
|||
|
|||
|
|||
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 |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch |
|||
|
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app |
|||
VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch |
|||
VITE_DEFAULT_PUSH_SERVER=https://timesafari.app |
@ -1,13 +0,0 @@ |
|||
# 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 |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch |
|||
|
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app |
|||
VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch |
|||
VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app |
|||
VITE_PASSKEYS_ENABLED=true |
@ -1,38 +0,0 @@ |
|||
module.exports = { |
|||
root: true, |
|||
env: { |
|||
node: true, |
|||
es2022: true, |
|||
}, |
|||
ignorePatterns: [ |
|||
'node_modules/', |
|||
'dist/', |
|||
'dist-electron/', |
|||
'*.d.ts' |
|||
], |
|||
extends: [ |
|||
"plugin:vue/vue3-recommended", |
|||
"eslint:recommended", |
|||
"@vue/typescript/recommended", |
|||
"plugin:prettier/recommended" |
|||
], |
|||
// parserOptions: {
|
|||
// ecmaVersion: 2020,
|
|||
// },
|
|||
rules: { |
|||
"max-len": ["warn", { |
|||
code: 100, |
|||
ignoreComments: true, |
|||
ignorePattern: '^\\s*class="[^"]*"$', |
|||
ignoreStrings: true, |
|||
ignoreTemplateLiterals: true, |
|||
ignoreUrls: true, |
|||
}], |
|||
"no-console": process.env.NODE_ENV === "production" ? "error" : "warn", |
|||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn", |
|||
"@typescript-eslint/no-explicit-any": "warn", |
|||
"@typescript-eslint/explicit-function-return-type": "off", |
|||
"@typescript-eslint/no-unnecessary-type-constraint": "off", |
|||
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] |
|||
}, |
|||
}; |
@ -1,27 +0,0 @@ |
|||
name: Playwright Tests |
|||
on: |
|||
push: |
|||
branches: [ main, master ] |
|||
pull_request: |
|||
branches: [ main, master ] |
|||
jobs: |
|||
test: |
|||
timeout-minutes: 60 |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- uses: actions/checkout@v4 |
|||
- uses: actions/setup-node@v4 |
|||
with: |
|||
node-version: lts/* |
|||
- name: Install dependencies |
|||
run: npm ci |
|||
- name: Install Playwright Browsers |
|||
run: npx playwright install --with-deps |
|||
- name: Run Playwright tests |
|||
run: npx playwright test |
|||
- uses: actions/upload-artifact@v4 |
|||
if: always() |
|||
with: |
|||
name: playwright-report |
|||
path: playwright-report/ |
|||
retention-days: 30 |
@ -1,58 +1,24 @@ |
|||
squashfs-root |
|||
dist-electron |
|||
dist-electon-build |
|||
.DS_Store |
|||
node_modules |
|||
dist |
|||
signature.bin |
|||
# generated during `npm run build` |
|||
sw_scripts-combined.js |
|||
*.pem |
|||
verified.txt |
|||
myenv |
|||
|
|||
*~ |
|||
# local env files |
|||
.env.local |
|||
.env.*.local |
|||
|
|||
# Log filesopenssl dgst -sha256 -verify public.pem -signature <(echo -n "$signature") "$signing_input" |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
pnpm-debug.log* |
|||
lerna-debug.log* |
|||
|
|||
node_modules |
|||
dist |
|||
dist-ssr |
|||
*.local |
|||
|
|||
# Editor directories and files |
|||
.vscode/* |
|||
!.vscode/extensions.json |
|||
.idea |
|||
.vscode |
|||
.DS_Store |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw? |
|||
/test-results/ |
|||
/playwright-report/ |
|||
/blob-report/ |
|||
/playwright/.cache/ |
|||
/dist-electron-build/ |
|||
/dist-capacitor/ |
|||
/test-playwright-results/ |
|||
playwright-tests |
|||
dist-electron-packages |
|||
.ruby-version |
|||
+.env |
|||
|
|||
# Test files generated by scripts test-ios.js & test-android.js |
|||
.generated/ |
|||
|
|||
.env.default |
|||
vendor/ |
|||
|
|||
# Build logs |
|||
build_logs/ |
|||
|
|||
# PWA icon files generated by capacitor-assets |
|||
icons |
|||
|
|||
|
|||
android/app/src/main/res/ |
@ -0,0 +1,3 @@ |
|||
{ |
|||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] |
|||
} |
@ -1,484 +0,0 @@ |
|||
# Building TimeSafari |
|||
|
|||
This guide explains how to build TimeSafari for different platforms. |
|||
|
|||
## Prerequisites |
|||
|
|||
For a quick dev environment setup, use [pkgx](https://pkgx.dev). |
|||
|
|||
- Node.js (LTS version recommended) |
|||
- npm (comes with Node.js) |
|||
- Git |
|||
- For desktop builds: Additional build tools based on your OS |
|||
|
|||
## Forks |
|||
|
|||
If you have forked this to make your own app, you'll want to customize the iOS & Android files. You can either edit existing ones, or you can remove the `ios` and `android` directories and regenerate them before the `npx cap sync` step in each setup. |
|||
|
|||
```bash |
|||
npx cap add android |
|||
npx cap add ios |
|||
``` |
|||
|
|||
You'll also want to edit the deep link configuration (see below). |
|||
|
|||
## Initial Setup |
|||
|
|||
Install dependencies: |
|||
|
|||
```bash |
|||
npm install |
|||
``` |
|||
|
|||
## Web Dev Locally |
|||
|
|||
```bash |
|||
npm run dev |
|||
``` |
|||
|
|||
## Web Build for Server |
|||
|
|||
1. Run the production build: |
|||
|
|||
```bash |
|||
rm -rf dist |
|||
npm run build:web |
|||
``` |
|||
|
|||
The built files will be in the `dist` directory. |
|||
|
|||
2. To test the production build locally: |
|||
|
|||
You'll likely want to use test locations for the Endorser & image & partner servers; see "DEFAULT_ENDORSER_API_SERVER" & "DEFAULT_IMAGE_API_SERVER" & "DEFAULT_PARTNER_API_SERVER" below. |
|||
|
|||
```bash |
|||
npm run serve |
|||
``` |
|||
|
|||
### Compile and minify for test & production |
|||
|
|||
* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations. |
|||
|
|||
* `npx prettier --write ./sw_scripts/` |
|||
|
|||
* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`. |
|||
|
|||
* Commit everything (since the commit hash is used the app). |
|||
|
|||
* Run a build to make sure package-lock version is updated, linting works, etc: `npm install && npm run build` |
|||
|
|||
* Put the commit hash in the changelog (which will help you remember to bump the version later). |
|||
|
|||
* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 0.3.55 && git push origin 0.3.55`. |
|||
|
|||
* For test, build the app (because test server is not yet set up to build): |
|||
|
|||
```bash |
|||
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app VITE_PASSKEYS_ENABLED=true npm run build:web |
|||
``` |
|||
|
|||
... and transfer to the test server: |
|||
|
|||
```bash |
|||
rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari |
|||
``` |
|||
|
|||
(Let's replace that with a .env.development or .env.staging file.) |
|||
|
|||
(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.) |
|||
|
|||
* For prod, get on the server and run the correct build: |
|||
|
|||
... and log onto the server: |
|||
|
|||
* `pkgx +npm sh` |
|||
|
|||
* `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 0.3.55 && npm install && npm run build && cd -` |
|||
|
|||
(The plain `npm run build` uses the .env.production file.) |
|||
|
|||
* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/` |
|||
|
|||
* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production. |
|||
|
|||
## Docker Deployment |
|||
|
|||
The application can be containerized using Docker for consistent deployment across environments. |
|||
|
|||
### Prerequisites |
|||
|
|||
- Docker installed on your system |
|||
- Docker Compose (optional, for multi-container setups) |
|||
|
|||
### Building the Docker Image |
|||
|
|||
1. Build the Docker image: |
|||
|
|||
```bash |
|||
docker build -t timesafari:latest . |
|||
``` |
|||
|
|||
2. For development builds with specific environment variables: |
|||
|
|||
```bash |
|||
docker build --build-arg NODE_ENV=development -t timesafari:dev . |
|||
``` |
|||
|
|||
### Running the Container |
|||
|
|||
1. Run the container: |
|||
|
|||
```bash |
|||
docker run -d -p 80:80 timesafari:latest |
|||
``` |
|||
|
|||
2. For development with hot-reloading: |
|||
|
|||
```bash |
|||
docker run -d -p 80:80 -v $(pwd):/app timesafari:dev |
|||
``` |
|||
|
|||
### Using Docker Compose |
|||
|
|||
Create a `docker-compose.yml` file: |
|||
|
|||
```yaml |
|||
version: '3.8' |
|||
services: |
|||
timesafari: |
|||
build: . |
|||
ports: |
|||
- "80:80" |
|||
environment: |
|||
- NODE_ENV=production |
|||
restart: unless-stopped |
|||
``` |
|||
|
|||
Run with Docker Compose: |
|||
|
|||
```bash |
|||
docker-compose up -d |
|||
``` |
|||
|
|||
### Production Deployment |
|||
|
|||
For production deployment, consider the following: |
|||
|
|||
1. Use specific version tags instead of 'latest' |
|||
2. Implement health checks |
|||
3. Configure proper logging |
|||
4. Set up reverse proxy with SSL termination |
|||
5. Use Docker secrets for sensitive data |
|||
|
|||
Example production deployment: |
|||
|
|||
```bash |
|||
# Build with specific version |
|||
docker build -t timesafari:1.0.0 . |
|||
|
|||
# Run with production settings |
|||
docker run -d \ |
|||
--name timesafari \ |
|||
-p 80:80 \ |
|||
--restart unless-stopped \ |
|||
-e NODE_ENV=production \ |
|||
timesafari:1.0.0 |
|||
``` |
|||
|
|||
### Troubleshooting Docker |
|||
|
|||
1. **Container fails to start** |
|||
- Check logs: `docker logs <container_id>` |
|||
- Verify port availability |
|||
- Check environment variables |
|||
|
|||
2. **Build fails** |
|||
- Ensure all dependencies are in package.json |
|||
- Check Dockerfile syntax |
|||
- Verify build context |
|||
|
|||
3. **Performance issues** |
|||
- Monitor container resources: `docker stats` |
|||
- Check nginx configuration |
|||
- Verify caching settings |
|||
|
|||
## Desktop Build (Electron) |
|||
|
|||
### Linux Build |
|||
|
|||
1. Build the electron app in production mode: |
|||
|
|||
```bash |
|||
npm run build:electron-prod |
|||
``` |
|||
|
|||
2. Package the Electron app for Linux: |
|||
|
|||
```bash |
|||
# For AppImage (recommended) |
|||
npm run electron:build-linux |
|||
|
|||
# For .deb package |
|||
npm run electron:build-linux-deb |
|||
``` |
|||
|
|||
3. The packaged applications will be in `dist-electron-packages/`: |
|||
- AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage` |
|||
- DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb` |
|||
|
|||
### macOS Build |
|||
|
|||
1. Build the electron app in production mode: |
|||
|
|||
```bash |
|||
npm run build:web |
|||
npm run build:electron |
|||
npm run electron:build-mac |
|||
``` |
|||
|
|||
2. Package the Electron app for macOS: |
|||
|
|||
```bash |
|||
# For Intel Macs |
|||
npm run electron:build-mac |
|||
|
|||
# For Universal build (Intel + Apple Silicon) |
|||
npm run electron:build-mac-universal |
|||
``` |
|||
|
|||
3. The packaged applications will be in `dist-electron-packages/`: |
|||
- `.app` bundle: `TimeSafari.app` |
|||
- `.dmg` installer: `TimeSafari-x.x.x.dmg` |
|||
- `.zip` archive: `TimeSafari-x.x.x-mac.zip` |
|||
|
|||
### Code Signing and Notarization (macOS) |
|||
|
|||
For public distribution on macOS, you need to code sign and notarize your app: |
|||
|
|||
1. Set up environment variables: |
|||
```bash |
|||
export CSC_LINK=/path/to/your/certificate.p12 |
|||
export CSC_KEY_PASSWORD=your_certificate_password |
|||
export APPLE_ID=your_apple_id |
|||
export APPLE_ID_PASSWORD=your_app_specific_password |
|||
``` |
|||
|
|||
2. Build with signing: |
|||
```bash |
|||
npm run electron:build-mac |
|||
``` |
|||
|
|||
### Running the Packaged App |
|||
|
|||
- **Linux**: |
|||
- AppImage: Make executable and run |
|||
```bash |
|||
chmod +x dist-electron-packages/TimeSafari-*.AppImage |
|||
./dist-electron-packages/TimeSafari-*.AppImage |
|||
``` |
|||
- DEB: Install and run |
|||
```bash |
|||
sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb |
|||
timesafari |
|||
``` |
|||
|
|||
- **macOS**: |
|||
- `.app` bundle: Double-click `TimeSafari.app` in Finder |
|||
- `.dmg` installer: |
|||
1. Double-click the `.dmg` file |
|||
2. Drag the app to your Applications folder |
|||
3. Launch from Applications |
|||
- `.zip` archive: |
|||
1. Extract the `.zip` file |
|||
2. Move `TimeSafari.app` to your Applications folder |
|||
3. Launch from Applications |
|||
|
|||
Note: If you get a security warning when running the app: |
|||
1. Right-click the app |
|||
2. Select "Open" |
|||
3. Click "Open" in the security dialog |
|||
|
|||
### Development Testing |
|||
|
|||
For testing the Electron build before packaging: |
|||
|
|||
```bash |
|||
# Build and run in development mode (includes DevTools) |
|||
npm run electron:dev |
|||
|
|||
# Build in production mode and test |
|||
npm run build:electron-prod && npm run electron:start |
|||
``` |
|||
|
|||
## Mobile Builds (Capacitor) |
|||
|
|||
### iOS Build |
|||
|
|||
Prerequisites: macOS with Xcode installed |
|||
|
|||
#### First-time iOS Configuration |
|||
|
|||
- Generate certificates inside XCode. |
|||
|
|||
- Right-click on App and under Signing & Capabilities set the Team. |
|||
|
|||
#### Each Release |
|||
|
|||
0. First time (or if dependencies change): |
|||
|
|||
- `pkgx +rubygems.org sh` |
|||
|
|||
- ... and you may have to fix these, especially with pkgx: |
|||
|
|||
```bash |
|||
gem_path=$(which gem) |
|||
shortened_path="${gem_path:h:h}" |
|||
export GEM_HOME=$shortened_path |
|||
export GEM_PATH=$shortened_path |
|||
``` |
|||
|
|||
1. Build the web assets & update ios: |
|||
|
|||
```bash |
|||
rm -rf dist |
|||
npm run build:web |
|||
npm run build:capacitor |
|||
npx cap sync ios |
|||
``` |
|||
|
|||
- If that fails with "Could not find..." then look at the "gem_path" instructions above. |
|||
|
|||
3. Copy the assets: |
|||
|
|||
```bash |
|||
# It makes no sense why capacitor-assets will not run without these but it actually changes the contents. |
|||
mkdir -p ios/App/App/Assets.xcassets/AppIcon.appiconset |
|||
echo '{"images":[]}' > ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json |
|||
mkdir -p ios/App/App/Assets.xcassets/Splash.imageset |
|||
echo '{"images":[]}' > ios/App/App/Assets.xcassets/Splash.imageset/Contents.json |
|||
npx capacitor-assets generate --ios |
|||
``` |
|||
|
|||
4. Bump the version to match Android & package.json: |
|||
|
|||
``` |
|||
cd ios/App |
|||
xcrun agvtool new-version 34 |
|||
# Unfortunately this edits Info.plist directly. |
|||
#xcrun agvtool new-marketing-version 0.4.5 |
|||
cat App.xcodeproj/project.pbxproj | sed "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 0.5.8;/g" > temp && mv temp App.xcodeproj/project.pbxproj |
|||
cd - |
|||
``` |
|||
|
|||
5. Open the project in Xcode: |
|||
|
|||
```bash |
|||
npx cap open ios |
|||
``` |
|||
|
|||
6. Use Xcode to build and run on simulator or device. |
|||
|
|||
* Select Product -> Destination with some Simulator version. Then click the run arrow. |
|||
|
|||
7. Release |
|||
|
|||
* Someday: Under "General" we want to rename a bunch of things to "Time Safari" |
|||
* Choose Product -> Destination -> Any iOS Device |
|||
* Choose Product -> Archive |
|||
* This will trigger a build and take time, needing user's "login" keychain password (user's login password), repeatedly. |
|||
* If it fails with `building for 'iOS', but linking in dylib (.../.pkgx/zlib.net/v1.3.0/lib/libz.1.3.dylib) built for 'macOS'` then run XCode outside that terminal (ie. not with `npx cap open ios`). |
|||
* Click Distribute -> App Store Connect |
|||
* In AppStoreConnect, add the build to the distribution: remove the current build with the "-" when you hover over it, then "Add Build" with the new build. |
|||
* May have to go to App Review, click Submission, then hover over the build and click "-". |
|||
* It can take 15 minutes for the build to show up in the list of builds. |
|||
* You'll probably have to "Manage" something about encryption, disallowed in France. |
|||
* Then "Save" and "Add to Review" and "Resubmit to App Review". |
|||
|
|||
### Android Build |
|||
|
|||
Prerequisites: Android Studio with Java SDK installed |
|||
|
|||
1. Build the web assets: |
|||
|
|||
```bash |
|||
rm -rf dist |
|||
npm run build:web |
|||
npm run build:capacitor |
|||
``` |
|||
|
|||
2. Update Android project with latest build: |
|||
|
|||
```bash |
|||
npx cap sync android |
|||
``` |
|||
|
|||
3. Copy the assets |
|||
|
|||
```bash |
|||
npx capacitor-assets generate --android |
|||
``` |
|||
|
|||
4. Bump version to match iOS & package.json: android/app/build.gradle |
|||
|
|||
5. Open the project in Android Studio: |
|||
|
|||
```bash |
|||
npx cap open android |
|||
``` |
|||
|
|||
6. Use Android Studio to build and run on emulator or device. |
|||
|
|||
## Android Build from the console |
|||
|
|||
```bash |
|||
cd android |
|||
./gradlew clean |
|||
./gradlew build -Dlint.baselines.continue=true |
|||
cd - |
|||
``` |
|||
|
|||
... or, to create the `aab` file, `bundle` instead of `build`: |
|||
|
|||
```bash |
|||
./gradlew bundleDebug -Dlint.baselines.continue=true |
|||
``` |
|||
|
|||
... or, to create a signed release: |
|||
|
|||
* Setup by adding the app/gradle.properties.secrets file (see properties at top of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file |
|||
* In app/build.gradle, bump the versionCode and maybe the versionName |
|||
* Then `bundleRelease`: |
|||
|
|||
```bash |
|||
cd android |
|||
./gradlew bundleRelease -Dlint.baselines.continue=true |
|||
cd - |
|||
``` |
|||
|
|||
... and find your `aab` file at app/build/outputs/bundle/release |
|||
|
|||
At play.google.com/console: |
|||
|
|||
- Go to the Testing Track (eg. Closed). |
|||
- Click "Create new release". |
|||
- Upload the `aab` file. |
|||
- Hit "Next". |
|||
- Save, go to "Publishing Overview" as prompted, and click "Send changes for review". |
|||
|
|||
- Note that if you add testers, you have to go to "Publishing Overview" and send those changes or your (closed) testers won't see it. |
|||
|
|||
|
|||
## Android Configuration for deep links |
|||
|
|||
You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file: |
|||
|
|||
```xml |
|||
<intent-filter android:autoVerify="true"> |
|||
<action android:name="android.intent.action.VIEW" /> |
|||
<category android:name="android.intent.category.DEFAULT" /> |
|||
<category android:name="android.intent.category.BROWSABLE" /> |
|||
<data android:scheme="timesafari" /> |
|||
</intent-filter> |
|||
``` |
|||
|
|||
... though when we tried that most recently it failed to 'build' the APK with: http(s) scheme and host attribute are missing, but are required for Android App Links [AppLinkUrlError] |
@ -1,743 +0,0 @@ |
|||
# Changelog |
|||
|
|||
All notable changes to this project will be documented in this file. |
|||
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
|||
|
|||
|
|||
## [0.5.8] |
|||
### Added |
|||
- /deep-link/ path for URLs that are shared with people |
|||
### Changed |
|||
- External links now go to /deep-link/... |
|||
- Feed visuals now have arrow imagery from giver to receiver |
|||
|
|||
|
|||
## [0.4.7] |
|||
### Fixed |
|||
- Cameras everywhere |
|||
### Changed |
|||
- IndexedDB -> SQLite |
|||
|
|||
|
|||
## [0.4.5] - 2025.02.23 |
|||
### Added |
|||
- Total amounts of gives on project page |
|||
### Changed in DB or environment |
|||
- Requires Endorser.ch version 4.2.6+ |
|||
|
|||
|
|||
## [0.4.4] - 2025.02.17 |
|||
|
|||
### Fixed in 0.4.4 |
|||
|
|||
- On production (due to data?) the search results would disappear after scrolling down. Now we don't show any results when going to the people map with a shortcut. |
|||
|
|||
## [0.4.3] - 2025.02.17 |
|||
|
|||
### Added in 0.4.3 |
|||
|
|||
- Discover query parameter searchPeople to go directly to the people map |
|||
|
|||
## [0.4.2] - 2025.02.17 |
|||
|
|||
### Added |
|||
|
|||
- Capacitor on iOS and Android |
|||
|
|||
### Fixed |
|||
|
|||
- Path issues |
|||
|
|||
## [0.4.1] - 2025.02.16 |
|||
|
|||
### Fixed in 0.4.1 |
|||
|
|||
- nostr build issue |
|||
- Linting |
|||
|
|||
## [0.4.0] - 2025.02.14 |
|||
|
|||
### Changed |
|||
|
|||
- Images in the home feed now take up the full width of the card. |
|||
- Clicking the image previously, would open the image in a new tab. Now, clicking the image opens the image in a lightbox view. |
|||
|
|||
### Added in 0.4.0 |
|||
|
|||
- Clicking an image also now displays an in-app lightbox view of the image. |
|||
- The lightbox view includes a download button for the image in mobile view. |
|||
|
|||
## [0.3.57] - 2025.02.11 |
|||
|
|||
### Added in 0.3.57 |
|||
|
|||
- Automatic user creation in onboarding meetings |
|||
|
|||
## [0.3.55] - 2025.02.07 |
|||
|
|||
### Added in 0.3.55 |
|||
|
|||
- End time for projects |
|||
|
|||
## [0.3.54] - 2025.02.06 |
|||
|
|||
### Added in 0.3.54 |
|||
|
|||
- Group onboarding meetings |
|||
|
|||
## [0.3.53] - 2025.01.30 |
|||
|
|||
### Added in 0.3.53 |
|||
|
|||
- Hints for contacting the creator of a project |
|||
|
|||
## [0.3.52] - 2025.01.22 |
|||
|
|||
### Fixed in 0.3.52 |
|||
|
|||
- User profile endpoint server for map was broken. |
|||
|
|||
## [0.3.51] - 2025.01.22 |
|||
|
|||
### Fixed in 0.3.51 |
|||
|
|||
- User profile map jumped on first zoom. |
|||
|
|||
## [0.3.50] - 2025.01.20 - b9fedcd3fd3e34c3fb0fc79150d1a81a76eaeb40 |
|||
|
|||
### Added in 0.3.50 |
|||
|
|||
- User public profiles |
|||
|
|||
## [0.3.49] - 2025.01.09 - 36301ed238ff84df25bb11a8d44a295ee7eaf0f8 |
|||
|
|||
### Changed in 0.3.49 |
|||
|
|||
- Make all external contact links direct to the contact-import page. |
|||
- Handle all new-single-contact JWTs in the contacts page, and multiple-contact JWTs in the contacts-import page. |
|||
|
|||
## [0.3.48] - 2025.01.08 - 398f3e64a376789f7eb1c400cd886f5a2cacd588 (but app shows 07c4e58) |
|||
|
|||
### Added in 0.3.48 |
|||
|
|||
- More sanity-checks on contact-import JWT |
|||
|
|||
## [0.3.47] - 2025.01.06 - 5bf6dd1ee32ca7cc46d39bd7afca58365b422f93 |
|||
|
|||
### Added in 0.3.47 |
|||
|
|||
- Notes on contacts page with new contact-edit page |
|||
- Contact methods (only on contact-edit page and under DID details) |
|||
- DID view with no DID shows user's info. |
|||
|
|||
### Changed in 0.3.47 |
|||
|
|||
- URL for user's contact info is now URL to this app (not endorser.ch). |
|||
- Extended details (eg. full claim) is beneath details link on claim page. |
|||
|
|||
## [0.3.46] - 2025.01.03 - 9e7056616b5e5acc51e5a8cf7354d408029fefb3 |
|||
|
|||
### Added in 0.3.46 |
|||
|
|||
- More action-oriented questions for the gift prompts |
|||
|
|||
### Fixed in 0.3.46 |
|||
|
|||
- Contact-list import set visibility for all, even if not chosen. |
|||
|
|||
## [0.3.45] - 2025.01.01 - 65402dc68ce69ccc6cb9aa8d2e7a9249bf4298e0 |
|||
|
|||
### Fixed in 0.3.45 |
|||
|
|||
- Previous project links stayed when following a link. |
|||
|
|||
## [0.3.44] - 2024.12.31 - 694b22987b05482e4527c2478bbe15e6b6f3b532 |
|||
|
|||
### Added in 0.3.44 |
|||
|
|||
- Project counts on a map |
|||
|
|||
## [0.3.42] - 2024.12.27 - 9751934bc24a1040415a8cfeacbae59ed91f92a5 |
|||
|
|||
### Added in 0.3.42 |
|||
|
|||
- Link from certificate page to the claim |
|||
|
|||
### Changed in 0.3.42 |
|||
|
|||
- Contact data sharing is now a verified JWT. |
|||
- Feed pictures are larger. |
|||
|
|||
## [0.3.41] - 2024.12.21 - ff6d14138f26daea6216b051562f0a04681f69fc |
|||
|
|||
### Added in 0.3.41 |
|||
|
|||
- Link from certificate page to the claim |
|||
|
|||
## [0.3.40] - 2024.12.20 - 77290d9fed3c364243793dc3e9bfe2e994a016b8 |
|||
|
|||
### Added in 0.3.40 |
|||
|
|||
- Only show issuer on certificate if it's not the agent. |
|||
|
|||
## [0.3.39] - 2024.12.20 - d8819155e2acd2b57fdab523168fa5d1d09e80cc |
|||
|
|||
### Added in 0.3.39 |
|||
|
|||
- Page for a framed claim certificate |
|||
|
|||
## [0.3.38] - 2024.12.14 - f8cae5ad4fee1f114320dcce052299eab12108b2 |
|||
|
|||
### Fixed in 0.3.38 |
|||
|
|||
- Error on BVC confirmation screen (from IndexedDB refactor) |
|||
|
|||
## [0.3.37] - 2024.12.13 - 4d805b43cd25eed73cdd6651f36ad1ec8c109555 |
|||
|
|||
### Added in 0.3.37 |
|||
|
|||
- Record a give from a project on the project page. |
|||
- New button on home page opens the gifted dialog. |
|||
- On confirmation buttons on the project page gives, mark when unavailable and explain why. |
|||
|
|||
### Changed in 0.3.37 |
|||
|
|||
- Moved the secret into IndexedDB (and out of localStorage) for more reliability. |
|||
- New "invite" destination page helps troubleshoot when JWT link doesn't come through. |
|||
|
|||
### Fixed in 0.3.37 |
|||
|
|||
- Problem showing claim issuer name |
|||
- Problem going "back" from a project page |
|||
|
|||
## [0.3.36] - 2024.11.24 - c8d23647d165016f8a8f575e13d32583242e53ac |
|||
|
|||
### Changed in 0.3.36 |
|||
|
|||
- More friendly default reminder message |
|||
- Blue borders around people to indicate clickability |
|||
|
|||
## [0.3.35] - 2024.11.24 - bff7d0a6320b70349185e26bfac72e3bb17f76df |
|||
|
|||
### Added in 0.3.35 |
|||
|
|||
- Daily reliable, hard-coded notification message |
|||
- Setting to change the partner API server |
|||
|
|||
## [0.3.33] - 2024.11.07 - adb7b16ecf1343c39cba71a7d6bb0e7a973e1102 |
|||
|
|||
### Fixed in 0.3.33 |
|||
|
|||
- Affirm Delivery button on offer claim page didn't work. |
|||
- Plans were not showing by default on project page. |
|||
|
|||
## [0.3.32] - 2024.11.06 - 9a3fa38a3fd28f977e06f0265fc39e635c9c5ccd |
|||
|
|||
### Added in 0.3.32 |
|||
|
|||
- Highlight in green new offers to user & to user's projects on the front page. |
|||
|
|||
## [0.3.31] - 2024.10.25 - 07c02ab98a09d293dd90d9289a7872e7d681d296 |
|||
|
|||
### Changed in 0.3.31 |
|||
|
|||
- Onboarding messages about offers |
|||
|
|||
## [0.3.30] |
|||
|
|||
### Added in 0.3.30 |
|||
|
|||
- Onboarding messages |
|||
|
|||
## [0.3.29] - 2024.10.09 - babd3832bdfe0c40eaa3869de1b41399a51713c1 |
|||
|
|||
### Added in 0.3.29 |
|||
|
|||
- Invite for a contact to join immediately |
|||
|
|||
### Changed in 0.3.29 |
|||
|
|||
- Send signed data to nostr endpoints to verify public key ownership. |
|||
- Enhanced help & help onboarding. |
|||
|
|||
### Changed in DB or environment |
|||
|
|||
- Uses Endorser.ch version 4.1.1 |
|||
|
|||
## [0.3.28] - 2024.09.30 - 84720b94049d29cc0ddd99c50cef2e7176130133 |
|||
|
|||
### Added in 0.3.28 |
|||
|
|||
- Posting to nostr apps Trustroots & TripHopping |
|||
- Display of providers on claim view page |
|||
|
|||
### Changed in 0.3.28 |
|||
|
|||
- Switched BVC-meeting-ending gift to be a gift from the group. |
|||
|
|||
### Changed in DB or environment in 0.3.28 |
|||
|
|||
- Requires Endorser.ch version 4.1.0 |
|||
|
|||
## [0.3.27] - 2024.09.22 - ee23e6f005e47f5bd6f04d804599f6395371b0e4 |
|||
|
|||
### Fixed in 0.3.27 |
|||
|
|||
- Error loading BVC claims to confirm |
|||
- Really allow visibility of bulk-imported contacts |
|||
|
|||
## [0.3.26] - 2024.09.16 - 8263ed2b29947b3ccc6f3133bbc9454c222bce28 |
|||
|
|||
### Added in 0.3.26 |
|||
|
|||
- Separate 'isRegistered' flag for each account |
|||
|
|||
### Fixed in 0.3.26 |
|||
|
|||
- Failure to assign offers to their project |
|||
- Alert when looking at one's own activity if not in contacts. |
|||
|
|||
## [0.3.25] - 2024.08.30 - dcbe02d877aecb4cdef2643d90e6595d246a9f82 |
|||
|
|||
### Added in 0.3.25 |
|||
|
|||
- "Ideas" now jumps directly to giving prompt or contact list. |
|||
|
|||
### Fixed in 0.3.25 |
|||
|
|||
- Empty giver name on gifted-details view |
|||
- Previously visited project would show up on the giving-details page. |
|||
|
|||
### Removed in 0.3.25 |
|||
|
|||
- All unnecessary localStorage for project IDs |
|||
|
|||
## [0.3.23] - 2024.08.30 |
|||
|
|||
### Added in 0.3.23 |
|||
|
|||
- Sections in Help for different kinds of users |
|||
- Discovery page parameters so that links with search text work |
|||
- Message when no projects are found |
|||
|
|||
## [0.3.21] - 2024.08.24 - a7b89f4bb6da928d56daeffaae7741fa74cc80bf |
|||
|
|||
### Added in 0.3.21 |
|||
|
|||
- Send list of contacts to someone, and move individual contact actions to detail page. |
|||
- Prompt for name in pop-up, and send to different contact-sharing screens. |
|||
|
|||
### Changed in 0.3.21 |
|||
|
|||
- Moved contact actions from list onto detail page |
|||
|
|||
## [0.3.20] - 2024.08.18 - 4064eb75a9743ca268bf00016fa0a5fc5dec4e30 |
|||
|
|||
### Fixed in 0.3.20 |
|||
|
|||
- Bad "give" verbiage on offer page |
|||
- Failing offer test |
|||
|
|||
## [0.3.19] - 2024.08.18 - ee9c14942ceba993bf21a11249601f205158ec71 |
|||
|
|||
### Added in 0.3.19 |
|||
|
|||
- Update of an offer |
|||
- Recipient description in offer list |
|||
|
|||
### Fixed in 0.3.19 |
|||
|
|||
- List of offers wasn't showing. |
|||
- Destination page after sharing photo was wrong. |
|||
|
|||
## [0.3.17] - 2024.07.11 - cefa384ff1a2d922848c370640c096c529920fab |
|||
|
|||
### Added in 0.3.17 |
|||
|
|||
- Photos on more screens |
|||
|
|||
### Fixed in 0.3.17 |
|||
|
|||
- Share of a photo, including sharing a photo from webkit/Safari which never worked |
|||
|
|||
### Changed in DB or environment in 0.3.17 |
|||
|
|||
- Nothing (though there's a new temp field in IndexedDB) |
|||
|
|||
## [0.3.15] - 2024.08.04 - c8f0f2c2b16b9f0b4b47d40f7bf29058c7baa68e |
|||
|
|||
### Added in 0.3.15 |
|||
|
|||
- Edit gives |
|||
- Page to edit claim JSON before submitting |
|||
- Update of imported contacts |
|||
- Improve messaging on give dialog |
|||
- Section for gives provided by plan |
|||
- Deletion of an identity |
|||
- UI for choosing a passkey creation (not enabled on prod) |
|||
- Cache signatures for reports for passkey-signed requests |
|||
- Refactor: consolidate alternative signing, eg. for passkeys & did:peer |
|||
- Playwright tests |
|||
|
|||
### Changed in 0.3.15 |
|||
|
|||
- Linked projects display below description (instead of at bottom) |
|||
|
|||
### Fixed in 0.3.15 |
|||
|
|||
- Visibility toggle appearance |
|||
|
|||
### Changed in DB or environment in 0.3.15 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.14] - 2024.06.22 - 1611d22892f683f43856d2503eee7f391b6bbce8 |
|||
|
|||
### Added in 0.3.14 |
|||
|
|||
- Clearer give-confirmation screen |
|||
- BX currency <https://thebx.medium.com/> |
|||
- Deselection of project on gifted details page |
|||
|
|||
### Fixed in 0.3.14 |
|||
|
|||
- Don't show registration pop-up for a new contact that is registered |
|||
|
|||
### Changed in DB or environment in 0.3.14 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.13] - 2024.05.24 - 08b67984e443c58d9178ad3776013b0bce7afddc |
|||
|
|||
### Added in 0.3.13 |
|||
|
|||
- Photos on projects |
|||
|
|||
### Changed in DB or environment in 0.3.13 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.12] - 2024.05.19 - 141fb39ad19c44d82fe1a33bf85115beacf50870 |
|||
|
|||
### Fixed in 0.3.12 |
|||
|
|||
- Photo share (share_target) failed because requests were sent to server |
|||
|
|||
### Changed in DB or environment in 0.3.12 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.11] - 2024.05.19 - 567bcad88dfb7e9ac8fea72530d1163985e4a7cc |
|||
|
|||
### Added in 0.3.11 |
|||
|
|||
- Choose a file for gifts, and a URL for gifts & profiles |
|||
|
|||
### Fixed in 0.3.11 |
|||
|
|||
- Multiple button pushes were required to switch camera |
|||
|
|||
### Changed in DB or environment in 0.3.11 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.10] - 2024.05.11 - 03ac31d98110f7828cf9acb366db8d01b185f64c |
|||
|
|||
### Added in 0.3.10 |
|||
|
|||
- Share an image |
|||
- Choose a file on the device for a profile image |
|||
|
|||
### Changed in DB or environment in 0.3.10 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.9] - 2024.04.28 - 874e717e698b93a1ace9f588e675b8a3dccd7617 |
|||
|
|||
### Added in 0.3.9 |
|||
|
|||
- Offers on contacts page |
|||
- Checks on front page until they show as registered |
|||
|
|||
### Changed in 0.3.9 |
|||
|
|||
- Scanned contacts now add immediately and prompt for registration. |
|||
- Better UI for gives on contact page |
|||
- Better UI for all confirmation messages |
|||
|
|||
### Fixed in 0.3.9 |
|||
|
|||
- Repeated elements at top of main feed |
|||
|
|||
### Changed in DB or environment in 0.3.9 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.8] - 2024.04.20 - 15c026c80ce03a26cae3ff80b0888934c101c7e2 |
|||
|
|||
### Added in 0.3.8 |
|||
|
|||
- Profile image for user |
|||
|
|||
### Fixed in 0.3.8 |
|||
|
|||
- Slow loading of home page feed |
|||
|
|||
### Changed in DB or environment in 0.3.8 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.7] - 2024.04.10 - cf18f1543a700d62a5f9e764905a4aafe1fb229b |
|||
|
|||
### Added in 0.3.7 |
|||
|
|||
- Filter on home page feed |
|||
- Ability to set time of daily notification |
|||
- Jump to app on click of notification |
|||
|
|||
### Changed in 0.3.7 |
|||
|
|||
- Built with vite |
|||
- Descriptions on home page to include projects |
|||
|
|||
### Changed in DB or environment in 0.3.7 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.6] - 2024.03.24 - 3a07e31d6313ab95711265562d9023c42916e141 |
|||
|
|||
### Added in 0.3.6 |
|||
|
|||
- Button to mirror photo during video |
|||
- More detailed onboarding help screen |
|||
- Public-data blurb |
|||
|
|||
### Changed in DB or environment in 0.3.6 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.3.5] - 2024.03.23 - 28754bdfb1e11aa221dd49a5dce4219b69cf6a9d |
|||
|
|||
### Added in 0.3.5 |
|||
|
|||
- Photo on gift records |
|||
|
|||
### Fixed in 0.3.5 |
|||
|
|||
- Environment variable for BVC meetings project |
|||
- Environment variables and build enhancements for test vs prod |
|||
|
|||
### Changed in DB or environment in 0.3.5 |
|||
|
|||
- New environment variable for image API server |
|||
- Test that a new browser session will get the right default APIs. |
|||
- Test that a new browser session will send the right BVC meetings project. |
|||
|
|||
## [0.2.17] - 2024.03.01 - 3612ea42240c5e1b7d7eff29a39ff18f1b869b36 |
|||
|
|||
### Added in 0.2.17 |
|||
|
|||
- Shortcut page for Bountiful Voluntaryist Community |
|||
|
|||
### Changed in 0.2.17 |
|||
|
|||
- More readable, targeted summaries in home-page feed items |
|||
|
|||
### Changed in DB |
|||
|
|||
- Nothing |
|||
|
|||
## [0.2.14] - 2024.02.14 - 5f9edea1167dbfb64e16648764eed8c09b24eaeb |
|||
|
|||
### Changed in 0.2.14 |
|||
|
|||
- Combine all service worker scripts into a single file. |
|||
|
|||
### Changed in DB in 0.2.14 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.2.13] - 2024.02.07 |
|||
|
|||
### Added in 0.2.13 |
|||
|
|||
- Display of user's offers |
|||
- Check for valid DIDs |
|||
|
|||
### Fixed in 0.2.13 |
|||
|
|||
- Name display on give prompt |
|||
- Non-numbers on number input & autocapitalize on URL input |
|||
|
|||
### Changed in DB in 0.2.13 |
|||
|
|||
- Nothing |
|||
|
|||
## [0.2.12] - 2024.02.01 |
|||
|
|||
### Added in 0.2.12 |
|||
|
|||
- Prompts for gratitude |
|||
|
|||
## [0.2.11] - 2024.01.28 |
|||
|
|||
### Added in 0.2.11 |
|||
|
|||
- Actions to share claim data with contacts |
|||
- Bulk CSV import from Endorser Mobile export |
|||
- Dates on give summaries |
|||
|
|||
## [0.2.10] - 2024.01.18 - 667e1e8890b42de59cd939caca1a01c7a7a702be |
|||
|
|||
### Added in 0.2.10 |
|||
|
|||
- Person identicons for contacts |
|||
- Confirmation & delivery directly from project page |
|||
- Offer dialog now allows units |
|||
- Links from claim detail page to the fulfilled project or offer |
|||
- Link to project from home feed |
|||
- Copy to clipboard in more places |
|||
|
|||
### Fixed in 0.2.10 |
|||
|
|||
- "More Contacts" for give on project page now links correctly. |
|||
|
|||
## [0.2.9] - 2024.01.15 - e5e702f8a5a53a6efbed48d35f0bc3cee63024a0 |
|||
|
|||
### Fixed in 0.2.9 |
|||
|
|||
- Set visibility for new contact. |
|||
|
|||
## [0.2.8] - 2024.01.14 |
|||
|
|||
### Added in 0.2.8 |
|||
|
|||
- Automatic ID creation from home page |
|||
- Agent who can also edit a project |
|||
|
|||
### Fixed in 0.2.8 |
|||
|
|||
- Cannot declare anonymous gift |
|||
|
|||
## [0.2.7] - 2024.01.12 |
|||
|
|||
### Added in 0.2.7 |
|||
|
|||
- Give to fulfill a particular offer |
|||
- Give as part of a trade as opposed to a donation |
|||
- Error notifications on import |
|||
|
|||
### Changed in 0.2.7 |
|||
|
|||
- Library security updates |
|||
- Visibility of actions & confirmations on claim page |
|||
|
|||
### Fixed in 0.2.7 |
|||
|
|||
- Name of offerer |
|||
|
|||
## [0.2.2] - 2024.01.05 |
|||
|
|||
### Added in 0.2.2 |
|||
|
|||
- Check for notification capability on front screen |
|||
- Contact next-public-key-hash in manual textual input |
|||
- Confirmation for contact visibility change |
|||
- YAML rendering of full claim details |
|||
- Hints for onboarding on the contact screen |
|||
|
|||
## [0.2.0] - 2024.01.04 |
|||
|
|||
### Added in 0.2.0 |
|||
|
|||
- Contact next-public-key-hash |
|||
- Icon for Android |
|||
- More thorough messaging and testing for notifications |
|||
|
|||
## [0.1.9] - 2024.01.01 |
|||
|
|||
### Added in 0.1.9 |
|||
|
|||
- Import for contacts and settings |
|||
- Second download button for DuckDuckGo |
|||
|
|||
### Changed in 0.1.9 |
|||
|
|||
- Removed some keys from Dexie's IndexedDB declarations |
|||
|
|||
## [0.1.8] - 2023.12.27- d26d1d360152a7d0e559b68486e85b72b88bd9ff |
|||
|
|||
### Added in 0.1.8 |
|||
|
|||
- DB logging for service-worker events |
|||
- Help page for notifications |
|||
- Test notification & web-push triggers inside app |
|||
- Check that the app is installed |
|||
|
|||
### Fixed in 0.1.8 |
|||
|
|||
- Project issuer display name |
|||
|
|||
## [0.1.7] - 2023.12.19 - 91c6c7c11c71f96006cc876fc946f1f98a274ba2 |
|||
|
|||
### Changed in 0.1.7 |
|||
|
|||
- Icons |
|||
|
|||
### Fixed in 0.1.7 |
|||
|
|||
- Notification switch now shows message |
|||
- Prod/test server warning message at top of page |
|||
|
|||
## [0.1.6] - 2023.12.17 - b445b1234fbfcf6b37d695373f259aab0eda1118 |
|||
|
|||
### Added in 0.1.6 |
|||
|
|||
- Infinite scroll on home page |
|||
|
|||
### Changed in 0.1.6 |
|||
|
|||
- UI improvements |
|||
- Show web-push subscription info |
|||
- Icon |
|||
|
|||
## [0.1.5] - 2023.12.09 - 9c36bb509a9bae9bb3306d3bd9eeb144b67aa8ad |
|||
|
|||
### Added in 0.1.5 |
|||
|
|||
- Web push notifications (though not finalized) |
|||
- Credentials details page |
|||
- See more data without an ID |
|||
- Change units of a give |
|||
|
|||
## [0.1.4] - 2023.11.20 - 7311d36726f3667ec4c68f241f91d404273ad4db |
|||
|
|||
### Added in 0.1.4 |
|||
|
|||
- Offer on a project |
|||
|
|||
### Changed in 0.1.4 |
|||
|
|||
- Automatically set as visible when importing a contact |
|||
|
|||
## [0.1.3] - 2023.11.08 - 910f57ec7d2e50803ae3d04f4b927e0f5219fbde |
|||
|
|||
### Added in 0.1.3 |
|||
|
|||
- Contact name editing |
|||
|
|||
### Changed in 0.1.3 |
|||
|
|||
- Don't show actions on front page if not registered. |
|||
|
|||
### Removed in 0.1.3 |
|||
|
|||
- Home page Notiwind test buttons |
|||
|
|||
## [0.1.2] - 2023.11.01 - 7f6c93802911a030a89fe3706e18b5c17151e5bb |
|||
|
|||
### Added in 0.1.2 |
|||
|
|||
- Basics: create ID, record a give, declare a project, search, and get notifications. |
@ -1,11 +0,0 @@ |
|||
# Contributing |
|||
|
|||
Welcome! We are happy to have your help with this project. |
|||
|
|||
We expect contributions to include automated tests and pass linting. Run the `test-all` task. |
|||
Note that some previous features don't have tests and adding more will make you friends quick. |
|||
|
|||
Note that all contributions will be under our [license, modeled after SQLite](https://github.com/trentlarson/endorser-ch/blob/master/LICENSE). |
|||
|
|||
If you want to see a code of conduct, we're probably not the people you want to hang with. |
|||
Basically, we'll work together as long as we both enjoy it, and we'll stop when that stops. |
@ -1,36 +0,0 @@ |
|||
# Build stage |
|||
FROM node:22-alpine3.20 AS builder |
|||
|
|||
# Install build dependencies |
|||
|
|||
RUN apk add --no-cache bash git python3 py3-pip py3-setuptools make g++ gcc |
|||
|
|||
# Set working directory |
|||
WORKDIR /app |
|||
|
|||
# Copy package files |
|||
COPY package*.json ./ |
|||
|
|||
# Install dependencies |
|||
RUN npm ci |
|||
|
|||
# Copy source code |
|||
COPY . . |
|||
|
|||
# Build the application |
|||
RUN npm run build:web |
|||
|
|||
# Production stage |
|||
FROM nginx:alpine |
|||
|
|||
# Copy built assets from builder stage |
|||
COPY --from=builder /app/dist /usr/share/nginx/html |
|||
|
|||
# Copy nginx configuration if needed |
|||
# COPY nginx.conf /etc/nginx/conf.d/default.conf |
|||
|
|||
# Expose port 80 |
|||
EXPOSE 80 |
|||
|
|||
# Start nginx |
|||
CMD ["nginx", "-g", "daemon off;"] |
@ -1,5 +0,0 @@ |
|||
source "https://rubygems.org" |
|||
|
|||
gem "fastlane" |
|||
gem "cocoapods" |
|||
|
@ -1,321 +0,0 @@ |
|||
GEM |
|||
remote: https://rubygems.org/ |
|||
specs: |
|||
CFPropertyList (3.0.7) |
|||
base64 |
|||
nkf |
|||
rexml |
|||
activesupport (7.2.2.1) |
|||
base64 |
|||
benchmark (>= 0.3) |
|||
bigdecimal |
|||
concurrent-ruby (~> 1.0, >= 1.3.1) |
|||
connection_pool (>= 2.2.5) |
|||
drb |
|||
i18n (>= 1.6, < 2) |
|||
logger (>= 1.4.2) |
|||
minitest (>= 5.1) |
|||
securerandom (>= 0.3) |
|||
tzinfo (~> 2.0, >= 2.0.5) |
|||
addressable (2.8.7) |
|||
public_suffix (>= 2.0.2, < 7.0) |
|||
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) |
|||
claide (1.1.0) |
|||
cocoapods (1.16.2) |
|||
addressable (~> 2.8) |
|||
claide (>= 1.0.2, < 2.0) |
|||
cocoapods-core (= 1.16.2) |
|||
cocoapods-deintegrate (>= 1.0.3, < 2.0) |
|||
cocoapods-downloader (>= 2.1, < 3.0) |
|||
cocoapods-plugins (>= 1.0.0, < 2.0) |
|||
cocoapods-search (>= 1.0.0, < 2.0) |
|||
cocoapods-trunk (>= 1.6.0, < 2.0) |
|||
cocoapods-try (>= 1.1.0, < 2.0) |
|||
colored2 (~> 3.1) |
|||
escape (~> 0.0.4) |
|||
fourflusher (>= 2.3.0, < 3.0) |
|||
gh_inspector (~> 1.0) |
|||
molinillo (~> 0.8.0) |
|||
nap (~> 1.0) |
|||
ruby-macho (>= 2.3.0, < 3.0) |
|||
xcodeproj (>= 1.27.0, < 2.0) |
|||
cocoapods-core (1.16.2) |
|||
activesupport (>= 5.0, < 8) |
|||
addressable (~> 2.8) |
|||
algoliasearch (~> 1.0) |
|||
concurrent-ruby (~> 1.1) |
|||
fuzzy_match (~> 2.0.4) |
|||
nap (~> 1.0) |
|||
netrc (~> 0.11) |
|||
public_suffix (~> 4.0) |
|||
typhoeus (~> 1.0) |
|||
cocoapods-deintegrate (1.0.5) |
|||
cocoapods-downloader (2.1) |
|||
cocoapods-plugins (1.0.0) |
|||
nap |
|||
cocoapods-search (1.0.1) |
|||
cocoapods-trunk (1.6.0) |
|||
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) |
|||
ffi (1.17.1-arm-linux-gnu) |
|||
ffi (1.17.1-arm-linux-musl) |
|||
ffi (1.17.1-arm64-darwin) |
|||
ffi (1.17.1-x86-linux-gnu) |
|||
ffi (1.17.1-x86-linux-musl) |
|||
ffi (1.17.1-x86_64-darwin) |
|||
ffi (1.17.1-x86_64-linux-gnu) |
|||
ffi (1.17.1-x86_64-linux-musl) |
|||
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) |
|||
claide (>= 1.0.2, < 2.0) |
|||
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 |
|||
aarch64-linux-musl |
|||
arm-linux-gnu |
|||
arm-linux-musl |
|||
arm64-darwin |
|||
ruby |
|||
x86-linux-gnu |
|||
x86-linux-musl |
|||
x86_64-darwin |
|||
x86_64-linux-gnu |
|||
x86_64-linux-musl |
|||
|
|||
DEPENDENCIES |
|||
cocoapods |
|||
fastlane |
|||
|
|||
BUNDLED WITH |
|||
2.6.5 |
@ -1,8 +0,0 @@ |
|||
The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: |
|||
|
|||
May you do good and not evil. |
|||
May you find forgiveness for yourself and forgive others. |
|||
May you share freely, never taking more than you give. |
|||
|
|||
________________________________________________________________ |
|||
from https://www.sqlite.org/src/info/689401a6cfb4c234 and memorialized here https://spdx.org/licenses/blessing.html |
@ -1,80 +1,18 @@ |
|||
# TimeSafari.app - Crowd-Funder for Time - PWA |
|||
# Vue 3 + TypeScript + Vite |
|||
|
|||
[Time Safari](https://timesafari.org/) allows people to ease into collaboration: start with expressions of gratitude |
|||
and expand to crowd-fund with time & money, then record and see the impact of contributions. |
|||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. |
|||
|
|||
## Roadmap |
|||
## Recommended IDE Setup |
|||
|
|||
See [project.task.yaml](project.task.yaml) for current priorities. |
|||
(Numbers at the beginning of lines are estimated hours. See [taskyaml.org](https://taskyaml.org/) for details.) |
|||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). |
|||
|
|||
## Setup & Building |
|||
## Type Support For `.vue` Imports in TS |
|||
|
|||
Quick start: |
|||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. |
|||
|
|||
* For setup, we recommend [pkgx](https://pkgx.dev), which installs what you need (either automatically or with the `dev` command). Core dependencies are typescript & npm; when building for other platforms, you'll need other things such as those in the pkgx.yaml & BUILDING.md files. |
|||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: |
|||
|
|||
```bash |
|||
npm install |
|||
npm run dev |
|||
``` |
|||
|
|||
See [BUILDING.md](BUILDING.md) for more details. |
|||
|
|||
|
|||
|
|||
|
|||
## Tests |
|||
|
|||
See [TESTING.md](test-playwright/TESTING.md) for detailed test instructions. |
|||
|
|||
|
|||
|
|||
|
|||
## Icons |
|||
|
|||
Application icons are in the `assets` directory, processed by the `capacitor-assets` command. |
|||
|
|||
To add a Font Awesome icon, add to main.ts and reference with `font-awesome` element and `icon` attribute with the hyphenated name. |
|||
|
|||
## Other |
|||
|
|||
### Reference Material |
|||
|
|||
* Notifications can be type of `toast` (self-dismiss), `info`, `success`, `warning`, and `danger`. |
|||
They are done via [notiwind](https://www.npmjs.com/package/notiwind) and set up in App.vue. |
|||
|
|||
* [Customize Vue configuration](https://cli.vuejs.org/config/). |
|||
|
|||
* If you are deploying in a subdirectory, add it to `publicPath` in vue.config.js, eg: `publicPath: "/app/time-tracker/",` |
|||
|
|||
### Code Organization |
|||
|
|||
The project uses a centralized approach to type definitions and interfaces: |
|||
|
|||
* `src/interfaces/` - Contains all TypeScript interfaces and type definitions |
|||
* `deepLinks.ts` - Deep linking type system and Zod validation schemas |
|||
* `give.ts` - Give-related interfaces and type definitions |
|||
* `claims.ts` - Claim-related interfaces and verifiable credentials |
|||
* `common.ts` - Shared interfaces and utility types |
|||
* Other domain-specific interface files |
|||
|
|||
Key principles: |
|||
- All interfaces and types are defined in the interfaces folder |
|||
- Zod schemas are used for runtime validation and type generation |
|||
- Domain-specific interfaces are separated into their own files |
|||
- Common interfaces are shared through `common.ts` |
|||
- Type definitions are generated from Zod schemas where possible |
|||
|
|||
### Kudos |
|||
|
|||
Gifts make the world go 'round! |
|||
|
|||
* [WebStorm by JetBrains](https://www.jetbrains.com/webstorm/) for the free open-source license |
|||
* [Máximo Fernández](https://medium.com/@maxfarenas) for the 3D [code](https://github.com/maxfer03/vue-three-ns) and [explanatory post](https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80) |
|||
* [Many tools & libraries](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/src/branch/master/package.json#L10) such as Nodejs.org, IntelliJ Idea, Veramo.io, Vuejs.org, threejs.org |
|||
* [Bush 3D model](https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439) |
|||
* [Forest floor image](https://www.goodfreephotos.com/albums/textures/leafy-autumn-forest-floor.jpg) |
|||
* Time Safari logo assisted by [DALL-E in ChatGPT](https://chat.openai.com/g/g-2fkFE8rbu-dall-e) |
|||
* [DiceBear](https://www.dicebear.com/licenses/) and [Avataaars](https://www.dicebear.com/styles/avataaars/#details) for human-looking identicons |
|||
* Some gratitude prompts thanks to [Develop Good Habits](https://www.developgoodhabits.com/gratitude-journal-prompts/) |
|||
1. Disable the built-in TypeScript Extension |
|||
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette |
|||
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` |
|||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. |
|||
|
@ -1,84 +0,0 @@ |
|||
|
|||
# What to do about storage for native apps? |
|||
|
|||
|
|||
## Problem |
|||
|
|||
We can't trust iOS IndexedDB to persist. I want to start delivering an app to people now, in preparation for presentations mid-June: Rotary on June 12 and Porcfest on June 17. |
|||
|
|||
* Apple WebKit puts a [7-day cap on IndexedDB](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/). |
|||
|
|||
* The web standards expose a `persist` method to mark memory as persistent, and [supposedly WebView supports it](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persisted), but too many other things indicate it's not reliable. I've talked with [ChatGPT](https://chatgpt.com/share/68322f40-84c8-8007-b213-855f7962989a) & Venice & Claude (in Cursor); [this answer from Perplexity](https://www.perplexity.ai/search/which-platforms-prompt-the-use-HUQLqy4qQD2cRbkmO4CgHg) says that most platforms don't prompt and Safari doesn't support it; I don't know if that means WebKit as well. |
|||
|
|||
* Capacitor says [not to trust it on iOS](https://capacitorjs.com/docs/v6/guides/storage). |
|||
|
|||
Also, with sensitive data, the accounts info should be encrypted. |
|||
|
|||
|
|||
# Options |
|||
|
|||
* There is a community [SQLite plugin for Capacitor](https://github.com/capacitor-community/sqlite) with encryption by [SQLCipher](https://github.com/sqlcipher/sqlcipher). |
|||
|
|||
* [This tutorial](https://jepiqueau.github.io/2023/09/05/Ionic7Vue-SQLite-CRUD-App.html#part-1---web---table-of-contents) shows how that plugin works for web as well as native. |
|||
|
|||
* Capacitor abstracts [user preferences in an API](https://capacitorjs.com/docs/apis/preferences), which uses different underlying libraries on iOS & Android. Unfortunately, it won't do any filtering or searching, and is only meant for small amounts of data. (It could be used for settings and for identifiers, but contacts will grow and image blobs won't work.) |
|||
|
|||
* There are hints that Capacitor offers another custom storage API but all I could find was that Preferences API. |
|||
|
|||
* [Ionic Storage](https://ionic.io/docs/secure-storage) is an enterprise solution, which also supports encryption. |
|||
|
|||
* Not an option yet: Dexie may support SQLite in [a future version](https://dexie.org/roadmap/dexie5.0). |
|||
|
|||
|
|||
|
|||
# Current Plan |
|||
|
|||
* Implement SQLite for Capacitor & web, with encryption. That will allow us to test quickly and keep the same interface for native & web, but we don't deal with migrations for current web users. |
|||
|
|||
* After that is delivered, write a migration for current web users from IndexedDB to SQLite. |
|||
|
|||
|
|||
|
|||
# Current method calls |
|||
|
|||
... which is not 100% complete because the AI that generated thus claimed no usage of 'temp' DB. |
|||
|
|||
### Secret Database (secretDB) - Used for storing the encryption key |
|||
|
|||
secretDB.open() - Opens the database |
|||
secretDB.secret.get(MASTER_SECRET_KEY) - Retrieves the secret key |
|||
secretDB.secret.add({ id: MASTER_SECRET_KEY, secret }) - Adds a new secret key |
|||
|
|||
### Accounts Database (accountsDB) - Used for storing sensitive account information |
|||
|
|||
accountsDB.open() - Opens the database |
|||
accountsDB.accounts.count() - Counts number of accounts |
|||
accountsDB.accounts.toArray() - Gets all accounts |
|||
accountsDB.accounts.where("did").equals(did).first() - Gets a specific account by DID |
|||
accountsDB.accounts.add(account) - Adds a new account |
|||
|
|||
### Non-sensitive Database (db) - Used for settings, contacts, logs, and temp data |
|||
|
|||
Settings operations: |
|||
export all settings (Dexie format) |
|||
db.settings.get(MASTER_SETTINGS_KEY) - Gets default settings |
|||
db.settings.where("accountDid").equals(did).first() - Gets account-specific settings |
|||
db.settings.where("accountDid").equals(did).modify(settingsChanges) - Updates account settings |
|||
db.settings.add(settingsChanges) - Adds new settings |
|||
db.settings.count() - Counts number of settings |
|||
db.settings.update(key, changes) - Updates settings |
|||
|
|||
Contacts operations: |
|||
export all contacts (Dexie format) |
|||
db.contacts.toArray() - Gets all contacts |
|||
db.contacts.add(contact) - Adds a new contact |
|||
db.contacts.update(did, contactData) - Updates a contact |
|||
db.contacts.delete(did) - Deletes a contact |
|||
db.contacts.where("did").equals(did).first() - Gets a specific contact by DID |
|||
|
|||
Logs operations: |
|||
db.logs.get(todayKey) - Gets logs for a specific day |
|||
db.logs.update(todayKey, { message: fullMessage }) - Updates logs |
|||
db.logs.clear() - Clears all logs |
|||
|
|||
|
@ -1,108 +0,0 @@ |
|||
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore |
|||
|
|||
app/build/* |
|||
!app/build/.npmkeep |
|||
|
|||
# Copied web assets |
|||
app/src/main/assets/public |
|||
|
|||
# Generated Config files |
|||
app/src/main/assets/capacitor.config.json |
|||
app/src/main/assets/capacitor.plugins.json |
|||
app/src/main/res/xml/config.xml |
|||
|
|||
# secrets |
|||
app/gradle.properties.secrets |
|||
app/time-safari-upload-key-pkcs12.jks |
|||
|
|||
# Built application files |
|||
*.apk |
|||
*.aar |
|||
*.ap_ |
|||
*.aab |
|||
|
|||
# Files for the ART/Dalvik VM |
|||
*.dex |
|||
|
|||
# Java class files |
|||
*.class |
|||
|
|||
# Generated files |
|||
bin/ |
|||
gen/ |
|||
out/ |
|||
# Uncomment the following line in case you need and you don't have the release build type files in your app |
|||
# release/ |
|||
|
|||
# Gradle files |
|||
.gradle/ |
|||
build/ |
|||
|
|||
# Local configuration file (sdk path, etc) |
|||
local.properties |
|||
|
|||
# Proguard folder generated by Eclipse |
|||
proguard/ |
|||
|
|||
# Log Files |
|||
*.log |
|||
|
|||
# Android Studio Navigation editor temp files |
|||
.navigation/ |
|||
|
|||
# Android Studio captures folder |
|||
captures/ |
|||
|
|||
# IntelliJ |
|||
*.iml |
|||
.idea/workspace.xml |
|||
.idea/tasks.xml |
|||
.idea/gradle.xml |
|||
.idea/assetWizardSettings.xml |
|||
.idea/dictionaries |
|||
.idea/libraries |
|||
# Android Studio 3 in .gitignore file. |
|||
.idea/caches |
|||
.idea/modules.xml |
|||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you |
|||
.idea/navEditor.xml |
|||
|
|||
# Keystore files |
|||
# Uncomment the following lines if you do not want to check your keystore files in. |
|||
#*.jks |
|||
#*.keystore |
|||
|
|||
# External native build folder generated in Android Studio 2.2 and later |
|||
.externalNativeBuild |
|||
.cxx/ |
|||
|
|||
# Google Services (e.g. APIs or Firebase) |
|||
# google-services.json |
|||
|
|||
# Freeline |
|||
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 |
|||
|
|||
# lint |
|||
lint/intermediates/ |
|||
lint/generated/ |
|||
lint/outputs/ |
|||
lint/tmp/ |
|||
# lint/reports/ |
|||
|
|||
# Android Profiling |
|||
*.hprof |
|||
|
|||
# Cordova plugins for Capacitor |
|||
capacitor-cordova-android-plugins |
@ -1,111 +0,0 @@ |
|||
apply plugin: 'com.android.application' |
|||
|
|||
// These are sample values to set in gradle.properties.secrets |
|||
// MY_KEYSTORE_FILE=time-safari-upload-key-pkcs12.jks |
|||
// MY_KEYSTORE_PASSWORD=... |
|||
// MY_KEY_ALIAS=time-safari-key-alias |
|||
// MY_KEY_PASSWORD=... |
|||
|
|||
// Try to load from environment variables first |
|||
project.ext.MY_KEYSTORE_FILE = System.getenv('ANDROID_KEYSTORE_FILE') ?: "" |
|||
project.ext.MY_KEYSTORE_PASSWORD = System.getenv('ANDROID_KEYSTORE_PASSWORD') ?: "" |
|||
project.ext.MY_KEY_ALIAS = System.getenv('ANDROID_KEY_ALIAS') ?: "" |
|||
project.ext.MY_KEY_PASSWORD = System.getenv('ANDROID_KEY_PASSWORD') ?: "" |
|||
|
|||
// If no environment variables, try to load from secrets file |
|||
if (!project.ext.MY_KEYSTORE_FILE) { |
|||
def secretsPropertiesFile = rootProject.file("app/gradle.properties.secrets") |
|||
if (secretsPropertiesFile.exists()) { |
|||
Properties secretsProperties = new Properties() |
|||
secretsProperties.load(new FileInputStream(secretsPropertiesFile)) |
|||
secretsProperties.each { name, value -> |
|||
project.ext[name] = value |
|||
} |
|||
} |
|||
} |
|||
|
|||
android { |
|||
namespace 'app.timesafari' |
|||
compileSdk rootProject.ext.compileSdkVersion |
|||
defaultConfig { |
|||
applicationId "app.timesafari.app" |
|||
minSdkVersion rootProject.ext.minSdkVersion |
|||
targetSdkVersion rootProject.ext.targetSdkVersion |
|||
versionCode 34 |
|||
versionName "0.5.8" |
|||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
|||
aaptOptions { |
|||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. |
|||
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 |
|||
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' |
|||
} |
|||
} |
|||
signingConfigs { |
|||
release { |
|||
if (project.ext.MY_KEYSTORE_FILE && |
|||
project.ext.MY_KEYSTORE_PASSWORD && |
|||
project.ext.MY_KEY_ALIAS && |
|||
project.ext.MY_KEY_PASSWORD) { |
|||
|
|||
storeFile file(project.ext.MY_KEYSTORE_FILE) |
|||
storePassword project.ext.MY_KEYSTORE_PASSWORD |
|||
keyAlias project.ext.MY_KEY_ALIAS |
|||
keyPassword project.ext.MY_KEY_PASSWORD |
|||
} |
|||
} |
|||
} |
|||
buildTypes { |
|||
release { |
|||
minifyEnabled false |
|||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' |
|||
// Only sign if we have the signing config |
|||
if (signingConfigs.release.storeFile != null) { |
|||
signingConfig signingConfigs.release |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Enable bundle builds (without which it doesn't work right for bundleDebug vs bundleRelease) |
|||
bundle { |
|||
language { |
|||
enableSplit = true |
|||
} |
|||
density { |
|||
enableSplit = true |
|||
} |
|||
abi { |
|||
enableSplit = true |
|||
} |
|||
} |
|||
} |
|||
|
|||
repositories { |
|||
flatDir{ |
|||
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' |
|||
} |
|||
} |
|||
|
|||
dependencies { |
|||
implementation fileTree(include: ['*.jar'], dir: 'libs') |
|||
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" |
|||
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" |
|||
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" |
|||
implementation project(':capacitor-android') |
|||
implementation project(':capacitor-community-sqlite') |
|||
implementation "androidx.biometric:biometric:1.2.0-alpha05" |
|||
testImplementation "junit:junit:$junitVersion" |
|||
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" |
|||
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" |
|||
implementation project(':capacitor-cordova-android-plugins') |
|||
} |
|||
|
|||
apply from: 'capacitor.build.gradle' |
|||
|
|||
try { |
|||
def servicesJSON = file('google-services.json') |
|||
if (servicesJSON.text) { |
|||
apply plugin: 'com.google.gms.google-services' |
|||
} |
|||
} catch(Exception e) { |
|||
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") |
|||
} |
@ -1,25 +0,0 @@ |
|||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN |
|||
|
|||
android { |
|||
compileOptions { |
|||
sourceCompatibility JavaVersion.VERSION_17 |
|||
targetCompatibility JavaVersion.VERSION_17 |
|||
} |
|||
} |
|||
|
|||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" |
|||
dependencies { |
|||
implementation project(':capacitor-community-sqlite') |
|||
implementation project(':capacitor-mlkit-barcode-scanning') |
|||
implementation project(':capacitor-app') |
|||
implementation project(':capacitor-camera') |
|||
implementation project(':capacitor-filesystem') |
|||
implementation project(':capacitor-share') |
|||
implementation project(':capawesome-capacitor-file-picker') |
|||
|
|||
} |
|||
|
|||
|
|||
if (hasProperty('postBuildExtras')) { |
|||
postBuildExtras() |
|||
} |
@ -1,28 +0,0 @@ |
|||
{ |
|||
"project_info": { |
|||
"project_number": "123456789000", |
|||
"project_id": "timesafari-app", |
|||
"storage_bucket": "timesafari-app.appspot.com" |
|||
}, |
|||
"client": [ |
|||
{ |
|||
"client_info": { |
|||
"mobilesdk_app_id": "1:123456789000:android:1234567890abcdef", |
|||
"android_client_info": { |
|||
"package_name": "app.timesafari.app" |
|||
} |
|||
}, |
|||
"oauth_client": [], |
|||
"api_key": [ |
|||
{ |
|||
"current_key": "AIzaSyDummyKeyForBuildPurposesOnly12345" |
|||
} |
|||
], |
|||
"services": { |
|||
"appinvite_service": { |
|||
"other_platform_oauth_client": [] |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
@ -1,21 +0,0 @@ |
|||
# Add project specific ProGuard rules here. |
|||
# You can control the set of applied configuration files using the |
|||
# proguardFiles setting in build.gradle. |
|||
# |
|||
# For more details, see |
|||
# http://developer.android.com/guide/developing/tools/proguard.html |
|||
|
|||
# If your project uses WebView with JS, uncomment the following |
|||
# and specify the fully qualified class name to the JavaScript interface |
|||
# class: |
|||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
|||
# public *; |
|||
#} |
|||
|
|||
# Uncomment this to preserve the line number information for |
|||
# debugging stack traces. |
|||
#-keepattributes SourceFile,LineNumberTable |
|||
|
|||
# If you keep the line number information, uncomment this to |
|||
# hide the original source file name. |
|||
#-renamesourcefileattribute SourceFile |
@ -1,26 +0,0 @@ |
|||
package com.getcapacitor.myapp; |
|||
|
|||
import static org.junit.Assert.*; |
|||
|
|||
import android.content.Context; |
|||
import androidx.test.ext.junit.runners.AndroidJUnit4; |
|||
import androidx.test.platform.app.InstrumentationRegistry; |
|||
import org.junit.Test; |
|||
import org.junit.runner.RunWith; |
|||
|
|||
/** |
|||
* Instrumented test, which will execute on an Android device. |
|||
* |
|||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
|||
*/ |
|||
@RunWith(AndroidJUnit4.class) |
|||
public class ExampleInstrumentedTest { |
|||
|
|||
@Test |
|||
public void useAppContext() throws Exception { |
|||
// Context of the app under test.
|
|||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); |
|||
|
|||
assertEquals("app.timesafari.app", appContext.getPackageName()); |
|||
} |
|||
} |
@ -1,46 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
<application |
|||
android:allowBackup="true" |
|||
android:icon="@mipmap/ic_launcher" |
|||
android:label="@string/app_name" |
|||
android:roundIcon="@mipmap/ic_launcher_round" |
|||
android:supportsRtl="true" |
|||
android:theme="@style/AppTheme"> |
|||
<activity |
|||
android:name=".MainActivity" |
|||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" |
|||
android:exported="true" |
|||
android:label="@string/title_activity_main" |
|||
android:launchMode="singleTask" |
|||
android:theme="@style/AppTheme.NoActionBarLaunch"> |
|||
<intent-filter> |
|||
<action android:name="android.intent.action.MAIN" /> |
|||
<category android:name="android.intent.category.LAUNCHER" /> |
|||
</intent-filter> |
|||
|
|||
<intent-filter> |
|||
<action android:name="android.intent.action.VIEW" /> |
|||
<category android:name="android.intent.category.DEFAULT" /> |
|||
<category android:name="android.intent.category.BROWSABLE" /> |
|||
<data android:scheme="timesafari" /> |
|||
</intent-filter> |
|||
</activity> |
|||
|
|||
<provider |
|||
android:name="androidx.core.content.FileProvider" |
|||
android:authorities="${applicationId}.fileprovider" |
|||
android:exported="false" |
|||
android:grantUriPermissions="true"> |
|||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> |
|||
</provider> |
|||
</application> |
|||
|
|||
<!-- Permissions --> |
|||
|
|||
<uses-permission android:name="android.permission.INTERNET" /> |
|||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
|||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
|||
<uses-permission android:name="android.permission.CAMERA" /> |
|||
<uses-feature android:name="android.hardware.camera" android:required="true" /> |
|||
</manifest> |
@ -1,56 +0,0 @@ |
|||
{ |
|||
"appId": "app.timesafari", |
|||
"appName": "TimeSafari", |
|||
"webDir": "dist", |
|||
"bundledWebRuntime": false, |
|||
"server": { |
|||
"cleartext": true |
|||
}, |
|||
"plugins": { |
|||
"App": { |
|||
"appUrlOpen": { |
|||
"handlers": [ |
|||
{ |
|||
"url": "timesafari://*", |
|||
"autoVerify": true |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
"SQLite": { |
|||
"iosDatabaseLocation": "Library/CapacitorDatabase", |
|||
"iosIsEncryption": true, |
|||
"iosBiometric": { |
|||
"biometricAuth": true, |
|||
"biometricTitle": "Biometric login for TimeSafari" |
|||
}, |
|||
"androidIsEncryption": true, |
|||
"androidBiometric": { |
|||
"biometricAuth": true, |
|||
"biometricTitle": "Biometric login for TimeSafari" |
|||
} |
|||
} |
|||
}, |
|||
"ios": { |
|||
"contentInset": "never", |
|||
"allowsLinkPreview": true, |
|||
"scrollEnabled": true, |
|||
"limitsNavigationsToAppBoundDomains": true, |
|||
"backgroundColor": "#ffffff", |
|||
"allowNavigation": [ |
|||
"*.timesafari.app", |
|||
"*.jsdelivr.net", |
|||
"api.endorser.ch" |
|||
] |
|||
}, |
|||
"android": { |
|||
"allowMixedContent": false, |
|||
"captureInput": true, |
|||
"webContentsDebuggingEnabled": false, |
|||
"allowNavigation": [ |
|||
"*.timesafari.app", |
|||
"*.jsdelivr.net", |
|||
"api.endorser.ch" |
|||
] |
|||
} |
|||
} |
@ -1,30 +0,0 @@ |
|||
[ |
|||
{ |
|||
"pkg": "@capacitor-community/sqlite", |
|||
"classpath": "com.getcapacitor.community.database.sqlite.CapacitorSQLitePlugin" |
|||
}, |
|||
{ |
|||
"pkg": "@capacitor-mlkit/barcode-scanning", |
|||
"classpath": "io.capawesome.capacitorjs.plugins.mlkit.barcodescanning.BarcodeScannerPlugin" |
|||
}, |
|||
{ |
|||
"pkg": "@capacitor/app", |
|||
"classpath": "com.capacitorjs.plugins.app.AppPlugin" |
|||
}, |
|||
{ |
|||
"pkg": "@capacitor/camera", |
|||
"classpath": "com.capacitorjs.plugins.camera.CameraPlugin" |
|||
}, |
|||
{ |
|||
"pkg": "@capacitor/filesystem", |
|||
"classpath": "com.capacitorjs.plugins.filesystem.FilesystemPlugin" |
|||
}, |
|||
{ |
|||
"pkg": "@capacitor/share", |
|||
"classpath": "com.capacitorjs.plugins.share.SharePlugin" |
|||
}, |
|||
{ |
|||
"pkg": "@capawesome/capacitor-file-picker", |
|||
"classpath": "io.capawesome.capacitorjs.plugins.filepicker.FilePickerPlugin" |
|||
} |
|||
] |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 270 KiB |
Before Width: | Height: | Size: 332 KiB |
Before Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 463 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 705 KiB |
@ -1,11 +0,0 @@ |
|||
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/) |
@ -1,229 +0,0 @@ |
|||
{ |
|||
"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 |
|||
} |
|||
] |
|||
} |
Before Width: | Height: | Size: 3.6 MiB |
Before Width: | Height: | Size: 4.7 MiB |
@ -1,2 +0,0 @@ |
|||
User-agent: * |
|||
Disallow: |
@ -1,15 +0,0 @@ |
|||
package app.timesafari; |
|||
|
|||
import android.os.Bundle; |
|||
import com.getcapacitor.BridgeActivity; |
|||
//import com.getcapacitor.community.sqlite.SQLite;
|
|||
|
|||
public class MainActivity extends BridgeActivity { |
|||
@Override |
|||
public void onCreate(Bundle savedInstanceState) { |
|||
super.onCreate(savedInstanceState); |
|||
|
|||
// Initialize SQLite
|
|||
//registerPlugin(SQLite.class);
|
|||
} |
|||
} |
@ -1,34 +0,0 @@ |
|||
<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> |
@ -1,170 +0,0 @@ |
|||
<?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> |
@ -1,12 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
tools:context=".MainActivity"> |
|||
|
|||
<WebView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" /> |
|||
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
@ -1,9 +0,0 @@ |
|||
<?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> |
@ -1,9 +0,0 @@ |
|||
<?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> |
@ -1,4 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<resources> |
|||
<color name="ic_launcher_background">#FFFFFF</color> |
|||
</resources> |
@ -1,7 +0,0 @@ |
|||
<?xml version='1.0' encoding='utf-8'?> |
|||
<resources> |
|||
<string name="app_name">TimeSafari</string> |
|||
<string name="title_activity_main">TimeSafari</string> |
|||
<string name="package_name">timesafari.app</string> |
|||
<string name="custom_url_scheme">timesafari.app</string> |
|||
</resources> |
@ -1,22 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<resources> |
|||
|
|||
<!-- Base application theme. --> |
|||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> |
|||
<!-- Customize your theme here. --> |
|||
<item name="colorPrimary">@color/colorPrimary</item> |
|||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> |
|||
<item name="colorAccent">@color/colorAccent</item> |
|||
</style> |
|||
|
|||
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar"> |
|||
<item name="windowActionBar">false</item> |
|||
<item name="windowNoTitle">true</item> |
|||
<item name="android:background">@null</item> |
|||
</style> |
|||
|
|||
|
|||
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen"> |
|||
<item name="android:background">@drawable/splash</item> |
|||
</style> |
|||
</resources> |
@ -1,6 +0,0 @@ |
|||
<?xml version='1.0' encoding='utf-8'?> |
|||
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> |
|||
<access origin="*" /> |
|||
|
|||
|
|||
</widget> |
@ -1,6 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<paths xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
<external-path name="my_images" path="." /> |
|||
<cache-path name="my_cache_images" path="." /> |
|||
<files-path name="my_files" path="." /> |
|||
</paths> |
@ -1,18 +0,0 @@ |
|||
package com.getcapacitor.myapp; |
|||
|
|||
import static org.junit.Assert.*; |
|||
|
|||
import org.junit.Test; |
|||
|
|||
/** |
|||
* Example local unit test, which will execute on the development machine (host). |
|||
* |
|||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
|||
*/ |
|||
public class ExampleUnitTest { |
|||
|
|||
@Test |
|||
public void addition_isCorrect() throws Exception { |
|||
assertEquals(4, 2 + 2); |
|||
} |
|||
} |
@ -1,29 +0,0 @@ |
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules. |
|||
|
|||
buildscript { |
|||
|
|||
repositories { |
|||
google() |
|||
mavenCentral() |
|||
} |
|||
dependencies { |
|||
classpath 'com.android.tools.build:gradle:8.9.1' |
|||
classpath 'com.google.gms:google-services:4.4.0' |
|||
|
|||
// NOTE: Do not place your application dependencies here; they belong |
|||
// in the individual module build.gradle files |
|||
} |
|||
} |
|||
|
|||
apply from: "variables.gradle" |
|||
|
|||
allprojects { |
|||
repositories { |
|||
google() |
|||
mavenCentral() |
|||
} |
|||
} |
|||
|
|||
task clean(type: Delete) { |
|||
delete rootProject.buildDir |
|||
} |
@ -1,59 +0,0 @@ |
|||
ext { |
|||
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1' |
|||
cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '10.1.1' |
|||
} |
|||
|
|||
buildscript { |
|||
repositories { |
|||
google() |
|||
mavenCentral() |
|||
} |
|||
dependencies { |
|||
classpath 'com.android.tools.build:gradle:8.2.1' |
|||
} |
|||
} |
|||
|
|||
apply plugin: 'com.android.library' |
|||
|
|||
android { |
|||
namespace "capacitor.cordova.android.plugins" |
|||
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34 |
|||
defaultConfig { |
|||
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 |
|||
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34 |
|||
versionCode 1 |
|||
versionName "1.0" |
|||
} |
|||
lintOptions { |
|||
abortOnError false |
|||
} |
|||
compileOptions { |
|||
sourceCompatibility JavaVersion.VERSION_17 |
|||
targetCompatibility JavaVersion.VERSION_17 |
|||
} |
|||
} |
|||
|
|||
repositories { |
|||
google() |
|||
mavenCentral() |
|||
flatDir{ |
|||
dirs 'src/main/libs', 'libs' |
|||
} |
|||
} |
|||
|
|||
dependencies { |
|||
implementation fileTree(dir: 'src/main/libs', include: ['*.jar']) |
|||
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" |
|||
implementation "org.apache.cordova:framework:$cordovaAndroidVersion" |
|||
// SUB-PROJECT DEPENDENCIES START |
|||
|
|||
// SUB-PROJECT DEPENDENCIES END |
|||
} |
|||
|
|||
// PLUGIN GRADLE EXTENSIONS START |
|||
apply from: "cordova.variables.gradle" |
|||
// PLUGIN GRADLE EXTENSIONS END |
|||
|
|||
for (def func : cdvPluginPostBuildExtras) { |
|||
func() |
|||
} |
@ -1,7 +0,0 @@ |
|||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN |
|||
ext { |
|||
cdvMinSdkVersion = project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 |
|||
// Plugin gradle extensions can append to this to have code run at the end. |
|||
cdvPluginPostBuildExtras = [] |
|||
cordovaConfig = [:] |
|||
} |
@ -1,8 +0,0 @@ |
|||
<?xml version='1.0' encoding='utf-8'?> |
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:amazon="http://schemas.amazon.com/apk/res/android"> |
|||
<application android:usesCleartextTraffic="true"> |
|||
|
|||
</application> |
|||
|
|||
</manifest> |
@ -1 +0,0 @@ |
|||
|
@ -1,24 +0,0 @@ |
|||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN |
|||
include ':capacitor-android' |
|||
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') |
|||
|
|||
include ':capacitor-community-sqlite' |
|||
project(':capacitor-community-sqlite').projectDir = new File('../node_modules/@capacitor-community/sqlite/android') |
|||
|
|||
include ':capacitor-mlkit-barcode-scanning' |
|||
project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../node_modules/@capacitor-mlkit/barcode-scanning/android') |
|||
|
|||
include ':capacitor-app' |
|||
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') |
|||
|
|||
include ':capacitor-camera' |
|||
project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android') |
|||
|
|||
include ':capacitor-filesystem' |
|||
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') |
|||
|
|||
include ':capacitor-share' |
|||
project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android') |
|||
|
|||
include ':capawesome-capacitor-file-picker' |
|||
project(':capawesome-capacitor-file-picker').projectDir = new File('../node_modules/@capawesome/capacitor-file-picker/android') |
@ -1,23 +0,0 @@ |
|||
# Project-wide Gradle settings. |
|||
|
|||
# IDE (e.g. Android Studio) users: |
|||
# Gradle settings configured through the IDE *will override* |
|||
# any settings specified in this file. |
|||
|
|||
# For more details on how to configure your build environment visit |
|||
# http://www.gradle.org/docs/current/userguide/build_environment.html |
|||
|
|||
# Specifies the JVM arguments used for the daemon process. |
|||
# The setting is particularly useful for tweaking memory settings. |
|||
org.gradle.jvmargs=-Xmx1536m |
|||
|
|||
# When configured, Gradle will run in incubating parallel mode. |
|||
# This option should only be used with decoupled projects. More details, visit |
|||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects |
|||
# org.gradle.parallel=true |
|||
|
|||
# AndroidX package structure to make it clearer which packages are bundled with the |
|||
# 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=34 |
@ -1,7 +0,0 @@ |
|||
distributionBase=GRADLE_USER_HOME |
|||
distributionPath=wrapper/dists |
|||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip |
|||
networkTimeout=10000 |
|||
validateDistributionUrl=true |
|||
zipStoreBase=GRADLE_USER_HOME |
|||
zipStorePath=wrapper/dists |
@ -1,248 +0,0 @@ |
|||
#!/bin/sh |
|||
|
|||
# |
|||
# Copyright © 2015-2021 the original authors. |
|||
# |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|||
# you may not use this file except in compliance with the License. |
|||
# You may obtain a copy of the License at |
|||
# |
|||
# https://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, |
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
# See the License for the specific language governing permissions and |
|||
# limitations under the License. |
|||
# |
|||
|
|||
############################################################################## |
|||
# |
|||
# Gradle start up script for POSIX generated by Gradle. |
|||
# |
|||
# Important for running: |
|||
# |
|||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is |
|||
# noncompliant, but you have some other compliant shell such as ksh or |
|||
# bash, then to run this script, type that shell name before the whole |
|||
# command line, like: |
|||
# |
|||
# ksh Gradle |
|||
# |
|||
# Busybox and similar reduced shells will NOT work, because this script |
|||
# requires all of these POSIX shell features: |
|||
# * functions; |
|||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», |
|||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»; |
|||
# * compound commands having a testable exit status, especially «case»; |
|||
# * various built-in commands including «command», «set», and «ulimit». |
|||
# |
|||
# Important for patching: |
|||
# |
|||
# (2) This script targets any POSIX shell, so it avoids extensions provided |
|||
# by Bash, Ksh, etc; in particular arrays are avoided. |
|||
# |
|||
# The "traditional" practice of packing multiple parameters into a |
|||
# space-separated string is a well documented source of bugs and security |
|||
# problems, so this is (mostly) avoided, by progressively accumulating |
|||
# options in "$@", and eventually passing that to Java. |
|||
# |
|||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, |
|||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; |
|||
# see the in-line comments for details. |
|||
# |
|||
# There are tweaks for specific operating systems such as AIX, CygWin, |
|||
# Darwin, MinGW, and NonStop. |
|||
# |
|||
# (3) This script is generated from the Groovy template |
|||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt |
|||
# within the Gradle project. |
|||
# |
|||
# You can find Gradle at https://github.com/gradle/gradle/. |
|||
# |
|||
############################################################################## |
|||
|
|||
# Attempt to set APP_HOME |
|||
|
|||
# Resolve links: $0 may be a link |
|||
app_path=$0 |
|||
|
|||
# Need this for daisy-chained symlinks. |
|||
while |
|||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path |
|||
[ -h "$app_path" ] |
|||
do |
|||
ls=$( ls -ld "$app_path" ) |
|||
link=${ls#*' -> '} |
|||
case $link in #( |
|||
/*) app_path=$link ;; #( |
|||
*) app_path=$APP_HOME$link ;; |
|||
esac |
|||
done |
|||
|
|||
# This is normally unused |
|||
# shellcheck disable=SC2034 |
|||
APP_BASE_NAME=${0##*/} |
|||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit |
|||
|
|||
# Use the maximum available, or set MAX_FD != -1 to use that value. |
|||
MAX_FD=maximum |
|||
|
|||
warn () { |
|||
echo "$*" |
|||
} >&2 |
|||
|
|||
die () { |
|||
echo |
|||
echo "$*" |
|||
echo |
|||
exit 1 |
|||
} >&2 |
|||
|
|||
# OS specific support (must be 'true' or 'false'). |
|||
cygwin=false |
|||
msys=false |
|||
darwin=false |
|||
nonstop=false |
|||
case "$( uname )" in #( |
|||
CYGWIN* ) cygwin=true ;; #( |
|||
Darwin* ) darwin=true ;; #( |
|||
MSYS* | MINGW* ) msys=true ;; #( |
|||
NONSTOP* ) nonstop=true ;; |
|||
esac |
|||
|
|||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |
|||
|
|||
|
|||
# Determine the Java command to use to start the JVM. |
|||
if [ -n "$JAVA_HOME" ] ; then |
|||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
|||
# IBM's JDK on AIX uses strange locations for the executables |
|||
JAVACMD=$JAVA_HOME/jre/sh/java |
|||
else |
|||
JAVACMD=$JAVA_HOME/bin/java |
|||
fi |
|||
if [ ! -x "$JAVACMD" ] ; then |
|||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME |
|||
|
|||
Please set the JAVA_HOME variable in your environment to match the |
|||
location of your Java installation." |
|||
fi |
|||
else |
|||
JAVACMD=java |
|||
if ! command -v java >/dev/null 2>&1 |
|||
then |
|||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
|||
|
|||
Please set the JAVA_HOME variable in your environment to match the |
|||
location of your Java installation." |
|||
fi |
|||
fi |
|||
|
|||
# Increase the maximum file descriptors if we can. |
|||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then |
|||
case $MAX_FD in #( |
|||
max*) |
|||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. |
|||
# shellcheck disable=SC3045 |
|||
MAX_FD=$( ulimit -H -n ) || |
|||
warn "Could not query maximum file descriptor limit" |
|||
esac |
|||
case $MAX_FD in #( |
|||
'' | soft) :;; #( |
|||
*) |
|||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. |
|||
# shellcheck disable=SC3045 |
|||
ulimit -n "$MAX_FD" || |
|||
warn "Could not set maximum file descriptor limit to $MAX_FD" |
|||
esac |
|||
fi |
|||
|
|||
# Collect all arguments for the java command, stacking in reverse order: |
|||
# * args from the command line |
|||
# * the main class name |
|||
# * -classpath |
|||
# * -D...appname settings |
|||
# * --module-path (only if needed) |
|||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. |
|||
|
|||
# For Cygwin or MSYS, switch paths to Windows format before running java |
|||
if "$cygwin" || "$msys" ; then |
|||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) |
|||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) |
|||
|
|||
JAVACMD=$( cygpath --unix "$JAVACMD" ) |
|||
|
|||
# Now convert the arguments - kludge to limit ourselves to /bin/sh |
|||
for arg do |
|||
if |
|||
case $arg in #( |
|||
-*) false ;; # don't mess with options #( |
|||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath |
|||
[ -e "$t" ] ;; #( |
|||
*) false ;; |
|||
esac |
|||
then |
|||
arg=$( cygpath --path --ignore --mixed "$arg" ) |
|||
fi |
|||
# Roll the args list around exactly as many times as the number of |
|||
# args, so each arg winds up back in the position where it started, but |
|||
# possibly modified. |
|||
# |
|||
# NB: a `for` loop captures its iteration list before it begins, so |
|||
# changing the positional parameters here affects neither the number of |
|||
# iterations, nor the values presented in `arg`. |
|||
shift # remove old arg |
|||
set -- "$@" "$arg" # push replacement arg |
|||
done |
|||
fi |
|||
|
|||
|
|||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
|||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' |
|||
|
|||
# Collect all arguments for the java command; |
|||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of |
|||
# shell script including quotes and variable substitutions, so put them in |
|||
# double quotes to make sure that they get re-expanded; and |
|||
# * put everything else in single quotes, so that it's not re-expanded. |
|||
|
|||
set -- \ |
|||
"-Dorg.gradle.appname=$APP_BASE_NAME" \ |
|||
-classpath "$CLASSPATH" \ |
|||
org.gradle.wrapper.GradleWrapperMain \ |
|||
"$@" |
|||
|
|||
# Stop when "xargs" is not available. |
|||
if ! command -v xargs >/dev/null 2>&1 |
|||
then |
|||
die "xargs is not available" |
|||
fi |
|||
|
|||
# Use "xargs" to parse quoted args. |
|||
# |
|||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. |
|||
# |
|||
# In Bash we could simply go: |
|||
# |
|||
# readarray ARGS < <( xargs -n1 <<<"$var" ) && |
|||
# set -- "${ARGS[@]}" "$@" |
|||
# |
|||
# but POSIX shell has neither arrays nor command substitution, so instead we |
|||
# post-process each arg (as a line of input to sed) to backslash-escape any |
|||
# character that might be a shell metacharacter, then use eval to reverse |
|||
# that process (while maintaining the separation between arguments), and wrap |
|||
# the whole thing up as a single "set" statement. |
|||
# |
|||
# This will of course break if any of these variables contains a newline or |
|||
# an unmatched quote. |
|||
# |
|||
|
|||
eval "set -- $( |
|||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | |
|||
xargs -n1 | |
|||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | |
|||
tr '\n' ' ' |
|||
)" '"$@"' |
|||
|
|||
exec "$JAVACMD" "$@" |
@ -1,92 +0,0 @@ |
|||
@rem |
|||
@rem Copyright 2015 the original author or authors. |
|||
@rem |
|||
@rem Licensed under the Apache License, Version 2.0 (the "License"); |
|||
@rem you may not use this file except in compliance with the License. |
|||
@rem You may obtain a copy of the License at |
|||
@rem |
|||
@rem https://www.apache.org/licenses/LICENSE-2.0 |
|||
@rem |
|||
@rem Unless required by applicable law or agreed to in writing, software |
|||
@rem distributed under the License is distributed on an "AS IS" BASIS, |
|||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
@rem See the License for the specific language governing permissions and |
|||
@rem limitations under the License. |
|||
@rem |
|||
|
|||
@if "%DEBUG%"=="" @echo off |
|||
@rem ########################################################################## |
|||
@rem |
|||
@rem Gradle startup script for Windows |
|||
@rem |
|||
@rem ########################################################################## |
|||
|
|||
@rem Set local scope for the variables with windows NT shell |
|||
if "%OS%"=="Windows_NT" setlocal |
|||
|
|||
set DIRNAME=%~dp0 |
|||
if "%DIRNAME%"=="" set DIRNAME=. |
|||
@rem This is normally unused |
|||
set APP_BASE_NAME=%~n0 |
|||
set APP_HOME=%DIRNAME% |
|||
|
|||
@rem Resolve any "." and ".." in APP_HOME to make it shorter. |
|||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi |
|||
|
|||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
|||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" |
|||
|
|||
@rem Find java.exe |
|||
if defined JAVA_HOME goto findJavaFromJavaHome |
|||
|
|||
set JAVA_EXE=java.exe |
|||
%JAVA_EXE% -version >NUL 2>&1 |
|||
if %ERRORLEVEL% equ 0 goto execute |
|||
|
|||
echo. |
|||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
|||
echo. |
|||
echo Please set the JAVA_HOME variable in your environment to match the |
|||
echo location of your Java installation. |
|||
|
|||
goto fail |
|||
|
|||
:findJavaFromJavaHome |
|||
set JAVA_HOME=%JAVA_HOME:"=% |
|||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe |
|||
|
|||
if exist "%JAVA_EXE%" goto execute |
|||
|
|||
echo. |
|||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% |
|||
echo. |
|||
echo Please set the JAVA_HOME variable in your environment to match the |
|||
echo location of your Java installation. |
|||
|
|||
goto fail |
|||
|
|||
:execute |
|||
@rem Setup the command line |
|||
|
|||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |
|||
|
|||
|
|||
@rem Execute Gradle |
|||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* |
|||
|
|||
:end |
|||
@rem End local scope for the variables with windows NT shell |
|||
if %ERRORLEVEL% equ 0 goto mainEnd |
|||
|
|||
:fail |
|||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of |
|||
rem the _cmd.exe /c_ return code! |
|||
set EXIT_CODE=%ERRORLEVEL% |
|||
if %EXIT_CODE% equ 0 set EXIT_CODE=1 |
|||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% |
|||
exit /b %EXIT_CODE% |
|||
|
|||
:mainEnd |
|||
if "%OS%"=="Windows_NT" endlocal |
|||
|
|||
:omega |
@ -1,5 +0,0 @@ |
|||
include ':app' |
|||
include ':capacitor-cordova-android-plugins' |
|||
project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') |
|||
|
|||
apply from: 'capacitor.settings.gradle' |
@ -1,16 +0,0 @@ |
|||
ext { |
|||
minSdkVersion = 22 |
|||
compileSdkVersion = 34 |
|||
targetSdkVersion = 34 |
|||
androidxActivityVersion = '1.8.0' |
|||
androidxAppCompatVersion = '1.6.1' |
|||
androidxCoordinatorLayoutVersion = '1.2.0' |
|||
androidxCoreVersion = '1.12.0' |
|||
androidxFragmentVersion = '1.6.2' |
|||
coreSplashScreenVersion = '1.0.1' |
|||
androidxWebkitVersion = '1.9.0' |
|||
junitVersion = '4.13.2' |
|||
androidxJunitVersion = '1.1.5' |
|||
androidxEspressoCoreVersion = '3.5.1' |
|||
cordovaAndroidVersion = '10.1.1' |
|||
} |
@ -1,2 +0,0 @@ |
|||
|
|||
Application icons are here. They are processed for android & ios by the `capacitor-assets` command, as indicated in the BUILDING.md file. |
Before Width: | Height: | Size: 279 KiB |
Before Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 1.9 MiB |
@ -1,4 +0,0 @@ |
|||
#!/bin/bash |
|||
export IMAGENAME="$(basename $PWD):1.0" |
|||
|
|||
docker build . --network=host -t $IMAGENAME --no-cache |
@ -1,56 +0,0 @@ |
|||
{ |
|||
"appId": "app.timesafari", |
|||
"appName": "TimeSafari", |
|||
"webDir": "dist", |
|||
"bundledWebRuntime": false, |
|||
"server": { |
|||
"cleartext": true |
|||
}, |
|||
"plugins": { |
|||
"App": { |
|||
"appUrlOpen": { |
|||
"handlers": [ |
|||
{ |
|||
"url": "timesafari://*", |
|||
"autoVerify": true |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
"SQLite": { |
|||
"iosDatabaseLocation": "Library/CapacitorDatabase", |
|||
"iosIsEncryption": true, |
|||
"iosBiometric": { |
|||
"biometricAuth": true, |
|||
"biometricTitle": "Biometric login for TimeSafari" |
|||
}, |
|||
"androidIsEncryption": true, |
|||
"androidBiometric": { |
|||
"biometricAuth": true, |
|||
"biometricTitle": "Biometric login for TimeSafari" |
|||
} |
|||
} |
|||
}, |
|||
"ios": { |
|||
"contentInset": "never", |
|||
"allowsLinkPreview": true, |
|||
"scrollEnabled": true, |
|||
"limitsNavigationsToAppBoundDomains": true, |
|||
"backgroundColor": "#ffffff", |
|||
"allowNavigation": [ |
|||
"*.timesafari.app", |
|||
"*.jsdelivr.net", |
|||
"api.endorser.ch" |
|||
] |
|||
}, |
|||
"android": { |
|||
"allowMixedContent": false, |
|||
"captureInput": true, |
|||
"webContentsDebuggingEnabled": false, |
|||
"allowNavigation": [ |
|||
"*.timesafari.app", |
|||
"*.jsdelivr.net", |
|||
"api.endorser.ch" |
|||
] |
|||
} |
|||
} |
@ -1,166 +0,0 @@ |
|||
# TimeSafari Deep Linking Documentation |
|||
|
|||
## Type System Overview |
|||
|
|||
The deep linking system uses a multi-layered type safety approach: |
|||
|
|||
1. **Runtime Validation (Zod Schemas)** |
|||
- Validates URL structure |
|||
- Enforces parameter requirements |
|||
- Sanitizes input data |
|||
- Provides detailed validation errors |
|||
- Generates TypeScript types automatically |
|||
|
|||
2. **TypeScript Types** |
|||
- Generated from Zod schemas using `z.infer` |
|||
- Ensures compile-time type safety |
|||
- Provides IDE autocompletion |
|||
- Catches type errors during development |
|||
- Maintains single source of truth for types |
|||
|
|||
3. **Router Integration** |
|||
- Type-safe parameter passing |
|||
- Route-specific parameter validation |
|||
- Query parameter type checking |
|||
- Automatic type inference for route parameters |
|||
|
|||
## Type System Implementation |
|||
|
|||
### Zod Schema to TypeScript Type Generation |
|||
|
|||
```typescript |
|||
// Define the schema |
|||
const claimSchema = z.object({ |
|||
id: z.string(), |
|||
view: z.enum(["details", "certificate", "raw"]).optional() |
|||
}); |
|||
|
|||
// TypeScript type is automatically generated |
|||
type ClaimParams = z.infer<typeof claimSchema>; |
|||
// Equivalent to: |
|||
// type ClaimParams = { |
|||
// id: string; |
|||
// view?: "details" | "certificate" | "raw"; |
|||
// } |
|||
``` |
|||
|
|||
### Type Safety Layers |
|||
|
|||
1. **Schema Definition** |
|||
```typescript |
|||
// src/interfaces/deepLinks.ts |
|||
export const deepLinkSchemas = { |
|||
claim: z.object({ |
|||
id: z.string(), |
|||
view: z.enum(["details", "certificate", "raw"]).optional() |
|||
}), |
|||
// Other route schemas... |
|||
}; |
|||
``` |
|||
|
|||
2. **Type Generation** |
|||
```typescript |
|||
// Types are automatically generated from schemas |
|||
export type DeepLinkParams = { |
|||
[K in keyof typeof deepLinkSchemas]: z.infer<(typeof deepLinkSchemas)[K]>; |
|||
}; |
|||
``` |
|||
|
|||
3. **Runtime Validation** |
|||
```typescript |
|||
// In DeepLinkHandler |
|||
const result = deepLinkSchemas.claim.safeParse(params); |
|||
if (!result.success) { |
|||
// Handle validation errors |
|||
console.error(result.error); |
|||
} |
|||
``` |
|||
|
|||
### Error Handling Types |
|||
|
|||
```typescript |
|||
export interface DeepLinkError extends Error { |
|||
code: string; |
|||
details?: unknown; |
|||
} |
|||
|
|||
// Usage in error handling |
|||
try { |
|||
await handler.handleDeepLink(url); |
|||
} catch (error) { |
|||
if (error instanceof DeepLinkError) { |
|||
// Type-safe error handling |
|||
console.error(error.code, error.message); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Implementation Files |
|||
|
|||
- `src/interfaces/deepLinks.ts`: Type definitions and validation schemas |
|||
- `src/services/deepLinks.ts`: Deep link processing service |
|||
- `src/main.capacitor.ts`: Capacitor integration |
|||
- `src/views/DeepLinkRedirectView.vue`: Page to handle links to both mobile and web |
|||
|
|||
## Type Safety Examples |
|||
|
|||
```typescript |
|||
// Parameter type safety |
|||
type ClaimParams = DeepLinkParams["claim"]; |
|||
// TypeScript knows this has: |
|||
// - id: string |
|||
// - view?: "details" | "certificate" | "raw" |
|||
// Runtime validation |
|||
const result = deepLinkSchemas.claim.safeParse({ |
|||
id: "123", |
|||
view: "details" |
|||
}); |
|||
// Validates at runtime with detailed error messages |
|||
``` |
|||
|
|||
## Supported URL Schemes |
|||
|
|||
All deep links follow the format: `timesafari://<route>/<param>?<query>` |
|||
|
|||
### Claim Routes |
|||
|
|||
- `timesafari://claim/:id` |
|||
- Query params: |
|||
- `view`: "details" | "certificate" | "raw" |
|||
|
|||
- `timesafari://claim-cert/:id` |
|||
- `timesafari://claim-add-raw/:id` |
|||
- Query params: |
|||
- `claim`: JSON string of claim data |
|||
- `claimJwtId`: JWT ID for claim |
|||
|
|||
### Contact Routes |
|||
|
|||
- `timesafari://contact-edit/:did` |
|||
- `timesafari://contact-import/:jwt` |
|||
- Query params: |
|||
- `contacts`: JSON array of contacts |
|||
|
|||
### Project Routes |
|||
|
|||
- `timesafari://project/:id` |
|||
- Query params: |
|||
- `view`: "details" | "edit" |
|||
|
|||
### Invite Routes |
|||
|
|||
- `timesafari://invite-one-accept/:jwt` |
|||
- Query params: |
|||
- `type`: "one" | "many" |
|||
|
|||
### Gift Routes |
|||
|
|||
- `timesafari://confirm-gift/:id` |
|||
- Query params: |
|||
- `action`: "confirm" | "details" |
|||
|
|||
### Offer Routes |
|||
|
|||
- `timesafari://offer-details/:id` |
|||
- Query params: |
|||
- `view`: "details" |
@ -1,76 +0,0 @@ |
|||
# TimeSafari Docs |
|||
|
|||
## Generating PDF from Markdown on OSx |
|||
|
|||
This uses Pandoc and BasicTex (LaTeX) Installed through Homebrew. |
|||
|
|||
### Set Up |
|||
|
|||
```bash |
|||
brew install pandoc |
|||
|
|||
brew install basictex |
|||
|
|||
# Setting up LaTex packages |
|||
|
|||
# First update tlmgr |
|||
sudo tlmgr update --self |
|||
|
|||
# Then install LaTex packages |
|||
sudo tlmgr install bbding |
|||
sudo tlmgr install enumitem |
|||
sudo tlmgr install environ |
|||
sudo tlmgr install fancyhdr |
|||
sudo tlmgr install framed |
|||
sudo tlmgr install import |
|||
sudo tlmgr install lastpage # Enables Page X of Y |
|||
sudo tlmgr install mdframed |
|||
sudo tlmgr install multirow |
|||
sudo tlmgr install needspace |
|||
sudo tlmgr install ntheorem |
|||
sudo tlmgr install tabu |
|||
sudo tlmgr install tcolorbox |
|||
sudo tlmgr install textpos |
|||
sudo tlmgr install titlesec |
|||
sudo tlmgr install titling # Required for the fancy headers used |
|||
sudo tlmgr install threeparttable |
|||
sudo tlmgr install trimspaces |
|||
sudo tlmgr install tocloft # Required for \tableofcontents generation |
|||
sudo tlmgr install varwidth |
|||
sudo tlmgr install wrapfig |
|||
|
|||
# Install fonts |
|||
sudo tlmgr install cmbright |
|||
sudo tlmgr install collection-fontsrecommended # And set up fonts |
|||
sudo tlmgr install fira |
|||
sudo tlmgr install fontaxes |
|||
sudo tlmgr install libertine # The main font the doc uses |
|||
sudo tlmgr install opensans |
|||
sudo tlmgr install sourceserifpro |
|||
|
|||
``` |
|||
|
|||
#### References |
|||
|
|||
The following guide was adapted to this project except that we install with Brew and have a few more packages. |
|||
|
|||
Guide: https://daniel.feldroy.com/posts/setting-up-latex-on-mac-os-x |
|||
|
|||
### Usage |
|||
|
|||
Use the `pandoc` command to generate a PDF. |
|||
|
|||
```bash |
|||
pandoc usage-guide.md -o usage-guide.pdf |
|||
``` |
|||
|
|||
And you can open the PDF with the `open` command. |
|||
|
|||
```bash |
|||
open usage-guide.pdf |
|||
``` |
|||
|
|||
Or use this one-liner |
|||
```bash |
|||
pandoc usage-guide.md -o usage-guide.pdf && open usage-guide.pdf |
|||
``` |
@ -1,399 +0,0 @@ |
|||
# Dexie to absurd-sql Mapping Guide |
|||
|
|||
## Schema Mapping |
|||
|
|||
### Current Dexie Schema |
|||
```typescript |
|||
// Current Dexie schema |
|||
const db = new Dexie('TimeSafariDB'); |
|||
|
|||
db.version(1).stores({ |
|||
accounts: 'did, publicKeyHex, createdAt, updatedAt', |
|||
settings: 'key, value, updatedAt', |
|||
contacts: 'id, did, name, createdAt, updatedAt' |
|||
}); |
|||
``` |
|||
|
|||
### New SQLite Schema |
|||
```sql |
|||
-- New SQLite schema |
|||
CREATE TABLE accounts ( |
|||
did TEXT PRIMARY KEY, |
|||
public_key_hex TEXT NOT NULL, |
|||
created_at INTEGER NOT NULL, |
|||
updated_at INTEGER NOT NULL |
|||
); |
|||
|
|||
CREATE TABLE settings ( |
|||
key TEXT PRIMARY KEY, |
|||
value TEXT NOT NULL, |
|||
updated_at INTEGER NOT NULL |
|||
); |
|||
|
|||
CREATE TABLE contacts ( |
|||
id TEXT PRIMARY KEY, |
|||
did TEXT NOT NULL, |
|||
name TEXT, |
|||
created_at INTEGER NOT NULL, |
|||
updated_at INTEGER NOT NULL, |
|||
FOREIGN KEY (did) REFERENCES accounts(did) |
|||
); |
|||
|
|||
-- Indexes for performance |
|||
CREATE INDEX idx_accounts_created_at ON accounts(created_at); |
|||
CREATE INDEX idx_contacts_did ON contacts(did); |
|||
CREATE INDEX idx_settings_updated_at ON settings(updated_at); |
|||
``` |
|||
|
|||
## Query Mapping |
|||
|
|||
### 1. Account Operations |
|||
|
|||
#### Get Account by DID |
|||
```typescript |
|||
// Dexie |
|||
const account = await db.accounts.get(did); |
|||
|
|||
// absurd-sql |
|||
const result = await db.exec(` |
|||
SELECT * FROM accounts WHERE did = ? |
|||
`, [did]); |
|||
const account = result[0]?.values[0]; |
|||
``` |
|||
|
|||
#### Get All Accounts |
|||
```typescript |
|||
// Dexie |
|||
const accounts = await db.accounts.toArray(); |
|||
|
|||
// absurd-sql |
|||
const result = await db.exec(` |
|||
SELECT * FROM accounts ORDER BY created_at DESC |
|||
`); |
|||
const accounts = result[0]?.values || []; |
|||
``` |
|||
|
|||
#### Add Account |
|||
```typescript |
|||
// Dexie |
|||
await db.accounts.add({ |
|||
did, |
|||
publicKeyHex, |
|||
createdAt: Date.now(), |
|||
updatedAt: Date.now() |
|||
}); |
|||
|
|||
// absurd-sql |
|||
await db.run(` |
|||
INSERT INTO accounts (did, public_key_hex, created_at, updated_at) |
|||
VALUES (?, ?, ?, ?) |
|||
`, [did, publicKeyHex, Date.now(), Date.now()]); |
|||
``` |
|||
|
|||
#### Update Account |
|||
```typescript |
|||
// Dexie |
|||
await db.accounts.update(did, { |
|||
publicKeyHex, |
|||
updatedAt: Date.now() |
|||
}); |
|||
|
|||
// absurd-sql |
|||
await db.run(` |
|||
UPDATE accounts |
|||
SET public_key_hex = ?, updated_at = ? |
|||
WHERE did = ? |
|||
`, [publicKeyHex, Date.now(), did]); |
|||
``` |
|||
|
|||
### 2. Settings Operations |
|||
|
|||
#### Get Setting |
|||
```typescript |
|||
// Dexie |
|||
const setting = await db.settings.get(key); |
|||
|
|||
// absurd-sql |
|||
const result = await db.exec(` |
|||
SELECT * FROM settings WHERE key = ? |
|||
`, [key]); |
|||
const setting = result[0]?.values[0]; |
|||
``` |
|||
|
|||
#### Set Setting |
|||
```typescript |
|||
// Dexie |
|||
await db.settings.put({ |
|||
key, |
|||
value, |
|||
updatedAt: Date.now() |
|||
}); |
|||
|
|||
// absurd-sql |
|||
await db.run(` |
|||
INSERT INTO settings (key, value, updated_at) |
|||
VALUES (?, ?, ?) |
|||
ON CONFLICT(key) DO UPDATE SET |
|||
value = excluded.value, |
|||
updated_at = excluded.updated_at |
|||
`, [key, value, Date.now()]); |
|||
``` |
|||
|
|||
### 3. Contact Operations |
|||
|
|||
#### Get Contacts by Account |
|||
```typescript |
|||
// Dexie |
|||
const contacts = await db.contacts |
|||
.where('did') |
|||
.equals(accountDid) |
|||
.toArray(); |
|||
|
|||
// absurd-sql |
|||
const result = await db.exec(` |
|||
SELECT * FROM contacts |
|||
WHERE did = ? |
|||
ORDER BY created_at DESC |
|||
`, [accountDid]); |
|||
const contacts = result[0]?.values || []; |
|||
``` |
|||
|
|||
#### Add Contact |
|||
```typescript |
|||
// Dexie |
|||
await db.contacts.add({ |
|||
id: generateId(), |
|||
did: accountDid, |
|||
name, |
|||
createdAt: Date.now(), |
|||
updatedAt: Date.now() |
|||
}); |
|||
|
|||
// absurd-sql |
|||
await db.run(` |
|||
INSERT INTO contacts (id, did, name, created_at, updated_at) |
|||
VALUES (?, ?, ?, ?, ?) |
|||
`, [generateId(), accountDid, name, Date.now(), Date.now()]); |
|||
``` |
|||
|
|||
## Transaction Mapping |
|||
|
|||
### Batch Operations |
|||
```typescript |
|||
// Dexie |
|||
await db.transaction('rw', [db.accounts, db.contacts], async () => { |
|||
await db.accounts.add(account); |
|||
await db.contacts.bulkAdd(contacts); |
|||
}); |
|||
|
|||
// absurd-sql |
|||
await db.exec('BEGIN TRANSACTION;'); |
|||
try { |
|||
await db.run(` |
|||
INSERT INTO accounts (did, public_key_hex, created_at, updated_at) |
|||
VALUES (?, ?, ?, ?) |
|||
`, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]); |
|||
|
|||
for (const contact of contacts) { |
|||
await db.run(` |
|||
INSERT INTO contacts (id, did, name, created_at, updated_at) |
|||
VALUES (?, ?, ?, ?, ?) |
|||
`, [contact.id, contact.did, contact.name, contact.createdAt, contact.updatedAt]); |
|||
} |
|||
await db.exec('COMMIT;'); |
|||
} catch (error) { |
|||
await db.exec('ROLLBACK;'); |
|||
throw error; |
|||
} |
|||
``` |
|||
|
|||
## Migration Helper Functions |
|||
|
|||
### 1. Data Export (Dexie to JSON) |
|||
```typescript |
|||
async function exportDexieData(): Promise<MigrationData> { |
|||
const db = new Dexie('TimeSafariDB'); |
|||
|
|||
return { |
|||
accounts: await db.accounts.toArray(), |
|||
settings: await db.settings.toArray(), |
|||
contacts: await db.contacts.toArray(), |
|||
metadata: { |
|||
version: '1.0.0', |
|||
timestamp: Date.now(), |
|||
dexieVersion: Dexie.version |
|||
} |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
### 2. Data Import (JSON to absurd-sql) |
|||
```typescript |
|||
async function importToAbsurdSql(data: MigrationData): Promise<void> { |
|||
await db.exec('BEGIN TRANSACTION;'); |
|||
try { |
|||
// Import accounts |
|||
for (const account of data.accounts) { |
|||
await db.run(` |
|||
INSERT INTO accounts (did, public_key_hex, created_at, updated_at) |
|||
VALUES (?, ?, ?, ?) |
|||
`, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]); |
|||
} |
|||
|
|||
// Import settings |
|||
for (const setting of data.settings) { |
|||
await db.run(` |
|||
INSERT INTO settings (key, value, updated_at) |
|||
VALUES (?, ?, ?) |
|||
`, [setting.key, setting.value, setting.updatedAt]); |
|||
} |
|||
|
|||
// Import contacts |
|||
for (const contact of data.contacts) { |
|||
await db.run(` |
|||
INSERT INTO contacts (id, did, name, created_at, updated_at) |
|||
VALUES (?, ?, ?, ?, ?) |
|||
`, [contact.id, contact.did, contact.name, contact.createdAt, contact.updatedAt]); |
|||
} |
|||
await db.exec('COMMIT;'); |
|||
} catch (error) { |
|||
await db.exec('ROLLBACK;'); |
|||
throw error; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### 3. Verification |
|||
```typescript |
|||
async function verifyMigration(dexieData: MigrationData): Promise<boolean> { |
|||
// Verify account count |
|||
const accountResult = await db.exec('SELECT COUNT(*) as count FROM accounts'); |
|||
const accountCount = accountResult[0].values[0][0]; |
|||
if (accountCount !== dexieData.accounts.length) { |
|||
return false; |
|||
} |
|||
|
|||
// Verify settings count |
|||
const settingsResult = await db.exec('SELECT COUNT(*) as count FROM settings'); |
|||
const settingsCount = settingsResult[0].values[0][0]; |
|||
if (settingsCount !== dexieData.settings.length) { |
|||
return false; |
|||
} |
|||
|
|||
// Verify contacts count |
|||
const contactsResult = await db.exec('SELECT COUNT(*) as count FROM contacts'); |
|||
const contactsCount = contactsResult[0].values[0][0]; |
|||
if (contactsCount !== dexieData.contacts.length) { |
|||
return false; |
|||
} |
|||
|
|||
// Verify data integrity |
|||
for (const account of dexieData.accounts) { |
|||
const result = await db.exec( |
|||
'SELECT * FROM accounts WHERE did = ?', |
|||
[account.did] |
|||
); |
|||
const migratedAccount = result[0]?.values[0]; |
|||
if (!migratedAccount || |
|||
migratedAccount[1] !== account.publicKeyHex) { // public_key_hex is second column |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
``` |
|||
|
|||
## Performance Considerations |
|||
|
|||
### 1. Indexing |
|||
- Dexie automatically creates indexes based on the schema |
|||
- absurd-sql requires explicit index creation |
|||
- Added indexes for frequently queried fields |
|||
- Use `PRAGMA journal_mode=MEMORY;` for better performance |
|||
|
|||
### 2. Batch Operations |
|||
- Dexie has built-in bulk operations |
|||
- absurd-sql uses transactions for batch operations |
|||
- Consider chunking large datasets |
|||
- Use prepared statements for repeated queries |
|||
|
|||
### 3. Query Optimization |
|||
- Dexie uses IndexedDB's native indexing |
|||
- absurd-sql requires explicit query optimization |
|||
- Use prepared statements for repeated queries |
|||
- Consider using `PRAGMA synchronous=NORMAL;` for better performance |
|||
|
|||
## Error Handling |
|||
|
|||
### 1. Common Errors |
|||
```typescript |
|||
// Dexie errors |
|||
try { |
|||
await db.accounts.add(account); |
|||
} catch (error) { |
|||
if (error instanceof Dexie.ConstraintError) { |
|||
// Handle duplicate key |
|||
} |
|||
} |
|||
|
|||
// absurd-sql errors |
|||
try { |
|||
await db.run(` |
|||
INSERT INTO accounts (did, public_key_hex, created_at, updated_at) |
|||
VALUES (?, ?, ?, ?) |
|||
`, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]); |
|||
} catch (error) { |
|||
if (error.message.includes('UNIQUE constraint failed')) { |
|||
// Handle duplicate key |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### 2. Transaction Recovery |
|||
```typescript |
|||
// Dexie transaction |
|||
try { |
|||
await db.transaction('rw', db.accounts, async () => { |
|||
// Operations |
|||
}); |
|||
} catch (error) { |
|||
// Dexie automatically rolls back |
|||
} |
|||
|
|||
// absurd-sql transaction |
|||
try { |
|||
await db.exec('BEGIN TRANSACTION;'); |
|||
// Operations |
|||
await db.exec('COMMIT;'); |
|||
} catch (error) { |
|||
await db.exec('ROLLBACK;'); |
|||
throw error; |
|||
} |
|||
``` |
|||
|
|||
## Migration Strategy |
|||
|
|||
1. **Preparation** |
|||
- Export all Dexie data |
|||
- Verify data integrity |
|||
- Create SQLite schema |
|||
- Setup indexes |
|||
|
|||
2. **Migration** |
|||
- Import data in transactions |
|||
- Verify each batch |
|||
- Handle errors gracefully |
|||
- Maintain backup |
|||
|
|||
3. **Verification** |
|||
- Compare record counts |
|||
- Verify data integrity |
|||
- Test common queries |
|||
- Validate relationships |
|||
|
|||
4. **Cleanup** |
|||
- Remove Dexie database |
|||
- Clear IndexedDB storage |
|||
- Update application code |
|||
- Remove old dependencies |
Before Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 62 KiB |