Compare commits
30 Commits
deep-link-
...
electron-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8827c4a973 | ||
| aa346a9abd | |||
| 9ea2f96106 | |||
| 623bf12ecd | |||
|
|
427660d686 | ||
|
|
643f31c43a | ||
| 2c6b787fa2 | |||
|
|
ec53452220 | ||
|
|
ec326495b2 | ||
|
|
cc50c38d13 | ||
| 3969167d92 | |||
|
|
9dfb2fda27 | ||
|
|
d3aa2e40a0 | ||
|
|
08cda50f13 | ||
| 716a23e76b | |||
|
|
dc857f9119 | ||
|
|
3a8652fd8d | ||
|
|
c2949c4dbf | ||
|
|
4ba58145d0 | ||
|
|
e3cc22245c | ||
|
|
eb44e7b51e | ||
|
|
ca8d72e1c9 | ||
|
|
a4528c5703 | ||
|
|
6acebb66ef | ||
| b3f7026afe | |||
|
|
ec1a725832 | ||
|
|
6d316c2b3f | ||
|
|
24f6730572 | ||
|
|
0fc44b31bf | ||
|
|
bed2c7106a |
32
BUILDING.md
32
BUILDING.md
@@ -251,7 +251,7 @@ npm run build:web:dev # Start development server with hot reload
|
|||||||
npm run build:web # Development build (starts dev server with hot reload)
|
npm run build:web # Development build (starts dev server with hot reload)
|
||||||
npm run build:web:test # Test environment build (optimized for testing)
|
npm run build:web:test # Test environment build (optimized for testing)
|
||||||
npm run build:web:prod # Production build (optimized for production)
|
npm run build:web:prod # Production build (optimized for production)
|
||||||
npm run build:web:serve # Build and serve locally (builds then serves)
|
npm run build:web:serve # Build and serve locally for production testing
|
||||||
|
|
||||||
# Docker builds
|
# Docker builds
|
||||||
npm run build:web:docker # Development build with Docker containerization
|
npm run build:web:docker # Development build with Docker containerization
|
||||||
@@ -269,6 +269,12 @@ Start the development server using `npm run build:web:dev` or `npm run build:web
|
|||||||
2. The built files will be in the `dist` directory
|
2. The built files will be in the `dist` directory
|
||||||
3. To test the production build locally, use `npm run build:web:serve` (builds then serves)
|
3. To test the production build locally, use `npm run build:web:serve` (builds then serves)
|
||||||
|
|
||||||
|
**Why Use `serve`?**
|
||||||
|
- **Production Testing**: Test your optimized production build locally before deployment
|
||||||
|
- **SPA Routing Validation**: Verify deep linking and navigation work correctly (handles routes like `/discover`, `/account`)
|
||||||
|
- **Performance Testing**: Test the minified and optimized build locally
|
||||||
|
- **Deployment Validation**: Ensure built files work correctly when served by a real HTTP server
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
### Web Build Script Details
|
### Web Build Script Details
|
||||||
@@ -288,7 +294,7 @@ All web build commands use the `./scripts/build-web.sh` script, which provides:
|
|||||||
- **Clean Build**: Removes previous `dist/` directory
|
- **Clean Build**: Removes previous `dist/` directory
|
||||||
- **Vite Build**: Executes `npx vite build --config vite.config.web.mts`
|
- **Vite Build**: Executes `npx vite build --config vite.config.web.mts`
|
||||||
- **Docker Support**: Optional Docker containerization
|
- **Docker Support**: Optional Docker containerization
|
||||||
- **Local Serving**: Built-in HTTP server for testing builds
|
- **Local Serving**: Built-in HTTP server for testing builds with SPA routing support
|
||||||
|
|
||||||
**Direct Script Usage:**
|
**Direct Script Usage:**
|
||||||
|
|
||||||
@@ -324,6 +330,25 @@ All web build commands use the `./scripts/build-web.sh` script, which provides:
|
|||||||
- `5` - Serve command failed
|
- `5` - Serve command failed
|
||||||
- `6` - Invalid build mode
|
- `6` - Invalid build mode
|
||||||
|
|
||||||
|
### Local Serving with `serve`
|
||||||
|
|
||||||
|
The `serve` functionality provides a local HTTP server for testing production builds:
|
||||||
|
|
||||||
|
**What It Does:**
|
||||||
|
1. **Builds** the application using Vite
|
||||||
|
2. **Serves** the built files from the `dist/` directory
|
||||||
|
3. **Handles SPA Routing** - serves `index.html` for all routes (fixes 404s on `/discover`, `/account`, etc.)
|
||||||
|
|
||||||
|
**Server Options:**
|
||||||
|
- **Primary**: `npx serve -s dist -l 8080` (recommended - full SPA support)
|
||||||
|
- **Fallback**: Python HTTP server (limited SPA routing support)
|
||||||
|
|
||||||
|
**Use Cases:**
|
||||||
|
- Testing production builds before deployment
|
||||||
|
- Validating SPA routing behavior
|
||||||
|
- Performance testing of optimized builds
|
||||||
|
- Debugging production build issues locally
|
||||||
|
|
||||||
### Compile and minify for test & production
|
### Compile and minify for test & production
|
||||||
|
|
||||||
- If there are DB changes: before updating the test server, open browser(s) with
|
- If there are DB changes: before updating the test server, open browser(s) with
|
||||||
@@ -592,7 +617,8 @@ The Electron build process follows a multi-stage approach:
|
|||||||
#### **Stage 2: Capacitor Sync**
|
#### **Stage 2: Capacitor Sync**
|
||||||
|
|
||||||
- Copies web assets to Electron app directory
|
- Copies web assets to Electron app directory
|
||||||
- Syncs Capacitor configuration and plugins
|
- Uses Electron-specific Capacitor configuration (not copied from main config)
|
||||||
|
- Syncs Capacitor plugins for Electron platform
|
||||||
- Prepares native module bindings
|
- Prepares native module bindings
|
||||||
|
|
||||||
#### **Stage 3: TypeScript Compile**
|
#### **Stage 3: TypeScript Compile**
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ npm install
|
|||||||
npm run build:web:serve -- --test
|
npm run build:web:serve -- --test
|
||||||
```
|
```
|
||||||
|
|
||||||
To be able to make submissions: go to "profile" (bottom left), go to the bottom and expand "Show Advanced Settings", go to the bottom and to the "Test Page", and finally "Become User 0" to see all the functionality.
|
To be able to take action on the platform: go to [the test page](http://localhost:8080/test) and click "Become User 0".
|
||||||
|
|
||||||
See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all platforms (Web, Electron, iOS, Android, Docker).
|
See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all platforms (Web, Electron, iOS, Android, Docker).
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ dependencies {
|
|||||||
implementation project(':capacitor-clipboard')
|
implementation project(':capacitor-clipboard')
|
||||||
implementation project(':capacitor-filesystem')
|
implementation project(':capacitor-filesystem')
|
||||||
implementation project(':capacitor-share')
|
implementation project(':capacitor-share')
|
||||||
|
implementation project(':capacitor-status-bar')
|
||||||
implementation project(':capawesome-capacitor-file-picker')
|
implementation project(':capawesome-capacitor-file-picker')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
android:label="@string/title_activity_main"
|
android:label="@string/title_activity_main"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:theme="@style/AppTheme.NoActionBarLaunch">
|
android:theme="@style/AppTheme.NoActionBarLaunch">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
"pkg": "@capacitor/share",
|
"pkg": "@capacitor/share",
|
||||||
"classpath": "com.capacitorjs.plugins.share.SharePlugin"
|
"classpath": "com.capacitorjs.plugins.share.SharePlugin"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"pkg": "@capacitor/status-bar",
|
||||||
|
"classpath": "com.capacitorjs.plugins.statusbar.StatusBarPlugin"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"pkg": "@capawesome/capacitor-file-picker",
|
"pkg": "@capawesome/capacitor-file-picker",
|
||||||
"classpath": "io.capawesome.capacitorjs.plugins.filepicker.FilePickerPlugin"
|
"classpath": "io.capawesome.capacitorjs.plugins.filepicker.FilePickerPlugin"
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
package app.timesafari;
|
package app.timesafari;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.WindowInsetsController;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebSettings;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
import com.getcapacitor.BridgeActivity;
|
import com.getcapacitor.BridgeActivity;
|
||||||
|
import app.timesafari.safearea.SafeAreaPlugin;
|
||||||
//import com.getcapacitor.community.sqlite.SQLite;
|
//import com.getcapacitor.community.sqlite.SQLite;
|
||||||
|
|
||||||
public class MainActivity extends BridgeActivity {
|
public class MainActivity extends BridgeActivity {
|
||||||
@@ -9,7 +18,39 @@ public class MainActivity extends BridgeActivity {
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Enable edge-to-edge display for modern Android
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
// Android 11+ (API 30+)
|
||||||
|
getWindow().setDecorFitsSystemWindows(false);
|
||||||
|
|
||||||
|
// Set up system UI visibility for edge-to-edge
|
||||||
|
WindowInsetsController controller = getWindow().getInsetsController();
|
||||||
|
if (controller != null) {
|
||||||
|
controller.setSystemBarsAppearance(
|
||||||
|
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS |
|
||||||
|
WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS,
|
||||||
|
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS |
|
||||||
|
WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
|
||||||
|
);
|
||||||
|
controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Legacy Android (API 21-29)
|
||||||
|
getWindow().getDecorView().setSystemUiVisibility(
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||||
|
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR |
|
||||||
|
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register SafeArea plugin
|
||||||
|
registerPlugin(SafeAreaPlugin.class);
|
||||||
|
|
||||||
// Initialize SQLite
|
// Initialize SQLite
|
||||||
//registerPlugin(SQLite.class);
|
//registerPlugin(SQLite.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package app.timesafari.safearea;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
import com.getcapacitor.JSObject;
|
||||||
|
import com.getcapacitor.Plugin;
|
||||||
|
import com.getcapacitor.PluginCall;
|
||||||
|
import com.getcapacitor.PluginMethod;
|
||||||
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||||
|
|
||||||
|
@CapacitorPlugin(name = "SafeArea")
|
||||||
|
public class SafeAreaPlugin extends Plugin {
|
||||||
|
|
||||||
|
@PluginMethod
|
||||||
|
public void getSafeAreaInsets(PluginCall call) {
|
||||||
|
JSObject result = new JSObject();
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
WindowInsets insets = getActivity().getWindow().getDecorView().getRootWindowInsets();
|
||||||
|
if (insets != null) {
|
||||||
|
int top = insets.getInsets(WindowInsets.Type.statusBars()).top;
|
||||||
|
int bottom = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
|
||||||
|
int left = insets.getInsets(WindowInsets.Type.systemBars()).left;
|
||||||
|
int right = insets.getInsets(WindowInsets.Type.systemBars()).right;
|
||||||
|
|
||||||
|
result.put("top", top);
|
||||||
|
result.put("bottom", bottom);
|
||||||
|
result.put("left", left);
|
||||||
|
result.put("right", right);
|
||||||
|
|
||||||
|
call.resolve(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback values
|
||||||
|
result.put("top", 0);
|
||||||
|
result.put("bottom", 0);
|
||||||
|
result.put("left", 0);
|
||||||
|
result.put("right", 0);
|
||||||
|
|
||||||
|
call.resolve(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,5 +18,14 @@
|
|||||||
|
|
||||||
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
|
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
|
||||||
<item name="android:background">@drawable/splash</item>
|
<item name="android:background">@drawable/splash</item>
|
||||||
|
<item name="android:windowTranslucentStatus">false</item>
|
||||||
|
<item name="android:windowTranslucentNavigation">false</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||||
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:windowLightStatusBar">true</item>
|
||||||
|
<item name="android:windowLightNavigationBar">true</item>
|
||||||
|
<item name="android:enforceStatusBarContrast">false</item>
|
||||||
|
<item name="android:enforceNavigationBarContrast">false</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -23,5 +23,8 @@ project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacit
|
|||||||
include ':capacitor-share'
|
include ':capacitor-share'
|
||||||
project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android')
|
project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android')
|
||||||
|
|
||||||
|
include ':capacitor-status-bar'
|
||||||
|
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
|
||||||
|
|
||||||
include ':capawesome-capacitor-file-picker'
|
include ':capawesome-capacitor-file-picker'
|
||||||
project(':capawesome-capacitor-file-picker').projectDir = new File('../node_modules/@capawesome/capacitor-file-picker/android')
|
project(':capawesome-capacitor-file-picker').projectDir = new File('../node_modules/@capawesome/capacitor-file-picker/android')
|
||||||
|
|||||||
116
electron/capacitor.config.ts
Normal file
116
electron/capacitor.config.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import { CapacitorConfig } from '@capacitor/cli';
|
||||||
|
|
||||||
|
const config: CapacitorConfig = {
|
||||||
|
appId: 'app.timesafari',
|
||||||
|
appName: 'TimeSafari',
|
||||||
|
webDir: 'dist',
|
||||||
|
server: {
|
||||||
|
cleartext: true
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
App: {
|
||||||
|
appUrlOpen: {
|
||||||
|
handlers: [
|
||||||
|
{
|
||||||
|
url: 'timesafari://*',
|
||||||
|
autoVerify: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SplashScreen: {
|
||||||
|
launchShowDuration: 3000,
|
||||||
|
launchAutoHide: true,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
androidSplashResourceName: 'splash',
|
||||||
|
androidScaleType: 'CENTER_CROP',
|
||||||
|
showSpinner: false,
|
||||||
|
androidSpinnerStyle: 'large',
|
||||||
|
iosSpinnerStyle: 'small',
|
||||||
|
spinnerColor: '#999999',
|
||||||
|
splashFullScreen: true,
|
||||||
|
splashImmersive: true
|
||||||
|
},
|
||||||
|
CapSQLite: {
|
||||||
|
iosDatabaseLocation: 'Library/CapacitorDatabase',
|
||||||
|
iosIsEncryption: false,
|
||||||
|
iosBiometric: {
|
||||||
|
biometricAuth: false,
|
||||||
|
biometricTitle: 'Biometric login for TimeSafari'
|
||||||
|
},
|
||||||
|
androidIsEncryption: false,
|
||||||
|
androidBiometric: {
|
||||||
|
biometricAuth: false,
|
||||||
|
biometricTitle: 'Biometric login for TimeSafari'
|
||||||
|
},
|
||||||
|
electronIsEncryption: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ios: {
|
||||||
|
contentInset: 'never',
|
||||||
|
allowsLinkPreview: true,
|
||||||
|
scrollEnabled: true,
|
||||||
|
limitsNavigationsToAppBoundDomains: true,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
allowNavigation: [
|
||||||
|
'*.timesafari.app',
|
||||||
|
'*.jsdelivr.net',
|
||||||
|
'api.endorser.ch'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
allowMixedContent: true,
|
||||||
|
captureInput: true,
|
||||||
|
webContentsDebuggingEnabled: false,
|
||||||
|
allowNavigation: [
|
||||||
|
'*.timesafari.app',
|
||||||
|
'*.jsdelivr.net',
|
||||||
|
'api.endorser.ch',
|
||||||
|
'10.0.2.2:3000'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
electron: {
|
||||||
|
deepLinking: {
|
||||||
|
schemes: ['timesafari']
|
||||||
|
},
|
||||||
|
buildOptions: {
|
||||||
|
appId: 'app.timesafari',
|
||||||
|
productName: 'TimeSafari',
|
||||||
|
directories: {
|
||||||
|
output: 'dist-electron-packages'
|
||||||
|
},
|
||||||
|
files: [
|
||||||
|
'dist/**/*',
|
||||||
|
'electron/**/*'
|
||||||
|
],
|
||||||
|
mac: {
|
||||||
|
category: 'public.app-category.productivity',
|
||||||
|
target: [
|
||||||
|
{
|
||||||
|
target: 'dmg',
|
||||||
|
arch: ['x64', 'arm64']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
win: {
|
||||||
|
target: [
|
||||||
|
{
|
||||||
|
target: 'nsis',
|
||||||
|
arch: ['x64']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
linux: {
|
||||||
|
target: [
|
||||||
|
{
|
||||||
|
target: 'AppImage',
|
||||||
|
arch: ['x64']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
category: 'Utility'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
1
electron/package-lock.json
generated
1
electron/package-lock.json
generated
@@ -56,7 +56,6 @@
|
|||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor-community/sqlite/-/sqlite-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@capacitor-community/sqlite/-/sqlite-6.0.2.tgz",
|
||||||
"integrity": "sha512-sj+2SPLu7E/3dM3xxcWwfNomG+aQHuN96/EFGrOtp4Dv30/2y5oIPyi6hZGjQGjPc5GDNoTQwW7vxWNzybjuMg==",
|
"integrity": "sha512-sj+2SPLu7E/3dM3xxcWwfNomG+aQHuN96/EFGrOtp4Dv30/2y5oIPyi6hZGjQGjPc5GDNoTQwW7vxWNzybjuMg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jeep-sqlite": "^2.7.2"
|
"jeep-sqlite": "^2.7.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compileOnSave": true,
|
"compileOnSave": true,
|
||||||
"include": ["./src/**/*", "./capacitor.config.ts", "./capacitor.config.js"],
|
"include": ["./src/**/*"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./build",
|
"outDir": "./build",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ def capacitor_pods
|
|||||||
pod 'CapacitorClipboard', :path => '../../node_modules/@capacitor/clipboard'
|
pod 'CapacitorClipboard', :path => '../../node_modules/@capacitor/clipboard'
|
||||||
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
|
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
|
||||||
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
|
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
|
||||||
|
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
||||||
pod 'CapawesomeCapacitorFilePicker', :path => '../../node_modules/@capawesome/capacitor-file-picker'
|
pod 'CapawesomeCapacitorFilePicker', :path => '../../node_modules/@capawesome/capacitor-file-picker'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ PODS:
|
|||||||
- GoogleMLKit/BarcodeScanning (= 5.0.0)
|
- GoogleMLKit/BarcodeScanning (= 5.0.0)
|
||||||
- CapacitorShare (6.0.3):
|
- CapacitorShare (6.0.3):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
|
- CapacitorStatusBar (6.0.2):
|
||||||
|
- Capacitor
|
||||||
- CapawesomeCapacitorFilePicker (6.2.0):
|
- CapawesomeCapacitorFilePicker (6.2.0):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- GoogleDataTransport (9.4.1):
|
- GoogleDataTransport (9.4.1):
|
||||||
@@ -96,6 +98,7 @@ DEPENDENCIES:
|
|||||||
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
|
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
|
||||||
- "CapacitorMlkitBarcodeScanning (from `../../node_modules/@capacitor-mlkit/barcode-scanning`)"
|
- "CapacitorMlkitBarcodeScanning (from `../../node_modules/@capacitor-mlkit/barcode-scanning`)"
|
||||||
- "CapacitorShare (from `../../node_modules/@capacitor/share`)"
|
- "CapacitorShare (from `../../node_modules/@capacitor/share`)"
|
||||||
|
- "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
|
||||||
- "CapawesomeCapacitorFilePicker (from `../../node_modules/@capawesome/capacitor-file-picker`)"
|
- "CapawesomeCapacitorFilePicker (from `../../node_modules/@capawesome/capacitor-file-picker`)"
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
@@ -134,6 +137,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../../node_modules/@capacitor-mlkit/barcode-scanning"
|
:path: "../../node_modules/@capacitor-mlkit/barcode-scanning"
|
||||||
CapacitorShare:
|
CapacitorShare:
|
||||||
:path: "../../node_modules/@capacitor/share"
|
:path: "../../node_modules/@capacitor/share"
|
||||||
|
CapacitorStatusBar:
|
||||||
|
:path: "../../node_modules/@capacitor/status-bar"
|
||||||
CapawesomeCapacitorFilePicker:
|
CapawesomeCapacitorFilePicker:
|
||||||
:path: "../../node_modules/@capawesome/capacitor-file-picker"
|
:path: "../../node_modules/@capawesome/capacitor-file-picker"
|
||||||
|
|
||||||
@@ -147,6 +152,7 @@ SPEC CHECKSUMS:
|
|||||||
CapacitorFilesystem: 59270a63c60836248812671aa3b15df673fbaf74
|
CapacitorFilesystem: 59270a63c60836248812671aa3b15df673fbaf74
|
||||||
CapacitorMlkitBarcodeScanning: 7652be9c7922f39203a361de735d340ae37e134e
|
CapacitorMlkitBarcodeScanning: 7652be9c7922f39203a361de735d340ae37e134e
|
||||||
CapacitorShare: d2a742baec21c8f3b92b361a2fbd2401cdd8288e
|
CapacitorShare: d2a742baec21c8f3b92b361a2fbd2401cdd8288e
|
||||||
|
CapacitorStatusBar: b16799a26320ffa52f6c8b01737d5a95bbb8f3eb
|
||||||
CapawesomeCapacitorFilePicker: c40822f0a39f86855321943c7829d52bca7f01bd
|
CapawesomeCapacitorFilePicker: c40822f0a39f86855321943c7829d52bca7f01bd
|
||||||
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
||||||
GoogleMLKit: 90ba06e028795a50261f29500d238d6061538711
|
GoogleMLKit: 90ba06e028795a50261f29500d238d6061538711
|
||||||
@@ -163,6 +169,6 @@ SPEC CHECKSUMS:
|
|||||||
SQLCipher: 31878d8ebd27e5c96db0b7cb695c96e9f8ad77da
|
SQLCipher: 31878d8ebd27e5c96db0b7cb695c96e9f8ad77da
|
||||||
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c
|
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c
|
||||||
|
|
||||||
PODFILE CHECKSUM: 60f54b19c5a7a07343ab5ba9e5db49019fd86aa0
|
PODFILE CHECKSUM: 5fa870b031c7c4e0733e2f96deaf81866c75ff7d
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
605
package-lock.json
generated
605
package-lock.json
generated
@@ -20,6 +20,7 @@
|
|||||||
"@capacitor/filesystem": "^6.0.0",
|
"@capacitor/filesystem": "^6.0.0",
|
||||||
"@capacitor/ios": "^6.2.0",
|
"@capacitor/ios": "^6.2.0",
|
||||||
"@capacitor/share": "^6.0.3",
|
"@capacitor/share": "^6.0.3",
|
||||||
|
"@capacitor/status-bar": "^6.0.2",
|
||||||
"@capawesome/capacitor-file-picker": "^6.2.0",
|
"@capawesome/capacitor-file-picker": "^6.2.0",
|
||||||
"@dicebear/collection": "^5.4.1",
|
"@dicebear/collection": "^5.4.1",
|
||||||
"@dicebear/core": "^5.4.1",
|
"@dicebear/core": "^5.4.1",
|
||||||
@@ -137,6 +138,7 @@
|
|||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
|
"serve": "^14.2.4",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"ts-jest": "^29.4.0",
|
"ts-jest": "^29.4.0",
|
||||||
"tsx": "^4.20.4",
|
"tsx": "^4.20.4",
|
||||||
@@ -2346,6 +2348,14 @@
|
|||||||
"@capacitor/core": "^6.0.0"
|
"@capacitor/core": "^6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@capacitor/status-bar": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-AmRIX6QvFemItlY7/69ARkIAqitRQqJ2qwgZmD1KqgFb78pH+XFXm1guvS/a8CuOOm/IqZ4ddDbl20yxtBqzGA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@capacitor/core": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@capawesome/capacitor-file-picker": {
|
"node_modules/@capawesome/capacitor-file-picker": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@capawesome/capacitor-file-picker/-/capacitor-file-picker-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@capawesome/capacitor-file-picker/-/capacitor-file-picker-6.2.0.tgz",
|
||||||
@@ -11751,6 +11761,13 @@
|
|||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@zeit/schemas": {
|
||||||
|
"version": "2.36.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz",
|
||||||
|
"integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@zxing/text-encoding": {
|
"node_modules/@zxing/text-encoding": {
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
|
||||||
@@ -11813,9 +11830,8 @@
|
|||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-types": "~2.1.34",
|
"mime-types": "~2.1.34",
|
||||||
"negotiator": "0.6.3"
|
"negotiator": "0.6.3"
|
||||||
@@ -11940,6 +11956,16 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-align": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-escapes": {
|
"node_modules/ansi-escapes": {
|
||||||
"version": "4.3.2",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
|
||||||
@@ -12144,6 +12170,27 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/arch": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/are-we-there-yet": {
|
"node_modules/are-we-there-yet": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
|
||||||
@@ -12940,6 +12987,153 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/boxen": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-align": "^3.0.1",
|
||||||
|
"camelcase": "^7.0.0",
|
||||||
|
"chalk": "^5.0.1",
|
||||||
|
"cli-boxes": "^3.0.0",
|
||||||
|
"string-width": "^5.1.2",
|
||||||
|
"type-fest": "^2.13.0",
|
||||||
|
"widest-line": "^4.0.1",
|
||||||
|
"wrap-ansi": "^8.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/ansi-regex": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/ansi-styles": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/camelcase": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/chalk": {
|
||||||
|
"version": "5.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
|
||||||
|
"integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/emoji-regex": {
|
||||||
|
"version": "9.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||||
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/string-width": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"eastasianwidth": "^0.2.0",
|
||||||
|
"emoji-regex": "^9.2.2",
|
||||||
|
"strip-ansi": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/strip-ansi": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/type-fest": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(MIT OR CC0-1.0)",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/wrap-ansi": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^6.1.0",
|
||||||
|
"string-width": "^5.0.1",
|
||||||
|
"strip-ansi": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bplist-creator": {
|
"node_modules/bplist-creator": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz",
|
||||||
@@ -13726,6 +13920,22 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chalk-template": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^4.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk-template?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/char-regex": {
|
"node_modules/char-regex": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
|
||||||
@@ -13950,6 +14160,19 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cli-boxes": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cli-cursor": {
|
"node_modules/cli-cursor": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||||
@@ -14006,6 +14229,24 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clipboardy": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"arch": "^2.2.0",
|
||||||
|
"execa": "^5.1.1",
|
||||||
|
"is-wsl": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cliui": {
|
"node_modules/cliui": {
|
||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||||
@@ -14172,9 +14413,8 @@
|
|||||||
"version": "2.0.18",
|
"version": "2.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||||
"integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
|
"integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": ">= 1.43.0 < 2"
|
"mime-db": ">= 1.43.0 < 2"
|
||||||
},
|
},
|
||||||
@@ -14448,6 +14688,16 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/content-disposition": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/conventional-changelog": {
|
"node_modules/conventional-changelog": {
|
||||||
"version": "3.1.25",
|
"version": "3.1.25",
|
||||||
"resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz",
|
"resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz",
|
||||||
@@ -19240,6 +19490,19 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-port-reachable": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-stream": {
|
"node_modules/is-stream": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||||
@@ -24431,9 +24694,8 @@
|
|||||||
"version": "1.54.0",
|
"version": "1.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -25897,6 +26159,13 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-is-inside": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(WTFPL OR MIT)"
|
||||||
|
},
|
||||||
"node_modules/path-key": {
|
"node_modules/path-key": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
@@ -25938,6 +26207,13 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-to-regexp": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||||
@@ -28103,6 +28379,30 @@
|
|||||||
"integrity": "sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==",
|
"integrity": "sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/registry-auth-token": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"rc": "^1.1.6",
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/registry-url": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"rc": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/regjsgen": {
|
"node_modules/regjsgen": {
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
|
||||||
@@ -29211,6 +29511,115 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/serve": {
|
||||||
|
"version": "14.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/serve/-/serve-14.2.4.tgz",
|
||||||
|
"integrity": "sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@zeit/schemas": "2.36.0",
|
||||||
|
"ajv": "8.12.0",
|
||||||
|
"arg": "5.0.2",
|
||||||
|
"boxen": "7.0.0",
|
||||||
|
"chalk": "5.0.1",
|
||||||
|
"chalk-template": "0.4.0",
|
||||||
|
"clipboardy": "3.0.0",
|
||||||
|
"compression": "1.7.4",
|
||||||
|
"is-port-reachable": "4.0.0",
|
||||||
|
"serve-handler": "6.1.6",
|
||||||
|
"update-check": "1.5.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"serve": "build/main.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-handler": {
|
||||||
|
"version": "6.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz",
|
||||||
|
"integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": "3.0.0",
|
||||||
|
"content-disposition": "0.5.2",
|
||||||
|
"mime-types": "2.1.18",
|
||||||
|
"minimatch": "3.1.2",
|
||||||
|
"path-is-inside": "1.0.2",
|
||||||
|
"path-to-regexp": "3.3.0",
|
||||||
|
"range-parser": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-handler/node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-handler/node_modules/bytes": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-handler/node_modules/mime-db": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-handler/node_modules/mime-types": {
|
||||||
|
"version": "2.1.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
|
||||||
|
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "~1.33.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-handler/node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-handler/node_modules/range-parser": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/serve-static": {
|
"node_modules/serve-static": {
|
||||||
"version": "1.16.2",
|
"version": "1.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||||
@@ -29342,6 +29751,106 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/serve/node_modules/ajv": {
|
||||||
|
"version": "8.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||||
|
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve/node_modules/bytes": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve/node_modules/chalk": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve/node_modules/compression": {
|
||||||
|
"version": "1.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
|
||||||
|
"integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "~1.3.5",
|
||||||
|
"bytes": "3.0.0",
|
||||||
|
"compressible": "~2.0.16",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"on-headers": "~1.0.2",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve/node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/serve/node_modules/ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/serve/node_modules/on-headers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/set-blocking": {
|
"node_modules/set-blocking": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
@@ -31671,6 +32180,17 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/update-check": {
|
||||||
|
"version": "1.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz",
|
||||||
|
"integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"registry-auth-token": "3.3.2",
|
||||||
|
"registry-url": "3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/uri-js": {
|
"node_modules/uri-js": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
@@ -31779,9 +32299,8 @@
|
|||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -32633,6 +33152,76 @@
|
|||||||
"string-width": "^1.0.2 || 2 || 3 || 4"
|
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/widest-line": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/widest-line/node_modules/ansi-regex": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/widest-line/node_modules/emoji-regex": {
|
||||||
|
"version": "9.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||||
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/widest-line/node_modules/string-width": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"eastasianwidth": "^0.2.0",
|
||||||
|
"emoji-regex": "^9.2.2",
|
||||||
|
"strip-ansi": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/widest-line/node_modules/strip-ansi": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wonka": {
|
"node_modules/wonka": {
|
||||||
"version": "6.3.5",
|
"version": "6.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz",
|
||||||
|
|||||||
@@ -150,6 +150,7 @@
|
|||||||
"@capacitor/filesystem": "^6.0.0",
|
"@capacitor/filesystem": "^6.0.0",
|
||||||
"@capacitor/ios": "^6.2.0",
|
"@capacitor/ios": "^6.2.0",
|
||||||
"@capacitor/share": "^6.0.3",
|
"@capacitor/share": "^6.0.3",
|
||||||
|
"@capacitor/status-bar": "^6.0.2",
|
||||||
"@capawesome/capacitor-file-picker": "^6.2.0",
|
"@capawesome/capacitor-file-picker": "^6.2.0",
|
||||||
"@dicebear/collection": "^5.4.1",
|
"@dicebear/collection": "^5.4.1",
|
||||||
"@dicebear/core": "^5.4.1",
|
"@dicebear/core": "^5.4.1",
|
||||||
@@ -267,6 +268,7 @@
|
|||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
|
"serve": "^14.2.4",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"ts-jest": "^29.4.0",
|
"ts-jest": "^29.4.0",
|
||||||
"tsx": "^4.20.4",
|
"tsx": "^4.20.4",
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ sync_capacitor() {
|
|||||||
copy_web_assets() {
|
copy_web_assets() {
|
||||||
log_info "Copying web assets to Electron"
|
log_info "Copying web assets to Electron"
|
||||||
safe_execute "Copying assets" "cp -r dist/* electron/app/"
|
safe_execute "Copying assets" "cp -r dist/* electron/app/"
|
||||||
safe_execute "Copying config" "cp capacitor.config.json electron/capacitor.config.json"
|
# Note: Electron has its own capacitor.config.ts file, so we don't copy the main config
|
||||||
}
|
}
|
||||||
|
|
||||||
# Compile TypeScript
|
# Compile TypeScript
|
||||||
|
|||||||
35
src/App.vue
35
src/App.vue
@@ -4,7 +4,7 @@
|
|||||||
<!-- Messages in the upper-right - https://github.com/emmanuelsw/notiwind -->
|
<!-- Messages in the upper-right - https://github.com/emmanuelsw/notiwind -->
|
||||||
<NotificationGroup group="alert">
|
<NotificationGroup group="alert">
|
||||||
<div
|
<div
|
||||||
class="fixed z-[120] top-[max(1rem,env(safe-area-inset-top))] right-4 left-4 sm:left-auto sm:w-full sm:max-w-sm flex flex-col items-start justify-end"
|
class="fixed z-[120] top-[max(1rem,env(safe-area-inset-top),var(--safe-area-inset-top,0px))] right-4 left-4 sm:left-auto sm:w-full sm:max-w-sm flex flex-col items-start justify-end"
|
||||||
>
|
>
|
||||||
<Notification
|
<Notification
|
||||||
v-slot="{ notifications, close }"
|
v-slot="{ notifications, close }"
|
||||||
@@ -175,7 +175,9 @@
|
|||||||
"-permission", "-mute", "-off"
|
"-permission", "-mute", "-off"
|
||||||
-->
|
-->
|
||||||
<NotificationGroup group="modal">
|
<NotificationGroup group="modal">
|
||||||
<div class="fixed z-[100] top-[env(safe-area-inset-top)] inset-x-0 w-full">
|
<div
|
||||||
|
class="fixed z-[100] top-[max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))] inset-x-0 w-full"
|
||||||
|
>
|
||||||
<Notification
|
<Notification
|
||||||
v-slot="{ notifications, close }"
|
v-slot="{ notifications, close }"
|
||||||
enter="transform ease-out duration-300 transition"
|
enter="transform ease-out duration-300 transition"
|
||||||
@@ -506,13 +508,32 @@ export default class App extends Vue {
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
#Content {
|
#Content {
|
||||||
padding-left: max(1.5rem, env(safe-area-inset-left));
|
padding-left: max(
|
||||||
padding-right: max(1.5rem, env(safe-area-inset-right));
|
1.5rem,
|
||||||
padding-top: max(1.5rem, env(safe-area-inset-top));
|
env(safe-area-inset-left),
|
||||||
padding-bottom: max(1.5rem, env(safe-area-inset-bottom));
|
var(--safe-area-inset-left, 0px)
|
||||||
|
);
|
||||||
|
padding-right: max(
|
||||||
|
1.5rem,
|
||||||
|
env(safe-area-inset-right),
|
||||||
|
var(--safe-area-inset-right, 0px)
|
||||||
|
);
|
||||||
|
padding-top: max(
|
||||||
|
1.5rem,
|
||||||
|
env(safe-area-inset-top),
|
||||||
|
var(--safe-area-inset-top, 0px)
|
||||||
|
);
|
||||||
|
padding-bottom: max(
|
||||||
|
1.5rem,
|
||||||
|
env(safe-area-inset-bottom),
|
||||||
|
var(--safe-area-inset-bottom, 0px)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#QuickNav ~ #Content {
|
#QuickNav ~ #Content {
|
||||||
padding-bottom: calc(env(safe-area-inset-bottom) + 6.333rem);
|
padding-bottom: calc(
|
||||||
|
max(env(safe-area-inset-bottom), var(--safe-area-inset-bottom, 0px)) +
|
||||||
|
6.333rem
|
||||||
|
);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ projects, and special entities with selection. * * @author Matthew Raymer */
|
|||||||
<!-- "Unnamed" entity -->
|
<!-- "Unnamed" entity -->
|
||||||
<SpecialEntityCard
|
<SpecialEntityCard
|
||||||
entity-type="unnamed"
|
entity-type="unnamed"
|
||||||
label="Unnamed"
|
:label="unnamedEntityName"
|
||||||
icon="circle-question"
|
icon="circle-question"
|
||||||
:entity-data="unnamedEntityData"
|
:entity-data="unnamedEntityData"
|
||||||
:notify="notify"
|
:notify="notify"
|
||||||
@@ -83,6 +83,7 @@ import ShowAllCard from "./ShowAllCard.vue";
|
|||||||
import { Contact } from "../db/tables/contacts";
|
import { Contact } from "../db/tables/contacts";
|
||||||
import { PlanData } from "../interfaces/records";
|
import { PlanData } from "../interfaces/records";
|
||||||
import { NotificationIface } from "../constants/app";
|
import { NotificationIface } from "../constants/app";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EntityGrid - Unified grid layout for displaying people or projects
|
* EntityGrid - Unified grid layout for displaying people or projects
|
||||||
@@ -159,6 +160,10 @@ export default class EntityGrid extends Vue {
|
|||||||
@Prop({ default: "other party" })
|
@Prop({ default: "other party" })
|
||||||
conflictContext!: string;
|
conflictContext!: string;
|
||||||
|
|
||||||
|
/** Whether to hide the "Show All" navigation */
|
||||||
|
@Prop({ default: false })
|
||||||
|
hideShowAll!: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to determine which entities to display (allows parent control)
|
* Function to determine which entities to display (allows parent control)
|
||||||
*
|
*
|
||||||
@@ -245,7 +250,9 @@ export default class EntityGrid extends Vue {
|
|||||||
* Whether to show the "Show All" navigation
|
* Whether to show the "Show All" navigation
|
||||||
*/
|
*/
|
||||||
get shouldShowAll(): boolean {
|
get shouldShowAll(): boolean {
|
||||||
return this.entities.length > 0 && this.showAllRoute !== "";
|
return (
|
||||||
|
!this.hideShowAll && this.entities.length > 0 && this.showAllRoute !== ""
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -271,10 +278,17 @@ export default class EntityGrid extends Vue {
|
|||||||
get unnamedEntityData(): { did: string; name: string } {
|
get unnamedEntityData(): { did: string; name: string } {
|
||||||
return {
|
return {
|
||||||
did: "",
|
did: "",
|
||||||
name: "Unnamed",
|
name: UNNAMED_ENTITY_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed entity name constant
|
||||||
|
*/
|
||||||
|
get unnamedEntityName(): string {
|
||||||
|
return UNNAMED_ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a person DID is conflicted
|
* Check if a person DID is conflicted
|
||||||
*/
|
*/
|
||||||
@@ -304,16 +318,13 @@ export default class EntityGrid extends Vue {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle special entity selection from SpecialEntityCard
|
* Handle special entity selection from SpecialEntityCard
|
||||||
|
* Treat "You" and "Unnamed" as person entities
|
||||||
*/
|
*/
|
||||||
handleEntitySelected(event: {
|
handleEntitySelected(event: { data: { did?: string; name: string } }): void {
|
||||||
type: string;
|
// Convert special entities to person entities since they represent people
|
||||||
entityType: string;
|
|
||||||
data: { did?: string; name: string };
|
|
||||||
}): void {
|
|
||||||
this.emitEntitySelected({
|
this.emitEntitySelected({
|
||||||
type: "special",
|
type: "person",
|
||||||
entityType: event.entityType,
|
data: event.data as Contact,
|
||||||
data: event.data,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,13 +332,11 @@ export default class EntityGrid extends Vue {
|
|||||||
|
|
||||||
@Emit("entity-selected")
|
@Emit("entity-selected")
|
||||||
emitEntitySelected(data: {
|
emitEntitySelected(data: {
|
||||||
type: "person" | "project" | "special";
|
type: "person" | "project";
|
||||||
entityType?: string;
|
data: Contact | PlanData;
|
||||||
data: Contact | PlanData | { did?: string; name: string };
|
|
||||||
}): {
|
}): {
|
||||||
type: "person" | "project" | "special";
|
type: "person" | "project";
|
||||||
entityType?: string;
|
data: Contact | PlanData;
|
||||||
data: Contact | PlanData | { did?: string; name: string };
|
|
||||||
} {
|
} {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Matthew Raymer */
|
|||||||
:show-all-query-params="showAllQueryParams"
|
:show-all-query-params="showAllQueryParams"
|
||||||
:notify="notify"
|
:notify="notify"
|
||||||
:conflict-context="conflictContext"
|
:conflict-context="conflictContext"
|
||||||
|
:hide-show-all="hideShowAll"
|
||||||
@entity-selected="handleEntitySelected"
|
@entity-selected="handleEntitySelected"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -55,9 +56,8 @@ interface EntityData {
|
|||||||
* Entity selection event data structure
|
* Entity selection event data structure
|
||||||
*/
|
*/
|
||||||
interface EntitySelectionEvent {
|
interface EntitySelectionEvent {
|
||||||
type: "person" | "project" | "special";
|
type: "person" | "project";
|
||||||
entityType?: string;
|
data: Contact | PlanData;
|
||||||
data: Contact | PlanData | EntityData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,6 +154,10 @@ export default class EntitySelectionStep extends Vue {
|
|||||||
@Prop()
|
@Prop()
|
||||||
notify?: (notification: NotificationIface, timeout?: number) => void;
|
notify?: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
|
/** Whether to hide the "Show All" navigation */
|
||||||
|
@Prop({ default: false })
|
||||||
|
hideShowAll!: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS classes for the cancel button
|
* CSS classes for the cancel button
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ computed CSS properties * * @author Matthew Raymer */
|
|||||||
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</p>
|
</p>
|
||||||
<h3 class="font-semibold truncate">
|
<h3 :class="nameClasses">
|
||||||
{{ entity?.name || "Unnamed" }}
|
{{ displayName }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@ import { Component, Prop, Vue } from "vue-facing-decorator";
|
|||||||
import EntityIcon from "./EntityIcon.vue";
|
import EntityIcon from "./EntityIcon.vue";
|
||||||
import ProjectIcon from "./ProjectIcon.vue";
|
import ProjectIcon from "./ProjectIcon.vue";
|
||||||
import { Contact } from "../db/tables/contacts";
|
import { Contact } from "../db/tables/contacts";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity interface for both person and project entities
|
* Entity interface for both person and project entities
|
||||||
@@ -138,6 +139,38 @@ export default class EntitySummaryButton extends Vue {
|
|||||||
return this.editable ? "text-blue-500" : "text-slate-400";
|
return this.editable ? "text-blue-500" : "text-slate-400";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed CSS classes for the entity name
|
||||||
|
*/
|
||||||
|
get nameClasses(): string {
|
||||||
|
const baseClasses = "font-semibold truncate";
|
||||||
|
|
||||||
|
// Add italic styling for special "Unnamed" or entities without set names
|
||||||
|
if (!this.entity?.name || this.entity?.did === "") {
|
||||||
|
return `${baseClasses} italic text-slate-500`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed display name for the entity
|
||||||
|
*/
|
||||||
|
get displayName(): string {
|
||||||
|
// If the entity has a set name, use that name
|
||||||
|
if (this.entity?.name) {
|
||||||
|
return this.entity.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entity is the special "Unnamed", use "Unnamed"
|
||||||
|
if (this.entity?.did === "") {
|
||||||
|
return UNNAMED_ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entity does not have a set name, but is not the special "Unnamed", use their DID
|
||||||
|
return this.entity?.did;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle click event - only call function prop if editable
|
* Handle click event - only call function prop if editable
|
||||||
* Allows parent to control edit behavior and validation
|
* Allows parent to control edit behavior and validation
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
:unit-code="unitCode"
|
:unit-code="unitCode"
|
||||||
:offer-id="offerId"
|
:offer-id="offerId"
|
||||||
:notify="$notify"
|
:notify="$notify"
|
||||||
|
:hide-show-all="hideShowAll"
|
||||||
@entity-selected="handleEntitySelected"
|
@entity-selected="handleEntitySelected"
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
/>
|
/>
|
||||||
@@ -87,6 +88,7 @@ import {
|
|||||||
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER,
|
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER,
|
||||||
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE,
|
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
@@ -114,6 +116,7 @@ export default class GiftedDialog extends Vue {
|
|||||||
@Prop() fromProjectId = "";
|
@Prop() fromProjectId = "";
|
||||||
@Prop() toProjectId = "";
|
@Prop() toProjectId = "";
|
||||||
@Prop() isFromProjectView = false;
|
@Prop() isFromProjectView = false;
|
||||||
|
@Prop() hideShowAll = false;
|
||||||
@Prop({ default: "person" }) giverEntityType = "person" as
|
@Prop({ default: "person" }) giverEntityType = "person" as
|
||||||
| "person"
|
| "person"
|
||||||
| "project";
|
| "project";
|
||||||
@@ -224,15 +227,6 @@ export default class GiftedDialog extends Vue {
|
|||||||
|
|
||||||
this.allMyDids = await retrieveAccountDids();
|
this.allMyDids = await retrieveAccountDids();
|
||||||
|
|
||||||
if (this.giver && !this.giver.name) {
|
|
||||||
this.giver.name = didInfo(
|
|
||||||
this.giver.did,
|
|
||||||
this.activeDid,
|
|
||||||
this.allMyDids,
|
|
||||||
this.allContacts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.giverEntityType === "project" ||
|
this.giverEntityType === "project" ||
|
||||||
this.recipientEntityType === "project"
|
this.recipientEntityType === "project"
|
||||||
@@ -455,14 +449,14 @@ export default class GiftedDialog extends Vue {
|
|||||||
if (contact) {
|
if (contact) {
|
||||||
this.giver = {
|
this.giver = {
|
||||||
did: contact.did,
|
did: contact.did,
|
||||||
name: contact.name || contact.did,
|
name: contact.name,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Only set to "Unnamed" if no giver is currently set
|
// Only set to "Unnamed" if no giver is currently set
|
||||||
if (!this.giver || !this.giver.did) {
|
if (!this.giver || !this.giver.did) {
|
||||||
this.giver = {
|
this.giver = {
|
||||||
did: "",
|
did: "",
|
||||||
name: "Unnamed",
|
name: UNNAMED_ENTITY_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,14 +511,14 @@ export default class GiftedDialog extends Vue {
|
|||||||
if (contact) {
|
if (contact) {
|
||||||
this.receiver = {
|
this.receiver = {
|
||||||
did: contact.did,
|
did: contact.did,
|
||||||
name: contact.name || contact.did,
|
name: contact.name,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Only set to "Unnamed" if no receiver is currently set
|
// Only set to "Unnamed" if no receiver is currently set
|
||||||
if (!this.receiver || !this.receiver.did) {
|
if (!this.receiver || !this.receiver.did) {
|
||||||
this.receiver = {
|
this.receiver = {
|
||||||
did: "",
|
did: "",
|
||||||
name: "Unnamed",
|
name: UNNAMED_ENTITY_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -566,20 +560,21 @@ export default class GiftedDialog extends Vue {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle entity selection from EntitySelectionStep
|
* Handle entity selection from EntitySelectionStep
|
||||||
* @param entity - The selected entity (person, project, or special) with stepType
|
* @param entity - The selected entity (person or project) with stepType
|
||||||
*/
|
*/
|
||||||
handleEntitySelected(entity: {
|
handleEntitySelected(entity: {
|
||||||
type: "person" | "project" | "special";
|
type: "person" | "project";
|
||||||
entityType?: string;
|
data: Contact | PlanData;
|
||||||
data: Contact | PlanData | { did?: string; name: string };
|
|
||||||
stepType: string;
|
stepType: string;
|
||||||
}) {
|
}) {
|
||||||
if (entity.type === "person") {
|
if (entity.type === "person") {
|
||||||
const contact = entity.data as Contact;
|
const contact = entity.data as Contact;
|
||||||
|
// Apply DID-based logic for person entities
|
||||||
|
const processedContact = this.processPersonEntity(contact);
|
||||||
if (entity.stepType === "giver") {
|
if (entity.stepType === "giver") {
|
||||||
this.selectGiver(contact);
|
this.selectGiver(processedContact);
|
||||||
} else {
|
} else {
|
||||||
this.selectRecipient(contact);
|
this.selectRecipient(processedContact);
|
||||||
}
|
}
|
||||||
} else if (entity.type === "project") {
|
} else if (entity.type === "project") {
|
||||||
const project = entity.data as PlanData;
|
const project = entity.data as PlanData;
|
||||||
@@ -588,33 +583,22 @@ export default class GiftedDialog extends Vue {
|
|||||||
} else {
|
} else {
|
||||||
this.selectRecipientProject(project);
|
this.selectRecipientProject(project);
|
||||||
}
|
}
|
||||||
} else if (entity.type === "special") {
|
}
|
||||||
// Handle special entities like "You" and "Unnamed"
|
}
|
||||||
if (entity.entityType === "you") {
|
|
||||||
// "You" entity selected
|
/**
|
||||||
const youEntity = {
|
* Processes person entities using DID-based logic for "You" and "Unnamed"
|
||||||
did: this.activeDid,
|
*/
|
||||||
name: "You",
|
private processPersonEntity(contact: Contact): Contact {
|
||||||
};
|
if (contact.did === this.activeDid) {
|
||||||
if (entity.stepType === "giver") {
|
// If DID matches active DID, create "You" entity
|
||||||
this.giver = youEntity;
|
return { ...contact, name: "You" };
|
||||||
} else {
|
} else if (!contact.did || contact.did === "") {
|
||||||
this.receiver = youEntity;
|
// If DID is empty/null, create "Unnamed" entity
|
||||||
}
|
return { ...contact, name: UNNAMED_ENTITY_NAME };
|
||||||
this.firstStep = false;
|
} else {
|
||||||
} else if (entity.entityType === "unnamed") {
|
// Return the contact as-is
|
||||||
// "Unnamed" entity selected
|
return contact;
|
||||||
const unnamedEntity = {
|
|
||||||
did: "",
|
|
||||||
name: "Unnamed",
|
|
||||||
};
|
|
||||||
if (entity.stepType === "giver") {
|
|
||||||
this.giver = unnamedEntity;
|
|
||||||
} else {
|
|
||||||
this.receiver = unnamedEntity;
|
|
||||||
}
|
|
||||||
this.firstStep = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h3 class="text-lg font-medium">
|
<h3 class="text-lg font-medium">
|
||||||
{{ member.name || "Unnamed Member" }}
|
{{ member.name || unnamedMember }}
|
||||||
</h3>
|
</h3>
|
||||||
<div
|
<div
|
||||||
v-if="!getContactFor(member.did) && member.did !== activeDid"
|
v-if="!getContactFor(member.did) && member.did !== activeDid"
|
||||||
@@ -177,6 +177,7 @@ import {
|
|||||||
NOTIFY_ADD_CONTACT_FIRST,
|
NOTIFY_ADD_CONTACT_FIRST,
|
||||||
NOTIFY_CONTINUE_WITHOUT_ADDING,
|
NOTIFY_CONTINUE_WITHOUT_ADDING,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||||
|
|
||||||
interface Member {
|
interface Member {
|
||||||
admitted: boolean;
|
admitted: boolean;
|
||||||
@@ -220,6 +221,13 @@ export default class MembersList extends Vue {
|
|||||||
apiServer = "";
|
apiServer = "";
|
||||||
contacts: Array<Contact> = [];
|
contacts: Array<Contact> = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed member constant
|
||||||
|
*/
|
||||||
|
get unnamedMember(): string {
|
||||||
|
return SOMEONE_UNNAMED;
|
||||||
|
}
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
this.notify = createNotifyHelpers(this.$notify);
|
this.notify = createNotifyHelpers(this.$notify);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ conflict detection. * * @author Matthew Raymer */
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 :class="nameClasses">
|
<h3 :class="nameClasses">
|
||||||
{{ person.name || person.did || "Unnamed" }}
|
{{ displayName }}
|
||||||
</h3>
|
</h3>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
@@ -98,9 +98,27 @@ export default class PersonCard extends Vue {
|
|||||||
return `${baseClasses} text-slate-400`;
|
return `${baseClasses} text-slate-400`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add italic styling for entities without set names
|
||||||
|
if (!this.person.name) {
|
||||||
|
return `${baseClasses} italic text-slate-500`;
|
||||||
|
}
|
||||||
|
|
||||||
return baseClasses;
|
return baseClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed display name for the person
|
||||||
|
*/
|
||||||
|
get displayName(): string {
|
||||||
|
// If the entity has a set name, use that name
|
||||||
|
if (this.person.name) {
|
||||||
|
return this.person.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entity does not have a set name
|
||||||
|
return this.person.did;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle card click - emit if selectable and not conflicted, show warning if conflicted
|
* Handle card click - emit if selectable and not conflicted, show warning if conflicted
|
||||||
*/
|
*/
|
||||||
@@ -114,7 +132,7 @@ export default class PersonCard extends Vue {
|
|||||||
group: "alert",
|
group: "alert",
|
||||||
type: "warning",
|
type: "warning",
|
||||||
title: "Cannot Select",
|
title: "Cannot Select",
|
||||||
text: `You cannot select "${this.person.name || this.person.did || "Unnamed"}" because they are already selected as the ${this.conflictContext}.`,
|
text: `You cannot select "${this.displayName}" because they are already selected as the ${this.conflictContext}.`,
|
||||||
},
|
},
|
||||||
3000,
|
3000,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Comprehensive error handling * * @author Matthew Raymer * @version 1.0.0 * @file
|
|||||||
PhotoDialog.vue */
|
PhotoDialog.vue */
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="visible" class="dialog-overlay>
|
<div v-if="visible" class="dialog-overlay">
|
||||||
<div class="dialog relative">
|
<div class="dialog relative">
|
||||||
<div class="text-lg text-center font-light relative z-50">
|
<div class="text-lg text-center font-light relative z-50">
|
||||||
<div id="ViewHeading" :class="headingClasses">
|
<div id="ViewHeading" :class="headingClasses">
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ issuer information. * * @author Matthew Raymer */
|
|||||||
<h3
|
<h3
|
||||||
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
|
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
|
||||||
>
|
>
|
||||||
{{ project.name || "Unnamed Project" }}
|
{{ project.name || unnamedProject }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="text-xs text-slate-500 truncate">
|
<div class="text-xs text-slate-500 truncate">
|
||||||
@@ -31,6 +31,7 @@ import ProjectIcon from "./ProjectIcon.vue";
|
|||||||
import { PlanData } from "../interfaces/records";
|
import { PlanData } from "../interfaces/records";
|
||||||
import { Contact } from "../db/tables/contacts";
|
import { Contact } from "../db/tables/contacts";
|
||||||
import { didInfo } from "../libs/endorserServer";
|
import { didInfo } from "../libs/endorserServer";
|
||||||
|
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProjectCard - Displays a project entity with selection capability
|
* ProjectCard - Displays a project entity with selection capability
|
||||||
@@ -63,6 +64,13 @@ export default class ProjectCard extends Vue {
|
|||||||
@Prop({ required: true })
|
@Prop({ required: true })
|
||||||
allContacts!: Contact[];
|
allContacts!: Contact[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed project constant
|
||||||
|
*/
|
||||||
|
get unnamedProject(): string {
|
||||||
|
return UNNAMED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed display name for the project issuer
|
* Computed display name for the project issuer
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ import { urlBase64ToUint8Array } from "../libs/crypto/vc/util";
|
|||||||
import * as libsUtil from "../libs/util";
|
import * as libsUtil from "../libs/util";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
// Example interface for error
|
// Example interface for error
|
||||||
interface ErrorResponse {
|
interface ErrorResponse {
|
||||||
@@ -602,7 +603,7 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
* Returns the default message for direct push
|
* Returns the default message for direct push
|
||||||
*/
|
*/
|
||||||
get notificationMessagePlaceholder(): string {
|
get notificationMessagePlaceholder(): string {
|
||||||
return "Click to share some gratitude with the world -- even if they're unnamed.";
|
return `Click to share some gratitude with the world -- even if they're ${UNNAMED_ENTITY_NAME.toLowerCase()}.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<!-- QUICK NAV -->
|
<!-- QUICK NAV -->
|
||||||
<nav
|
<nav
|
||||||
id="QuickNav"
|
id="QuickNav"
|
||||||
class="fixed bottom-0 left-0 right-0 bg-slate-200 z-50 pb-[env(safe-area-inset-bottom)]"
|
class="fixed bottom-0 left-0 right-0 bg-slate-200 z-50 pb-[max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px))]"
|
||||||
>
|
>
|
||||||
<ul class="flex text-2xl px-6 py-2 gap-1 max-w-3xl mx-auto">
|
<ul class="flex text-2xl px-6 py-2 gap-1 max-w-3xl mx-auto">
|
||||||
<!-- Home Feed -->
|
<!-- Home Feed -->
|
||||||
|
|||||||
@@ -124,8 +124,6 @@ export default class SpecialEntityCard extends Vue {
|
|||||||
handleClick(): void {
|
handleClick(): void {
|
||||||
if (this.selectable && !this.conflicted) {
|
if (this.selectable && !this.conflicted) {
|
||||||
this.emitEntitySelected({
|
this.emitEntitySelected({
|
||||||
type: "special",
|
|
||||||
entityType: this.entityType,
|
|
||||||
data: this.entityData,
|
data: this.entityData,
|
||||||
});
|
});
|
||||||
} else if (this.conflicted && this.notify) {
|
} else if (this.conflicted && this.notify) {
|
||||||
@@ -145,13 +143,7 @@ export default class SpecialEntityCard extends Vue {
|
|||||||
// Emit methods using @Emit decorator
|
// Emit methods using @Emit decorator
|
||||||
|
|
||||||
@Emit("entity-selected")
|
@Emit("entity-selected")
|
||||||
emitEntitySelected(data: {
|
emitEntitySelected(data: { data: { did?: string; name: string } }): {
|
||||||
type: string;
|
|
||||||
entityType: string;
|
|
||||||
data: { did?: string; name: string };
|
|
||||||
}): {
|
|
||||||
type: string;
|
|
||||||
entityType: string;
|
|
||||||
data: { did?: string; name: string };
|
data: { did?: string; name: string };
|
||||||
} {
|
} {
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="absolute right-5 top-[max(0.75rem,env(safe-area-inset-top))]">
|
<div
|
||||||
|
class="absolute right-5 top-[max(0.75rem,env(safe-area-inset-top),var(--safe-area-inset-top,0px))]"
|
||||||
|
>
|
||||||
<span class="align-center text-red-500 mr-2">{{ message }}</span>
|
<span class="align-center text-red-500 mr-2">{{ message }}</span>
|
||||||
<span class="ml-2">
|
<span class="ml-2">
|
||||||
<router-link
|
<router-link
|
||||||
|
|||||||
14
src/constants/entities.ts
Normal file
14
src/constants/entities.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Constants for entity-related strings, particularly for unnamed/unknown person entities
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Core unnamed entity names
|
||||||
|
export const UNNAMED_ENTITY_NAME = "Unnamed";
|
||||||
|
|
||||||
|
// Descriptive phrases for unnamed entities
|
||||||
|
export const SOMEONE_UNNAMED = "Someone Unnamed";
|
||||||
|
export const THAT_UNNAMED_PERSON = "That unnamed person";
|
||||||
|
export const UNNAMED_PERSON = "unnamed person";
|
||||||
|
|
||||||
|
// Project-related unnamed entities
|
||||||
|
export const UNNAMED_PROJECT = "Unnamed Project";
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { THAT_UNNAMED_PERSON } from "./entities";
|
||||||
|
|
||||||
// Notification message constants for user-facing notifications
|
// Notification message constants for user-facing notifications
|
||||||
// Add new notification messages here as needed
|
// Add new notification messages here as needed
|
||||||
@@ -873,7 +874,7 @@ export const NOTIFY_CONTACT_LINK_COPIED = {
|
|||||||
// Template for registration success message
|
// Template for registration success message
|
||||||
// Used in: ContactsView.vue (register method - registration success with contact name)
|
// Used in: ContactsView.vue (register method - registration success with contact name)
|
||||||
export const getRegisterPersonSuccessMessage = (name?: string): string =>
|
export const getRegisterPersonSuccessMessage = (name?: string): string =>
|
||||||
`${name || "That unnamed person"} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`;
|
`${name || THAT_UNNAMED_PERSON} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`;
|
||||||
|
|
||||||
// Template for visibility success message
|
// Template for visibility success message
|
||||||
// Used in: ContactsView.vue (setVisibility method - visibility success with contact name)
|
// Used in: ContactsView.vue (setVisibility method - visibility success with contact name)
|
||||||
@@ -1378,7 +1379,7 @@ export function createQRContactAddedMessage(hasVisibility: boolean): string {
|
|||||||
export function createQRRegistrationSuccessMessage(
|
export function createQRRegistrationSuccessMessage(
|
||||||
contactName: string,
|
contactName: string,
|
||||||
): string {
|
): string {
|
||||||
return `${contactName || "That unnamed person"}${NOTIFY_QR_REGISTRATION_SUCCESS.message}`;
|
return `${contactName || THAT_UNNAMED_PERSON}${NOTIFY_QR_REGISTRATION_SUCCESS.message}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContactQRScanShowView.vue timeout constants
|
// ContactQRScanShowView.vue timeout constants
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import { PlanSummaryRecord } from "../interfaces/records";
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||||
import { APP_SERVER } from "@/constants/app";
|
import { APP_SERVER } from "@/constants/app";
|
||||||
|
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard context for schema.org data
|
* Standard context for schema.org data
|
||||||
@@ -309,7 +310,7 @@ export function didInfoForContact(
|
|||||||
showDidForVisible: boolean = false,
|
showDidForVisible: boolean = false,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
): { known: boolean; displayName: string; profileImageUrl?: string } {
|
): { known: boolean; displayName: string; profileImageUrl?: string } {
|
||||||
if (!did) return { displayName: "Someone Not Named", known: false };
|
if (!did) return { displayName: SOMEONE_UNNAMED, known: false };
|
||||||
if (did === activeDid) {
|
if (did === activeDid) {
|
||||||
return { displayName: "You", known: true };
|
return { displayName: "You", known: true };
|
||||||
} else if (contact) {
|
} else if (contact) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { logger } from "../utils/logger";
|
|||||||
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
|
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
|
||||||
import { IIdentifier } from "@veramo/core";
|
import { IIdentifier } from "@veramo/core";
|
||||||
import { DEFAULT_ROOT_DERIVATION_PATH } from "./crypto";
|
import { DEFAULT_ROOT_DERIVATION_PATH } from "./crypto";
|
||||||
|
import { UNNAMED_PERSON } from "@/constants/entities";
|
||||||
|
|
||||||
// Consolidate this with src/utils/PlatformServiceMixin.mapQueryResultToValues
|
// Consolidate this with src/utils/PlatformServiceMixin.mapQueryResultToValues
|
||||||
function mapQueryResultToValues(
|
function mapQueryResultToValues(
|
||||||
@@ -192,7 +193,7 @@ export const nameForContact = (
|
|||||||
): string => {
|
): string => {
|
||||||
return (
|
return (
|
||||||
(contact?.name as string) ||
|
(contact?.name as string) ||
|
||||||
(capitalize ? "This" : "this") + " unnamed user"
|
(capitalize ? "This" : "this") + " " + UNNAMED_PERSON
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { handleApiError } from "./services/api";
|
|||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { DeepLinkHandler } from "./services/deepLinks";
|
import { DeepLinkHandler } from "./services/deepLinks";
|
||||||
import { logger, safeStringify } from "./utils/logger";
|
import { logger, safeStringify } from "./utils/logger";
|
||||||
|
import "./utils/safeAreaInset";
|
||||||
|
|
||||||
logger.log("[Capacitor] 🚀 Starting initialization");
|
logger.log("[Capacitor] 🚀 Starting initialization");
|
||||||
logger.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
|
logger.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
|
||||||
|
|||||||
@@ -74,13 +74,13 @@ export class DeepLinkHandler {
|
|||||||
* @throws {DeepLinkError} If validation fails or route is invalid
|
* @throws {DeepLinkError} If validation fails or route is invalid
|
||||||
*/
|
*/
|
||||||
async handleDeepLink(url: string): Promise<void> {
|
async handleDeepLink(url: string): Promise<void> {
|
||||||
logger.info(`[DeepLink] 🚀 Starting deeplink processing for URL: ${url}`);
|
logger.debug(`[DeepLink] 🚀 Starting deeplink processing for URL: ${url}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info(`[DeepLink] 📍 Parsing URL: ${url}`);
|
logger.debug(`[DeepLink] 📍 Parsing URL: ${url}`);
|
||||||
const { path, params, query } = this.parseDeepLink(url);
|
const { path, params, query } = this.parseDeepLink(url);
|
||||||
|
|
||||||
logger.info(`[DeepLink] ✅ URL parsed successfully:`, {
|
logger.debug(`[DeepLink] ✅ URL parsed successfully:`, {
|
||||||
path,
|
path,
|
||||||
params: Object.keys(params),
|
params: Object.keys(params),
|
||||||
query: Object.keys(query),
|
query: Object.keys(query),
|
||||||
@@ -93,10 +93,10 @@ export class DeepLinkHandler {
|
|||||||
Object.entries(params).map(([key, value]) => [key, value ?? ""]),
|
Object.entries(params).map(([key, value]) => [key, value ?? ""]),
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.info(`[DeepLink] 🧹 Parameters sanitized:`, sanitizedParams);
|
logger.debug(`[DeepLink] 🧹 Parameters sanitized:`, sanitizedParams);
|
||||||
|
|
||||||
await this.validateAndRoute(path, sanitizedParams, query);
|
await this.validateAndRoute(path, sanitizedParams, query);
|
||||||
logger.info(`[DeepLink] 🎯 Deeplink processing completed successfully`);
|
logger.debug(`[DeepLink] 🎯 Deeplink processing completed successfully`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[DeepLink] ❌ Deeplink processing failed:`, {
|
logger.error(`[DeepLink] ❌ Deeplink processing failed:`, {
|
||||||
url,
|
url,
|
||||||
@@ -159,7 +159,7 @@ export class DeepLinkHandler {
|
|||||||
logger.debug(`[DeepLink] 🔗 Query parameters extracted:`, query);
|
logger.debug(`[DeepLink] 🔗 Query parameters extracted:`, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`[DeepLink] ✅ Parse completed:`, {
|
logger.debug(`[DeepLink] ✅ Parse completed:`, {
|
||||||
routePath,
|
routePath,
|
||||||
pathParams: pathParams.length,
|
pathParams: pathParams.length,
|
||||||
queryParams: Object.keys(query).length,
|
queryParams: Object.keys(query).length,
|
||||||
@@ -186,7 +186,7 @@ export class DeepLinkHandler {
|
|||||||
params: Record<string, string>,
|
params: Record<string, string>,
|
||||||
query: Record<string, string>,
|
query: Record<string, string>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
logger.info(
|
logger.debug(
|
||||||
`[DeepLink] 🎯 Starting validation and routing for path: ${path}`,
|
`[DeepLink] 🎯 Starting validation and routing for path: ${path}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -197,11 +197,11 @@ export class DeepLinkHandler {
|
|||||||
logger.debug(`[DeepLink] 🔍 Validating route path: ${path}`);
|
logger.debug(`[DeepLink] 🔍 Validating route path: ${path}`);
|
||||||
// Validate route exists
|
// Validate route exists
|
||||||
const validRoute = routeSchema.parse(path) as DeepLinkRoute;
|
const validRoute = routeSchema.parse(path) as DeepLinkRoute;
|
||||||
logger.info(`[DeepLink] ✅ Route validation passed: ${validRoute}`);
|
logger.debug(`[DeepLink] ✅ Route validation passed: ${validRoute}`);
|
||||||
|
|
||||||
// Get route configuration
|
// Get route configuration
|
||||||
const routeConfig = ROUTE_MAP[validRoute];
|
const routeConfig = ROUTE_MAP[validRoute];
|
||||||
logger.info(`[DeepLink] 📋 Route config retrieved:`, routeConfig);
|
logger.debug(`[DeepLink] 📋 Route config retrieved:`, routeConfig);
|
||||||
|
|
||||||
if (!routeConfig) {
|
if (!routeConfig) {
|
||||||
logger.error(`[DeepLink] ❌ No route config found for: ${validRoute}`);
|
logger.error(`[DeepLink] ❌ No route config found for: ${validRoute}`);
|
||||||
@@ -209,7 +209,7 @@ export class DeepLinkHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
routeName = routeConfig.name;
|
routeName = routeConfig.name;
|
||||||
logger.info(`[DeepLink] 🎯 Route name resolved: ${routeName}`);
|
logger.debug(`[DeepLink] 🎯 Route name resolved: ${routeName}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[DeepLink] ❌ Route validation failed:`, {
|
logger.error(`[DeepLink] ❌ Route validation failed:`, {
|
||||||
path,
|
path,
|
||||||
@@ -228,14 +228,14 @@ export class DeepLinkHandler {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.debug(
|
||||||
`[DeepLink] 🔄 Redirected to error page for invalid route: ${path}`,
|
`[DeepLink] 🔄 Redirected to error page for invalid route: ${path}`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue with parameter validation
|
// Continue with parameter validation
|
||||||
logger.info(
|
logger.debug(
|
||||||
`[DeepLink] 🔍 Starting parameter validation for route: ${routeName}`,
|
`[DeepLink] 🔍 Starting parameter validation for route: ${routeName}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@ export class DeepLinkHandler {
|
|||||||
if (pathSchema) {
|
if (pathSchema) {
|
||||||
logger.debug(`[DeepLink] 🔍 Validating path parameters:`, params);
|
logger.debug(`[DeepLink] 🔍 Validating path parameters:`, params);
|
||||||
validatedPathParams = await pathSchema.parseAsync(params);
|
validatedPathParams = await pathSchema.parseAsync(params);
|
||||||
logger.info(
|
logger.debug(
|
||||||
`[DeepLink] ✅ Path parameters validated:`,
|
`[DeepLink] ✅ Path parameters validated:`,
|
||||||
validatedPathParams,
|
validatedPathParams,
|
||||||
);
|
);
|
||||||
@@ -270,7 +270,7 @@ export class DeepLinkHandler {
|
|||||||
if (querySchema) {
|
if (querySchema) {
|
||||||
logger.debug(`[DeepLink] 🔍 Validating query parameters:`, query);
|
logger.debug(`[DeepLink] 🔍 Validating query parameters:`, query);
|
||||||
validatedQueryParams = await querySchema.parseAsync(query);
|
validatedQueryParams = await querySchema.parseAsync(query);
|
||||||
logger.info(
|
logger.debug(
|
||||||
`[DeepLink] ✅ Query parameters validated:`,
|
`[DeepLink] ✅ Query parameters validated:`,
|
||||||
validatedQueryParams,
|
validatedQueryParams,
|
||||||
);
|
);
|
||||||
@@ -299,7 +299,7 @@ export class DeepLinkHandler {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.debug(
|
||||||
`[DeepLink] 🔄 Redirected to error page for invalid parameters`,
|
`[DeepLink] 🔄 Redirected to error page for invalid parameters`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -307,7 +307,7 @@ export class DeepLinkHandler {
|
|||||||
|
|
||||||
// Attempt navigation
|
// Attempt navigation
|
||||||
try {
|
try {
|
||||||
logger.info(`[DeepLink] 🚀 Attempting navigation:`, {
|
logger.debug(`[DeepLink] 🚀 Attempting navigation:`, {
|
||||||
routeName,
|
routeName,
|
||||||
pathParams: validatedPathParams,
|
pathParams: validatedPathParams,
|
||||||
queryParams: validatedQueryParams,
|
queryParams: validatedQueryParams,
|
||||||
@@ -319,7 +319,7 @@ export class DeepLinkHandler {
|
|||||||
query: validatedQueryParams,
|
query: validatedQueryParams,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(`[DeepLink] ✅ Navigation successful to: ${routeName}`);
|
logger.debug(`[DeepLink] ✅ Navigation successful to: ${routeName}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[DeepLink] ❌ Navigation failed:`, {
|
logger.error(`[DeepLink] ❌ Navigation failed:`, {
|
||||||
routeName,
|
routeName,
|
||||||
@@ -342,7 +342,7 @@ export class DeepLinkHandler {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.debug(
|
||||||
`[DeepLink] 🔄 Redirected to error page for navigation failure`,
|
`[DeepLink] 🔄 Redirected to error page for navigation failure`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
226
src/utils/safeAreaInset.js
Normal file
226
src/utils/safeAreaInset.js
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
/**
|
||||||
|
* Safe Area Inset Injection for Android WebView
|
||||||
|
*
|
||||||
|
* This script injects safe area inset values into CSS environment variables
|
||||||
|
* when running in Android WebView, since Android doesn't natively support
|
||||||
|
* CSS env(safe-area-inset-*) variables like iOS does.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check if we're running in Android WebView with Capacitor
|
||||||
|
const isAndroidWebView = () => {
|
||||||
|
// Check if we're on iOS - if so, skip this script entirely
|
||||||
|
const isIOS =
|
||||||
|
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
||||||
|
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
||||||
|
|
||||||
|
if (isIOS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're on Android
|
||||||
|
const isAndroid = /Android/.test(navigator.userAgent);
|
||||||
|
|
||||||
|
// Check if we have Capacitor (required for Android WebView)
|
||||||
|
const hasCapacitor = window.Capacitor !== undefined;
|
||||||
|
|
||||||
|
// Only run on Android with Capacitor
|
||||||
|
return isAndroid && hasCapacitor;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wait for Capacitor to be available
|
||||||
|
const waitForCapacitor = () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (window.Capacitor) {
|
||||||
|
resolve(window.Capacitor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for Capacitor to be available
|
||||||
|
const checkCapacitor = () => {
|
||||||
|
if (window.Capacitor) {
|
||||||
|
resolve(window.Capacitor);
|
||||||
|
} else {
|
||||||
|
setTimeout(checkCapacitor, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkCapacitor();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inject safe area inset values into CSS custom properties
|
||||||
|
const injectSafeAreaInsets = async () => {
|
||||||
|
try {
|
||||||
|
// Wait for Capacitor to be available
|
||||||
|
const Capacitor = await waitForCapacitor();
|
||||||
|
|
||||||
|
// Try to get safe area insets using StatusBar plugin (which is already available)
|
||||||
|
|
||||||
|
let top = 0,
|
||||||
|
bottom = 0,
|
||||||
|
left = 0,
|
||||||
|
right = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use StatusBar plugin to get status bar height
|
||||||
|
if (Capacitor.Plugins.StatusBar) {
|
||||||
|
const statusBarInfo = await Capacitor.Plugins.StatusBar.getInfo();
|
||||||
|
// Status bar height is typically the top safe area inset
|
||||||
|
top = statusBarInfo.overlays ? 0 : statusBarInfo.height || 0;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Status bar info not available, will use fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect navigation bar and gesture bar heights
|
||||||
|
const detectNavigationBar = () => {
|
||||||
|
const screenHeight = window.screen.height;
|
||||||
|
const screenWidth = window.screen.width;
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
const devicePixelRatio = window.devicePixelRatio || 1;
|
||||||
|
|
||||||
|
// Calculate navigation bar height
|
||||||
|
let navBarHeight = 0;
|
||||||
|
|
||||||
|
// Method 1: Direct comparison (most reliable)
|
||||||
|
if (windowHeight < screenHeight) {
|
||||||
|
navBarHeight = screenHeight - windowHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method 2: Check for gesture navigation indicators
|
||||||
|
if (navBarHeight === 0) {
|
||||||
|
// Look for common gesture navigation patterns
|
||||||
|
const isTallDevice = screenHeight > 2000;
|
||||||
|
const isModernDevice = screenHeight > 1800;
|
||||||
|
const hasHighDensity = devicePixelRatio >= 2.5;
|
||||||
|
|
||||||
|
if (isTallDevice && hasHighDensity) {
|
||||||
|
// Modern gesture-based device
|
||||||
|
navBarHeight = 12; // Typical gesture bar height
|
||||||
|
} else if (isModernDevice) {
|
||||||
|
// Modern device with traditional navigation
|
||||||
|
navBarHeight = 48; // Traditional navigation bar height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method 3: Check visual viewport (more accurate for WebView)
|
||||||
|
if (navBarHeight === 0) {
|
||||||
|
if (window.visualViewport) {
|
||||||
|
const visualHeight = window.visualViewport.height;
|
||||||
|
|
||||||
|
if (visualHeight < windowHeight) {
|
||||||
|
navBarHeight = windowHeight - visualHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method 4: Device-specific estimation based on screen dimensions
|
||||||
|
if (navBarHeight === 0) {
|
||||||
|
// Common Android navigation bar heights in pixels
|
||||||
|
const commonNavBarHeights = {
|
||||||
|
"1080x2400": 48, // Common 1080p devices
|
||||||
|
"1440x3200": 64, // QHD devices
|
||||||
|
"720x1600": 32, // HD devices
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolution = `${screenWidth}x${screenHeight}`;
|
||||||
|
const estimatedHeight = commonNavBarHeights[resolution];
|
||||||
|
|
||||||
|
if (estimatedHeight) {
|
||||||
|
navBarHeight = estimatedHeight;
|
||||||
|
} else {
|
||||||
|
// Fallback: estimate based on screen height
|
||||||
|
navBarHeight = screenHeight > 2000 ? 48 : 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return navBarHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get navigation bar height
|
||||||
|
bottom = detectNavigationBar();
|
||||||
|
|
||||||
|
// If we still don't have a top value, estimate it
|
||||||
|
if (top === 0) {
|
||||||
|
const screenHeight = window.screen.height;
|
||||||
|
// Common status bar heights: 24dp (48px) for most devices, 32dp (64px) for some
|
||||||
|
top = screenHeight > 1920 ? 64 : 48;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left/right safe areas are rare on Android
|
||||||
|
left = 0;
|
||||||
|
right = 0;
|
||||||
|
|
||||||
|
// Create CSS custom properties
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.textContent = `
|
||||||
|
:root {
|
||||||
|
--safe-area-inset-top: ${top}px;
|
||||||
|
--safe-area-inset-bottom: ${bottom}px;
|
||||||
|
--safe-area-inset-left: ${left}px;
|
||||||
|
--safe-area-inset-right: ${right}px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Inject the style into the document head
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
// Also set CSS environment variables if supported
|
||||||
|
if (CSS.supports("env(safe-area-inset-top)")) {
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--env-safe-area-inset-top",
|
||||||
|
`${top}px`,
|
||||||
|
);
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--env-safe-area-inset-bottom",
|
||||||
|
`${bottom}px`,
|
||||||
|
);
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--env-safe-area-inset-left",
|
||||||
|
`${left}px`,
|
||||||
|
);
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--env-safe-area-inset-right",
|
||||||
|
`${right}px`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Error injecting safe area insets, will use fallback values
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize when DOM is ready
|
||||||
|
const initializeSafeArea = () => {
|
||||||
|
// Check if we should run this script at all
|
||||||
|
if (!isAndroidWebView()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a small delay to ensure WebView is fully initialized
|
||||||
|
setTimeout(() => {
|
||||||
|
injectSafeAreaInsets();
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", initializeSafeArea);
|
||||||
|
} else {
|
||||||
|
initializeSafeArea();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-inject on orientation change (only on Android)
|
||||||
|
window.addEventListener("orientationchange", () => {
|
||||||
|
if (isAndroidWebView()) {
|
||||||
|
setTimeout(() => injectSafeAreaInsets(), 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-inject on resize (only on Android)
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
if (isAndroidWebView()) {
|
||||||
|
setTimeout(() => injectSafeAreaInsets(), 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Export for use in other modules
|
||||||
|
export { injectSafeAreaInsets, isAndroidWebView };
|
||||||
@@ -58,7 +58,8 @@
|
|||||||
v-if="!isRegistered"
|
v-if="!isRegistered"
|
||||||
:passkeys-enabled="PASSKEYS_ENABLED"
|
:passkeys-enabled="PASSKEYS_ENABLED"
|
||||||
:given-name="givenName"
|
:given-name="givenName"
|
||||||
message="Before you can publicly announce a new project or time commitment, a friend needs to register you."
|
message="Before you can publicly announce a new project or time commitment,
|
||||||
|
a friend needs to register you."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
|
|||||||
@@ -17,20 +17,40 @@
|
|||||||
|
|
||||||
<!-- Results List -->
|
<!-- Results List -->
|
||||||
<ul class="border-t border-slate-300">
|
<ul class="border-t border-slate-300">
|
||||||
<li class="border-b border-slate-300 py-3">
|
<!-- "You" entity -->
|
||||||
|
<li v-if="shouldShowYouEntity" class="border-b border-slate-300 py-3">
|
||||||
<h2 class="text-base flex gap-4 items-center">
|
<h2 class="text-base flex gap-4 items-center">
|
||||||
<span class="grow flex gap-2 items-center font-medium">
|
<span class="grow flex gap-2 items-center font-medium">
|
||||||
<font-awesome
|
<font-awesome icon="hand" class="text-blue-500 text-4xl shrink-0" />
|
||||||
icon="circle-question"
|
<span class="text-ellipsis overflow-hidden text-blue-500">You</span>
|
||||||
class="text-slate-400 text-4xl"
|
|
||||||
/>
|
|
||||||
<span class="italic text-slate-400">(Not Named)</span>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="text-right">
|
<span class="text-right">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="block w-full text-center text-sm uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
|
class="block w-full text-center text-sm uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
|
||||||
@click="openDialog('Unnamed')"
|
@click="openDialog({ did: activeDid, name: 'You' })"
|
||||||
|
>
|
||||||
|
<font-awesome icon="gift" class="fa-fw"></font-awesome>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</li>
|
||||||
|
<li class="border-b border-slate-300 py-3">
|
||||||
|
<h2 class="text-base flex gap-4 items-center">
|
||||||
|
<span class="grow flex gap-2 items-center font-medium">
|
||||||
|
<font-awesome
|
||||||
|
icon="circle-question"
|
||||||
|
class="text-slate-400 text-4xl shrink-0"
|
||||||
|
/>
|
||||||
|
<span class="text-ellipsis overflow-hidden italic text-slate-500">{{
|
||||||
|
unnamedEntityName
|
||||||
|
}}</span>
|
||||||
|
</span>
|
||||||
|
<span class="text-right">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="block w-full text-center text-sm uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
|
||||||
|
@click="openDialog({ did: '', name: unnamedEntityName })"
|
||||||
>
|
>
|
||||||
<font-awesome icon="gift" class="fa-fw"></font-awesome>
|
<font-awesome icon="gift" class="fa-fw"></font-awesome>
|
||||||
</button>
|
</button>
|
||||||
@@ -43,14 +63,22 @@
|
|||||||
class="border-b border-slate-300 py-3"
|
class="border-b border-slate-300 py-3"
|
||||||
>
|
>
|
||||||
<h2 class="text-base flex gap-4 items-center">
|
<h2 class="text-base flex gap-4 items-center">
|
||||||
<span class="grow flex gap-2 items-center font-medium">
|
<span
|
||||||
|
class="grow flex gap-2 items-center font-medium overflow-hidden"
|
||||||
|
>
|
||||||
<EntityIcon
|
<EntityIcon
|
||||||
:contact="contact"
|
:contact="contact"
|
||||||
:icon-size="34"
|
:icon-size="34"
|
||||||
class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden"
|
class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden shrink-0"
|
||||||
/>
|
/>
|
||||||
<span v-if="contact.name">{{ contact.name }}</span>
|
<span v-if="contact.name" class="text-ellipsis overflow-hidden">{{
|
||||||
<span v-else class="italic text-slate-400">(No name)</span>
|
contact.name
|
||||||
|
}}</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-ellipsis overflow-hidden italic text-slate-500"
|
||||||
|
>{{ contact.did }}</span
|
||||||
|
>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-right">
|
<span class="text-right">
|
||||||
<button
|
<button
|
||||||
@@ -72,6 +100,7 @@
|
|||||||
:from-project-id="fromProjectId"
|
:from-project-id="fromProjectId"
|
||||||
:to-project-id="toProjectId"
|
:to-project-id="toProjectId"
|
||||||
:is-from-project-view="isFromProjectView"
|
:is-from-project-view="isFromProjectView"
|
||||||
|
:hide-show-all="true"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -89,6 +118,7 @@ import { GiverReceiverInputInfo } from "../libs/util";
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
@Component({
|
@Component({
|
||||||
components: { GiftedDialog, QuickNav, EntityIcon },
|
components: { GiftedDialog, QuickNav, EntityIcon },
|
||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
@@ -188,147 +218,151 @@ export default class ContactGiftingView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openDialog(contact?: GiverReceiverInputInfo | "Unnamed") {
|
openDialog(contact?: GiverReceiverInputInfo) {
|
||||||
if (contact === "Unnamed") {
|
// Determine the selected entity based on contact type
|
||||||
// Special case: Handle "Unnamed" contacts for both givers and recipients
|
const selectedEntity = this.createEntityFromContact(contact);
|
||||||
let recipient: GiverReceiverInputInfo;
|
|
||||||
let giver: GiverReceiverInputInfo | undefined;
|
|
||||||
|
|
||||||
if (this.stepType === "giver") {
|
// Create giver and recipient based on step type and selected entity
|
||||||
// We're selecting a giver, so preserve the existing recipient from context
|
const { giver, recipient } = this.createGiverAndRecipient(selectedEntity);
|
||||||
if (this.recipientEntityType === "project") {
|
|
||||||
recipient = {
|
|
||||||
did: this.recipientProjectHandleId,
|
|
||||||
name: this.recipientProjectName,
|
|
||||||
image: this.recipientProjectImage,
|
|
||||||
handleId: this.recipientProjectHandleId,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Preserve the existing recipient from context
|
|
||||||
if (this.recipientDid === this.activeDid) {
|
|
||||||
// Recipient was "You"
|
|
||||||
recipient = { did: this.activeDid, name: "You" };
|
|
||||||
} else if (this.recipientDid) {
|
|
||||||
// Recipient was a regular contact
|
|
||||||
recipient = {
|
|
||||||
did: this.recipientDid,
|
|
||||||
name: this.recipientProjectName || "Someone",
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Fallback to "You" if no recipient was previously selected
|
|
||||||
recipient = { did: this.activeDid, name: "You" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
giver = undefined; // Will be set to "Unnamed" in GiftedDialog
|
|
||||||
} else {
|
|
||||||
// We're selecting a recipient, so recipient is "Unnamed" and giver is preserved from context
|
|
||||||
recipient = { did: "", name: "Unnamed" };
|
|
||||||
|
|
||||||
// Preserve the existing giver from the context
|
// Open the dialog
|
||||||
if (this.giverEntityType === "project") {
|
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||||
giver = {
|
giver,
|
||||||
did: this.giverProjectHandleId,
|
recipient,
|
||||||
name: this.giverProjectName,
|
this.offerId,
|
||||||
image: this.giverProjectImage,
|
this.prompt,
|
||||||
handleId: this.giverProjectHandleId,
|
this.description,
|
||||||
};
|
this.amountInput,
|
||||||
} else if (this.giverDid) {
|
this.unitCode,
|
||||||
giver = {
|
);
|
||||||
did: this.giverDid,
|
|
||||||
name: this.giverProjectName || "Someone",
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
giver = { did: this.activeDid, name: "You" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
// Move to Step 2 - entities are already set by the open() call
|
||||||
giver,
|
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
||||||
recipient,
|
}
|
||||||
this.offerId,
|
|
||||||
this.prompt,
|
|
||||||
this.description,
|
|
||||||
this.amountInput,
|
|
||||||
this.unitCode,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Move to Step 2 - entities are already set by the open() call
|
/**
|
||||||
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
* Creates an entity object from the contact parameter
|
||||||
|
* Uses DID-based logic to determine "You" and "Unnamed" entities
|
||||||
|
*/
|
||||||
|
private createEntityFromContact(
|
||||||
|
contact?: GiverReceiverInputInfo,
|
||||||
|
): GiverReceiverInputInfo | undefined {
|
||||||
|
if (!contact) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle GiverReceiverInputInfo object
|
||||||
|
if (contact.did === this.activeDid) {
|
||||||
|
// If DID matches active DID, create "You" entity
|
||||||
|
return { did: this.activeDid, name: "You" };
|
||||||
|
} else if (!contact.did || contact.did === "") {
|
||||||
|
// If DID is empty/null, create "Unnamed" entity
|
||||||
|
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||||
} else {
|
} else {
|
||||||
// Regular case: contact is a GiverReceiverInputInfo
|
// Create a copy of the contact to avoid modifying the original
|
||||||
let giver: GiverReceiverInputInfo;
|
return { ...contact };
|
||||||
let recipient: GiverReceiverInputInfo;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.stepType === "giver") {
|
/**
|
||||||
// We're selecting a giver, so the contact becomes the giver
|
* Creates giver and recipient objects based on step type and selected entity
|
||||||
giver = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
|
*/
|
||||||
|
private createGiverAndRecipient(selectedEntity?: GiverReceiverInputInfo): {
|
||||||
|
giver: GiverReceiverInputInfo | undefined;
|
||||||
|
recipient: GiverReceiverInputInfo;
|
||||||
|
} {
|
||||||
|
if (this.stepType === "giver") {
|
||||||
|
// We're selecting a giver, so the selected entity becomes the giver
|
||||||
|
const giver = selectedEntity;
|
||||||
|
const recipient = this.createRecipientFromContext();
|
||||||
|
return { giver, recipient };
|
||||||
|
} else {
|
||||||
|
// We're selecting a recipient, so the selected entity becomes the recipient
|
||||||
|
const recipient = selectedEntity || {
|
||||||
|
did: "",
|
||||||
|
name: UNNAMED_ENTITY_NAME,
|
||||||
|
};
|
||||||
|
const giver = this.createGiverFromContext();
|
||||||
|
return { giver, recipient };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Preserve the existing recipient from the context
|
/**
|
||||||
if (this.recipientEntityType === "project") {
|
* Creates recipient object from context (preserves existing recipient)
|
||||||
recipient = {
|
*/
|
||||||
did: this.recipientProjectHandleId,
|
private createRecipientFromContext(): GiverReceiverInputInfo {
|
||||||
name: this.recipientProjectName,
|
if (this.recipientEntityType === "project") {
|
||||||
image: this.recipientProjectImage,
|
return {
|
||||||
handleId: this.recipientProjectHandleId,
|
name: this.recipientProjectName,
|
||||||
};
|
image: this.recipientProjectImage,
|
||||||
} else {
|
handleId: this.recipientProjectHandleId,
|
||||||
// Check if the preserved recipient was "You" or a regular contact
|
};
|
||||||
if (this.recipientDid === this.activeDid) {
|
} else {
|
||||||
// Recipient was "You"
|
if (this.recipientDid === this.activeDid) {
|
||||||
recipient = { did: this.activeDid, name: "You" };
|
return { did: this.activeDid, name: "You" };
|
||||||
} else if (this.recipientDid) {
|
} else if (this.recipientDid) {
|
||||||
// Recipient was a regular contact
|
return {
|
||||||
recipient = {
|
did: this.recipientDid,
|
||||||
did: this.recipientDid,
|
name: this.recipientProjectName,
|
||||||
name: this.recipientProjectName || "Someone",
|
};
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Fallback to "Unnamed"
|
|
||||||
recipient = { did: "", name: "Unnamed" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// We're selecting a recipient, so the contact becomes the recipient
|
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||||
recipient = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
|
|
||||||
|
|
||||||
// Preserve the existing giver from the context
|
|
||||||
if (this.giverEntityType === "project") {
|
|
||||||
giver = {
|
|
||||||
did: this.giverProjectHandleId,
|
|
||||||
name: this.giverProjectName,
|
|
||||||
image: this.giverProjectImage,
|
|
||||||
handleId: this.giverProjectHandleId,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Check if the preserved giver was "You" or a regular contact
|
|
||||||
if (this.giverDid === this.activeDid) {
|
|
||||||
// Giver was "You"
|
|
||||||
giver = { did: this.activeDid, name: "You" };
|
|
||||||
} else if (this.giverDid) {
|
|
||||||
// Giver was a regular contact
|
|
||||||
giver = {
|
|
||||||
did: this.giverDid,
|
|
||||||
name: this.giverProjectName || "Someone",
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Fallback to "Unnamed"
|
|
||||||
giver = { did: "", name: "Unnamed" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
/**
|
||||||
giver,
|
* Creates giver object from context (preserves existing giver)
|
||||||
recipient,
|
*/
|
||||||
this.offerId,
|
private createGiverFromContext(): GiverReceiverInputInfo {
|
||||||
this.prompt,
|
if (this.giverEntityType === "project") {
|
||||||
this.description,
|
return {
|
||||||
this.amountInput,
|
name: this.giverProjectName,
|
||||||
this.unitCode,
|
image: this.giverProjectImage,
|
||||||
);
|
handleId: this.giverProjectHandleId,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (this.giverDid === this.activeDid) {
|
||||||
|
return { did: this.activeDid, name: "You" };
|
||||||
|
} else if (this.giverDid) {
|
||||||
|
return {
|
||||||
|
did: this.giverDid,
|
||||||
|
name: this.giverProjectName,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Move to Step 2 - entities are already set by the open() call
|
/**
|
||||||
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
* Get the unnamed entity name constant
|
||||||
|
*/
|
||||||
|
get unnamedEntityName(): string {
|
||||||
|
return UNNAMED_ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldShowYouEntity(): boolean {
|
||||||
|
if (this.stepType === "giver") {
|
||||||
|
// When selecting a giver, show "You" if the current recipient is not "You"
|
||||||
|
// This prevents selecting yourself as both giver and recipient
|
||||||
|
if (this.recipientEntityType === "project") {
|
||||||
|
// If recipient is a project, we can select "You" as giver
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// If recipient is a person, check if it's not "You"
|
||||||
|
return this.recipientDid !== this.activeDid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// When selecting a recipient, show "You" if the current giver is not "You"
|
||||||
|
// This prevents selecting yourself as both giver and recipient
|
||||||
|
if (this.giverEntityType === "project") {
|
||||||
|
// If giver is a project, we can select "You" as recipient
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// If giver is a person, check if it's not "You"
|
||||||
|
return this.giverDid !== this.activeDid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,21 +220,21 @@ export default class ContactQRScanFull extends Vue {
|
|||||||
* Computed property for QR code container CSS classes
|
* Computed property for QR code container CSS classes
|
||||||
*/
|
*/
|
||||||
get qrContainerClasses(): string {
|
get qrContainerClasses(): string {
|
||||||
return "block w-full max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto mt-4";
|
return "block w-full max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto mt-4";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed property for camera frame CSS classes
|
* Computed property for camera frame CSS classes
|
||||||
*/
|
*/
|
||||||
get cameraFrameClasses(): string {
|
get cameraFrameClasses(): string {
|
||||||
return "relative w-full max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto border border-dashed border-white mt-8 aspect-square";
|
return "relative w-full max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto border border-dashed border-white mt-8 aspect-square";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed property for main content container CSS classes
|
* Computed property for main content container CSS classes
|
||||||
*/
|
*/
|
||||||
get mainContentClasses(): string {
|
get mainContentClasses(): string {
|
||||||
return "p-6 bg-white w-full max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto";
|
return "p-6 bg-white w-full max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -257,11 +257,11 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get qrCodeContainerClasses(): string {
|
get qrCodeContainerClasses(): string {
|
||||||
return "block w-[90vw] max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto my-4";
|
return "block w-[90vw] max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto my-4";
|
||||||
}
|
}
|
||||||
|
|
||||||
get scannerContainerClasses(): string {
|
get scannerContainerClasses(): string {
|
||||||
return "relative aspect-square overflow-hidden bg-slate-800 w-[90vw] max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto";
|
return "relative aspect-square overflow-hidden bg-slate-800 w-[90vw] max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto";
|
||||||
}
|
}
|
||||||
|
|
||||||
get statusMessageClasses(): string {
|
get statusMessageClasses(): string {
|
||||||
|
|||||||
@@ -290,6 +290,7 @@ import {
|
|||||||
NOTIFY_SERVER_ACCESS_ERROR,
|
NOTIFY_SERVER_ACCESS_ERROR,
|
||||||
NOTIFY_NO_IDENTITY_ERROR,
|
NOTIFY_NO_IDENTITY_ERROR,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { THAT_UNNAMED_PERSON } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DIDView Component
|
* DIDView Component
|
||||||
@@ -551,7 +552,7 @@ export default class DIDView extends Vue {
|
|||||||
contact.registered = true;
|
contact.registered = true;
|
||||||
await this.$updateContact(contact.did, { registered: true });
|
await this.$updateContact(contact.did, { registered: true });
|
||||||
|
|
||||||
const name = contact.name || "That unnamed person";
|
const name = contact.name || THAT_UNNAMED_PERSON;
|
||||||
this.notify.success(
|
this.notify.success(
|
||||||
`${name} ${NOTIFY_REGISTRATION_SUCCESS.message}`,
|
`${name} ${NOTIFY_REGISTRATION_SUCCESS.message}`,
|
||||||
TIMEOUTS.LONG,
|
TIMEOUTS.LONG,
|
||||||
|
|||||||
@@ -233,7 +233,7 @@
|
|||||||
|
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<h2 class="text-base font-semibold">
|
<h2 class="text-base font-semibold">
|
||||||
{{ project.name || "Unnamed Project" }}
|
{{ project.name || unnamedProject }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<font-awesome
|
<font-awesome
|
||||||
@@ -340,6 +340,7 @@ import {
|
|||||||
NOTIFY_DISCOVER_LOCAL_SEARCH_ERROR,
|
NOTIFY_DISCOVER_LOCAL_SEARCH_ERROR,
|
||||||
NOTIFY_DISCOVER_MAP_SEARCH_ERROR,
|
NOTIFY_DISCOVER_MAP_SEARCH_ERROR,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||||
interface Tile {
|
interface Tile {
|
||||||
indexLat: number;
|
indexLat: number;
|
||||||
indexLon: number;
|
indexLon: number;
|
||||||
@@ -370,6 +371,13 @@ export default class DiscoverView extends Vue {
|
|||||||
|
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed project constant
|
||||||
|
*/
|
||||||
|
get unnamedProject(): string {
|
||||||
|
return UNNAMED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
allContacts: Array<Contact> = [];
|
allContacts: Array<Contact> = [];
|
||||||
allMyDids: Array<string> = [];
|
allMyDids: Array<string> = [];
|
||||||
|
|||||||
@@ -200,7 +200,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Then you can record your appreciation for... whatever: select any contact on the home page
|
Then you can record your appreciation for... whatever: select any contact on the home page
|
||||||
(or "Unnamed") and send it. The main goal is to record what people
|
(or "{{ unnamedEntityName }}") and send it. The main goal is to record what people
|
||||||
have given you, to grow giving economies. You can also record your own
|
have given you, to grow giving economies. You can also record your own
|
||||||
ideas for projects. Each claim is recorded on a
|
ideas for projects. Each claim is recorded on a
|
||||||
custom ledger.
|
custom ledger.
|
||||||
@@ -600,6 +600,7 @@ import QuickNav from "../components/QuickNav.vue";
|
|||||||
import { APP_SERVER } from "../constants/app";
|
import { APP_SERVER } from "../constants/app";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { QRNavigationService } from "@/services/QRNavigationService";
|
import { QRNavigationService } from "@/services/QRNavigationService";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HelpView.vue - Comprehensive Help System Component
|
* HelpView.vue - Comprehensive Help System Component
|
||||||
@@ -647,6 +648,13 @@ export default class HelpView extends Vue {
|
|||||||
APP_SERVER = APP_SERVER;
|
APP_SERVER = APP_SERVER;
|
||||||
// Capacitor reference removed - using QRNavigationService instead
|
// Capacitor reference removed - using QRNavigationService instead
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed entity name constant
|
||||||
|
*/
|
||||||
|
get unnamedEntityName(): string {
|
||||||
|
return UNNAMED_ENTITY_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
// Ideally, we put no functionality in here, especially in the setup,
|
// Ideally, we put no functionality in here, especially in the setup,
|
||||||
// because we never want this page to have a chance of throwing an error.
|
// because we never want this page to have a chance of throwing an error.
|
||||||
|
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications";
|
import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications";
|
||||||
import * as Package from "../../package.json";
|
import * as Package from "../../package.json";
|
||||||
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
|
|
||||||
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
||||||
interface Claim {
|
interface Claim {
|
||||||
@@ -1546,30 +1547,41 @@ export default class HomeView extends Vue {
|
|||||||
* @param giver Optional contact info for giver
|
* @param giver Optional contact info for giver
|
||||||
* @param description Optional gift description
|
* @param description Optional gift description
|
||||||
*/
|
*/
|
||||||
openDialog(giver?: GiverReceiverInputInfo | "Unnamed", prompt?: string) {
|
openDialog(giver?: GiverReceiverInputInfo, prompt?: string) {
|
||||||
if (giver === "Unnamed") {
|
// Determine the giver entity based on DID logic
|
||||||
// Special case: Pass undefined to trigger Step 1, but with "Unnamed" pre-selected
|
const giverEntity = this.createGiverEntity(giver);
|
||||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
|
||||||
undefined,
|
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||||
{
|
giverEntity,
|
||||||
did: this.activeDid,
|
{
|
||||||
name: "You",
|
did: this.activeDid,
|
||||||
} as GiverReceiverInputInfo,
|
name: "You", // In HomeView, we always use "You" as the giver
|
||||||
undefined,
|
} as GiverReceiverInputInfo,
|
||||||
prompt,
|
undefined,
|
||||||
);
|
prompt,
|
||||||
// Immediately select "Unnamed" and move to Step 2
|
);
|
||||||
(this.$refs.giftedDialog as GiftedDialog).selectGiver();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates giver entity using DID-based logic
|
||||||
|
*/
|
||||||
|
private createGiverEntity(
|
||||||
|
giver?: GiverReceiverInputInfo,
|
||||||
|
): GiverReceiverInputInfo | undefined {
|
||||||
|
if (!giver) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle GiverReceiverInputInfo object
|
||||||
|
if (giver.did === this.activeDid) {
|
||||||
|
// If DID matches active DID, create "You" entity
|
||||||
|
return { did: this.activeDid, name: "You" };
|
||||||
|
} else if (!giver.did || giver.did === "") {
|
||||||
|
// If DID is empty/null, create "Unnamed" entity
|
||||||
|
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||||
} else {
|
} else {
|
||||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
// Return the giver as-is
|
||||||
giver,
|
return giver;
|
||||||
{
|
|
||||||
did: this.activeDid,
|
|
||||||
name: "You",
|
|
||||||
} as GiverReceiverInputInfo,
|
|
||||||
undefined,
|
|
||||||
prompt,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1632,10 +1644,15 @@ export default class HomeView extends Vue {
|
|||||||
this.isImageViewerOpen = true;
|
this.isImageViewerOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
openPersonDialog(
|
private handleQRCodeClick() {
|
||||||
giver?: GiverReceiverInputInfo | "Unnamed",
|
if (Capacitor.isNativePlatform()) {
|
||||||
prompt?: string,
|
this.$router.push({ name: "contact-qr-scan-full" });
|
||||||
) {
|
} else {
|
||||||
|
this.$router.push({ name: "contact-qr" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openPersonDialog(giver?: GiverReceiverInputInfo, prompt?: string) {
|
||||||
this.showProjectsDialog = false;
|
this.showProjectsDialog = false;
|
||||||
this.openDialog(giver, prompt);
|
this.openDialog(giver, prompt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@
|
|||||||
class="text-blue-500"
|
class="text-blue-500"
|
||||||
@click="onClickLoadProject(plan.handleId)"
|
@click="onClickLoadProject(plan.handleId)"
|
||||||
>
|
>
|
||||||
{{ plan.name || "Unnamed Project" }}
|
{{ plan.name || unnamedProject }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="fulfillersToHitLimit" class="text-center">
|
<div v-if="fulfillersToHitLimit" class="text-center">
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
class="text-blue-500"
|
class="text-blue-500"
|
||||||
@click="onClickLoadProject(fulfilledByThis.handleId)"
|
@click="onClickLoadProject(fulfilledByThis.handleId)"
|
||||||
>
|
>
|
||||||
{{ fulfilledByThis.name || "Unnamed Project" }}
|
{{ fulfilledByThis.name || unnamedProject }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -611,6 +611,7 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
import { NOTIFY_CONFIRM_CLAIM } from "@/constants/notifications";
|
import { NOTIFY_CONFIRM_CLAIM } from "@/constants/notifications";
|
||||||
import { APP_SERVER } from "@/constants/app";
|
import { APP_SERVER } from "@/constants/app";
|
||||||
|
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||||
/**
|
/**
|
||||||
* Project View Component
|
* Project View Component
|
||||||
* @author Matthew Raymer
|
* @author Matthew Raymer
|
||||||
@@ -664,6 +665,13 @@ export default class ProjectViewView extends Vue {
|
|||||||
/** Notification helpers instance */
|
/** Notification helpers instance */
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed project constant
|
||||||
|
*/
|
||||||
|
get unnamedProject(): string {
|
||||||
|
return UNNAMED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
// Account and Settings State
|
// Account and Settings State
|
||||||
/** Currently active DID */
|
/** Currently active DID */
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
|
|||||||
@@ -244,7 +244,7 @@
|
|||||||
|
|
||||||
<div class="grow overflow-hidden">
|
<div class="grow overflow-hidden">
|
||||||
<h2 class="text-base font-semibold">
|
<h2 class="text-base font-semibold">
|
||||||
{{ project.name || "Unnamed Project" }}
|
{{ project.name || unnamedProject }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="text-sm truncate">
|
<div class="text-sm truncate">
|
||||||
{{ project.description }}
|
{{ project.description }}
|
||||||
@@ -286,6 +286,7 @@ import {
|
|||||||
NOTIFY_OFFERS_LOAD_ERROR,
|
NOTIFY_OFFERS_LOAD_ERROR,
|
||||||
NOTIFY_OFFERS_FETCH_ERROR,
|
NOTIFY_OFFERS_FETCH_ERROR,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Projects View Component
|
* Projects View Component
|
||||||
@@ -324,6 +325,13 @@ export default class ProjectsView extends Vue {
|
|||||||
|
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unnamed project constant
|
||||||
|
*/
|
||||||
|
get unnamedProject(): string {
|
||||||
|
return UNNAMED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
// User account state
|
// User account state
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
allContacts: Array<Contact> = [];
|
allContacts: Array<Contact> = [];
|
||||||
|
|||||||
@@ -239,7 +239,8 @@ export default class QuickActionBvcEndView extends Vue {
|
|||||||
}
|
}
|
||||||
const eventStartDateObj = currentOrPreviousSat
|
const eventStartDateObj = currentOrPreviousSat
|
||||||
.set({ weekday: 6 })
|
.set({ weekday: 6 })
|
||||||
.set({ hour: 9 })
|
.set({ hour: 8 })
|
||||||
|
.set({ minute: 30 }) // to catch if people put their claims 30 minutes early
|
||||||
.startOf("hour");
|
.startOf("hour");
|
||||||
|
|
||||||
// Hack, but full ISO pushes the length to 340 which crashes verifyJWT!
|
// Hack, but full ISO pushes the length to 340 which crashes verifyJWT!
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||||
import { deleteContact, generateAndRegisterEthrUser, importUser } from './testUtils';
|
import { deleteContact, generateAndRegisterEthrUser, importUser } from './testUtils';
|
||||||
|
|
||||||
test('Check activity feed - check that server is running', async ({ page }) => {
|
test('Check activity feed - check that server is running', async ({ page }) => {
|
||||||
@@ -177,7 +178,7 @@ test('Check User 0 can register a random person', async ({ page }) => {
|
|||||||
await page.goto('./');
|
await page.goto('./');
|
||||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||||
await page.getByRole('button', { name: 'Person' }).click();
|
await page.getByRole('button', { name: 'Person' }).click();
|
||||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||||
await page.getByPlaceholder('What was given').fill('Gave me access!');
|
await page.getByPlaceholder('What was given').fill('Gave me access!');
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||||
import { importUser } from './testUtils';
|
import { importUser } from './testUtils';
|
||||||
|
|
||||||
test('Record something given', async ({ page }) => {
|
test('Record something given', async ({ page }) => {
|
||||||
@@ -101,7 +102,7 @@ test('Record something given', async ({ page }) => {
|
|||||||
await page.goto('./');
|
await page.goto('./');
|
||||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||||
await page.getByRole('button', { name: 'Person' }).click();
|
await page.getByRole('button', { name: 'Person' }).click();
|
||||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||||
await page.getByPlaceholder('What was given').fill(finalTitle);
|
await page.getByPlaceholder('What was given').fill(finalTitle);
|
||||||
await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString());
|
await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString());
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
|
|||||||
@@ -85,6 +85,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||||
import { importUser, createUniqueStringsArray, createRandomNumbersArray } from './testUtils';
|
import { importUser, createUniqueStringsArray, createRandomNumbersArray } from './testUtils';
|
||||||
|
|
||||||
test('Record 9 new gifts', async ({ page }) => {
|
test('Record 9 new gifts', async ({ page }) => {
|
||||||
@@ -116,7 +117,7 @@ test('Record 9 new gifts', async ({ page }) => {
|
|||||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||||
}
|
}
|
||||||
await page.getByRole('button', { name: 'Person' }).click();
|
await page.getByRole('button', { name: 'Person' }).click();
|
||||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||||
await page.getByPlaceholder('What was given').fill(finalTitles[i]);
|
await page.getByPlaceholder('What was given').fill(finalTitles[i]);
|
||||||
await page.getByRole('spinbutton').fill(finalNumbers[i].toString());
|
await page.getByRole('spinbutton').fill(finalNumbers[i].toString());
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
|
|||||||
Reference in New Issue
Block a user