Compare commits
17 Commits
onboard-al
...
dialog-not
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e588e223bc | ||
|
|
02eead5609 | ||
| 1e203da9bb | |||
|
|
504f26190f | ||
|
|
1893c2af1b | ||
|
|
df49c80199 | ||
|
|
4d89042997 | ||
| 580a485573 | |||
|
|
8f5111d100 | ||
|
|
5a4bc9efc5 | ||
|
|
909d6ff561 | ||
|
|
6d0f4d910f | ||
|
|
87ebe4ffae | ||
|
|
47509b1482 | ||
|
|
48856d4fce | ||
|
|
c3bd22fb83 | ||
|
|
c18a6b334f |
27
BUILDING.md
27
BUILDING.md
@@ -2716,6 +2716,33 @@ configuration files in the repository.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 2025-08-21 - Commitlint Configuration Refinement
|
||||||
|
|
||||||
|
#### Commit Message Validation Improvements
|
||||||
|
- **Modified**: Commitlint configuration moved from `package.json` to dedicated `commitlint.config.js`
|
||||||
|
- **Enhanced**: Strict validation rules downgraded from errors to warnings
|
||||||
|
- **Before**: `subject-case` and `subject-full-stop` rules caused red error messages
|
||||||
|
- **After**: Same rules now show yellow warnings without blocking commits
|
||||||
|
- **Benefit**: Eliminates confusing red error messages while maintaining commit quality guidance
|
||||||
|
|
||||||
|
#### Configuration Structure
|
||||||
|
- **File**: `commitlint.config.js` - Dedicated commitlint configuration
|
||||||
|
- **Extends**: `@commitlint/config-conventional` - Standard conventional commit rules
|
||||||
|
- **Custom Rules**:
|
||||||
|
- `subject-case: [1, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']]`
|
||||||
|
- `subject-full-stop: [1, 'never', '.']`
|
||||||
|
- **Levels**:
|
||||||
|
- `0` = Disabled, `1` = Warning, `2` = Error
|
||||||
|
- Current: Problematic rules set to warning level (1)
|
||||||
|
|
||||||
|
#### User Experience Impact
|
||||||
|
- **Before**: Red error messages on every push with strict commit rules
|
||||||
|
- **After**: Yellow warning messages that provide guidance without disruption
|
||||||
|
- **Workflow**: Commits and pushes continue to work while maintaining quality standards
|
||||||
|
- **Feedback**: Developers still receive helpful commit message guidance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
**Note**: This documentation is maintained alongside the build system. For the
|
**Note**: This documentation is maintained alongside the build system. For the
|
||||||
most up-to-date information, refer to the actual script files and Vite
|
most up-to-date information, refer to the actual script files and Vite
|
||||||
configuration files in the repository.
|
configuration files in the repository.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ dependencies {
|
|||||||
implementation project(':capacitor-mlkit-barcode-scanning')
|
implementation project(':capacitor-mlkit-barcode-scanning')
|
||||||
implementation project(':capacitor-app')
|
implementation project(':capacitor-app')
|
||||||
implementation project(':capacitor-camera')
|
implementation project(':capacitor-camera')
|
||||||
|
implementation project(':capacitor-clipboard')
|
||||||
implementation project(':capacitor-filesystem')
|
implementation project(':capacitor-filesystem')
|
||||||
implementation project(':capacitor-share')
|
implementation project(':capacitor-share')
|
||||||
implementation project(':capawesome-capacitor-file-picker')
|
implementation project(':capawesome-capacitor-file-picker')
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
"pkg": "@capacitor/camera",
|
"pkg": "@capacitor/camera",
|
||||||
"classpath": "com.capacitorjs.plugins.camera.CameraPlugin"
|
"classpath": "com.capacitorjs.plugins.camera.CameraPlugin"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"pkg": "@capacitor/clipboard",
|
||||||
|
"classpath": "com.capacitorjs.plugins.clipboard.ClipboardPlugin"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"pkg": "@capacitor/filesystem",
|
"pkg": "@capacitor/filesystem",
|
||||||
"classpath": "com.capacitorjs.plugins.filesystem.FilesystemPlugin"
|
"classpath": "com.capacitorjs.plugins.filesystem.FilesystemPlugin"
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/
|
|||||||
include ':capacitor-camera'
|
include ':capacitor-camera'
|
||||||
project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')
|
project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')
|
||||||
|
|
||||||
|
include ':capacitor-clipboard'
|
||||||
|
project(':capacitor-clipboard').projectDir = new File('../node_modules/@capacitor/clipboard/android')
|
||||||
|
|
||||||
include ':capacitor-filesystem'
|
include ':capacitor-filesystem'
|
||||||
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')
|
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')
|
||||||
|
|
||||||
|
|||||||
9
commitlint.config.js
Normal file
9
commitlint.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['@commitlint/config-conventional'],
|
||||||
|
rules: {
|
||||||
|
// Downgrade strict case rules to warnings (level 1) instead of errors (level 2)
|
||||||
|
// This eliminates red error messages while maintaining helpful guidance
|
||||||
|
'subject-case': [1, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
|
||||||
|
'subject-full-stop': [1, 'never', '.'],
|
||||||
|
}
|
||||||
|
};
|
||||||
69
doc/z-index-guide.md
Normal file
69
doc/z-index-guide.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Z-Index Guide — TimeSafari
|
||||||
|
|
||||||
|
**Author**: Development Team
|
||||||
|
**Date**: 2025-08-25T19:38:09-08:00
|
||||||
|
**Status**: 🎯 **ACTIVE** - Z-index layering standards
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Establish consistent z-index values across the TimeSafari application to ensure proper layering of UI elements.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
This document defines the z-index hierarchy for all UI components.
|
||||||
|
|
||||||
|
## Use/Run
|
||||||
|
Reference these values when implementing new components or modifying existing ones to maintain consistent layering.
|
||||||
|
|
||||||
|
## Z-Index Hierarchy
|
||||||
|
|
||||||
|
| Component | Z-Index | Usage |
|
||||||
|
|-----------|---------|-------|
|
||||||
|
| **Map** | `40` | Base map layer and map-related overlays |
|
||||||
|
| **QuickNav** | `50` | Quick navigation bottom bar |
|
||||||
|
| **Dialogs and Modals** | `100` | Modal dialogs, popups, and overlay content |
|
||||||
|
| **Notifications and Toasts** | `120` | System notifications, alerts, and toast messages |
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Never exceed 120** - Keep the highest z-index reserved for critical notifications
|
||||||
|
2. **Use increments of 10** - Leave room for future additions between layers
|
||||||
|
3. **Document exceptions** - If you need a z-index outside this range, document the reason
|
||||||
|
4. **Test layering** - Verify z-index behavior across different screen sizes and devices
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
- **Avoid arbitrary values** - Don't use random z-index numbers
|
||||||
|
- **Don't nest high z-index** - Keep child elements within their parent's z-index range
|
||||||
|
- **Consider stacking context** - Remember that `position: relative` creates new stacking contexts
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
| Owner | Task | Exit Criteria | Target Date |
|
||||||
|
|-------|------|---------------|-------------|
|
||||||
|
| Dev Team | Apply z-index classes to existing components | All components use defined z-index values | 2025-09-01 |
|
||||||
|
|
||||||
|
## Competence Hooks
|
||||||
|
|
||||||
|
- **Why this works**: Creates predictable layering hierarchy that prevents UI conflicts
|
||||||
|
- **Common pitfalls**: Using arbitrary z-index values or exceeding the defined range
|
||||||
|
- **Next skill unlock**: Learn about CSS stacking contexts and their impact on z-index
|
||||||
|
- **Teach-back**: Explain the z-index hierarchy to a team member without referencing this guide
|
||||||
|
|
||||||
|
## Collaboration Hooks
|
||||||
|
|
||||||
|
- **Reviewers**: Frontend team, UI/UX designers
|
||||||
|
- **Sign-off checklist**:
|
||||||
|
- [ ] All new components follow z-index guidelines
|
||||||
|
- [ ] Existing components updated to use defined values
|
||||||
|
- [ ] Cross-browser testing completed
|
||||||
|
- [ ] Mobile responsiveness verified
|
||||||
|
|
||||||
|
## Assumptions & Limits
|
||||||
|
|
||||||
|
- Assumes modern browser support for z-index
|
||||||
|
- Limited to 4 defined layers (expandable if needed)
|
||||||
|
- Requires team discipline to maintain consistency
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [MDN Z-Index Documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index)
|
||||||
|
- [CSS Stacking Context Guide](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context)
|
||||||
@@ -15,6 +15,7 @@ def capacitor_pods
|
|||||||
pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/@capacitor-mlkit/barcode-scanning'
|
pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/@capacitor-mlkit/barcode-scanning'
|
||||||
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
|
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
|
||||||
pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
|
pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
|
||||||
|
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 'CapawesomeCapacitorFilePicker', :path => '../../node_modules/@capawesome/capacitor-file-picker'
|
pod 'CapawesomeCapacitorFilePicker', :path => '../../node_modules/@capawesome/capacitor-file-picker'
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ PODS:
|
|||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorCamera (6.1.2):
|
- CapacitorCamera (6.1.2):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
|
- CapacitorClipboard (6.0.2):
|
||||||
|
- Capacitor
|
||||||
- CapacitorCommunitySqlite (6.0.2):
|
- CapacitorCommunitySqlite (6.0.2):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- SQLCipher
|
- SQLCipher
|
||||||
@@ -88,6 +90,7 @@ DEPENDENCIES:
|
|||||||
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
|
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
|
||||||
- "CapacitorApp (from `../../node_modules/@capacitor/app`)"
|
- "CapacitorApp (from `../../node_modules/@capacitor/app`)"
|
||||||
- "CapacitorCamera (from `../../node_modules/@capacitor/camera`)"
|
- "CapacitorCamera (from `../../node_modules/@capacitor/camera`)"
|
||||||
|
- "CapacitorClipboard (from `../../node_modules/@capacitor/clipboard`)"
|
||||||
- "CapacitorCommunitySqlite (from `../../node_modules/@capacitor-community/sqlite`)"
|
- "CapacitorCommunitySqlite (from `../../node_modules/@capacitor-community/sqlite`)"
|
||||||
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
|
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
|
||||||
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
|
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
|
||||||
@@ -119,6 +122,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../../node_modules/@capacitor/app"
|
:path: "../../node_modules/@capacitor/app"
|
||||||
CapacitorCamera:
|
CapacitorCamera:
|
||||||
:path: "../../node_modules/@capacitor/camera"
|
:path: "../../node_modules/@capacitor/camera"
|
||||||
|
CapacitorClipboard:
|
||||||
|
:path: "../../node_modules/@capacitor/clipboard"
|
||||||
CapacitorCommunitySqlite:
|
CapacitorCommunitySqlite:
|
||||||
:path: "../../node_modules/@capacitor-community/sqlite"
|
:path: "../../node_modules/@capacitor-community/sqlite"
|
||||||
CapacitorCordova:
|
CapacitorCordova:
|
||||||
@@ -136,6 +141,7 @@ SPEC CHECKSUMS:
|
|||||||
Capacitor: c95400d761e376be9da6be5a05f226c0e865cebf
|
Capacitor: c95400d761e376be9da6be5a05f226c0e865cebf
|
||||||
CapacitorApp: e1e6b7d05e444d593ca16fd6d76f2b7c48b5aea7
|
CapacitorApp: e1e6b7d05e444d593ca16fd6d76f2b7c48b5aea7
|
||||||
CapacitorCamera: 9bc7b005d0e6f1d5f525b8137045b60cffffce79
|
CapacitorCamera: 9bc7b005d0e6f1d5f525b8137045b60cffffce79
|
||||||
|
CapacitorClipboard: 4443c3cdb7c77b1533dfe3ff0f9f7756aa8579df
|
||||||
CapacitorCommunitySqlite: 0299d20f4b00c2e6aa485a1d8932656753937b9b
|
CapacitorCommunitySqlite: 0299d20f4b00c2e6aa485a1d8932656753937b9b
|
||||||
CapacitorCordova: 8d93e14982f440181be7304aa9559ca631d77fff
|
CapacitorCordova: 8d93e14982f440181be7304aa9559ca631d77fff
|
||||||
CapacitorFilesystem: 59270a63c60836248812671aa3b15df673fbaf74
|
CapacitorFilesystem: 59270a63c60836248812671aa3b15df673fbaf74
|
||||||
@@ -157,6 +163,6 @@ SPEC CHECKSUMS:
|
|||||||
SQLCipher: 31878d8ebd27e5c96db0b7cb695c96e9f8ad77da
|
SQLCipher: 31878d8ebd27e5c96db0b7cb695c96e9f8ad77da
|
||||||
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c
|
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c
|
||||||
|
|
||||||
PODFILE CHECKSUM: f987510f7383b04a1b09ea8472bdadcd88b6c924
|
PODFILE CHECKSUM: 60f54b19c5a7a07343ab5ba9e5db49019fd86aa0
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -15,6 +15,7 @@
|
|||||||
"@capacitor/app": "^6.0.0",
|
"@capacitor/app": "^6.0.0",
|
||||||
"@capacitor/camera": "^6.0.0",
|
"@capacitor/camera": "^6.0.0",
|
||||||
"@capacitor/cli": "^6.2.0",
|
"@capacitor/cli": "^6.2.0",
|
||||||
|
"@capacitor/clipboard": "^6.0.2",
|
||||||
"@capacitor/core": "^6.2.0",
|
"@capacitor/core": "^6.2.0",
|
||||||
"@capacitor/filesystem": "^6.0.0",
|
"@capacitor/filesystem": "^6.0.0",
|
||||||
"@capacitor/ios": "^6.2.0",
|
"@capacitor/ios": "^6.2.0",
|
||||||
@@ -2301,6 +2302,14 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@capacitor/clipboard": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/clipboard/-/clipboard-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-jQ6UeFra5NP58THNZNb7HtzOZU7cHsjgrbQGVuMTgsK1uTILZpNeh+pfqHbKggba6KaNh5DAsJvEVQGpIR1VBA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@capacitor/core": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@capacitor/core": {
|
"node_modules/@capacitor/core": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-6.2.1.tgz",
|
||||||
|
|||||||
@@ -136,11 +136,7 @@
|
|||||||
"*.{js,ts,vue,css,json,yml,yaml}": "eslint --fix || true",
|
"*.{js,ts,vue,css,json,yml,yaml}": "eslint --fix || true",
|
||||||
"*.{md,markdown,mdc}": "markdownlint-cli2 --fix"
|
"*.{md,markdown,mdc}": "markdownlint-cli2 --fix"
|
||||||
},
|
},
|
||||||
"commitlint": {
|
|
||||||
"extends": [
|
|
||||||
"@commitlint/config-conventional"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor-community/electron": "^5.0.1",
|
"@capacitor-community/electron": "^5.0.1",
|
||||||
"@capacitor-community/sqlite": "6.0.2",
|
"@capacitor-community/sqlite": "6.0.2",
|
||||||
@@ -149,6 +145,7 @@
|
|||||||
"@capacitor/app": "^6.0.0",
|
"@capacitor/app": "^6.0.0",
|
||||||
"@capacitor/camera": "^6.0.0",
|
"@capacitor/camera": "^6.0.0",
|
||||||
"@capacitor/cli": "^6.2.0",
|
"@capacitor/cli": "^6.2.0",
|
||||||
|
"@capacitor/clipboard": "^6.0.2",
|
||||||
"@capacitor/core": "^6.2.0",
|
"@capacitor/core": "^6.2.0",
|
||||||
"@capacitor/filesystem": "^6.0.0",
|
"@capacitor/filesystem": "^6.0.0",
|
||||||
"@capacitor/ios": "^6.2.0",
|
"@capacitor/ios": "^6.2.0",
|
||||||
|
|||||||
@@ -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-[90] 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))] 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 }"
|
||||||
|
|||||||
@@ -14,4 +14,12 @@
|
|||||||
transform: translateX(100%);
|
transform: translateX(100%);
|
||||||
background-color: #FFF !important;
|
background-color: #FFF !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog-overlay {
|
||||||
|
@apply z-[100] fixed inset-0 bg-black/50 flex justify-center items-center p-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
@apply bg-white p-4 rounded-lg w-full max-w-lg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<!-- similar to UserNameDialog -->
|
<!-- similar to UserNameDialog -->
|
||||||
<template>
|
<template>
|
||||||
<div v-if="visible" :class="overlayClasses">
|
<div v-if="visible" class="dialog-overlay">
|
||||||
<div :class="dialogClasses">
|
<div class="dialog">
|
||||||
<h1 :class="titleClasses">{{ title }}</h1>
|
<h1 :class="titleClasses">{{ title }}</h1>
|
||||||
{{ message }}
|
{{ message }}
|
||||||
Note that their name is only stored on this device.
|
Note that their name is only stored on this device.
|
||||||
@@ -61,20 +61,6 @@ export default class ContactNameDialog extends Vue {
|
|||||||
title = "Contact Name";
|
title = "Contact Name";
|
||||||
visible = false;
|
visible = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS classes for the modal overlay backdrop
|
|
||||||
*/
|
|
||||||
get overlayClasses(): string {
|
|
||||||
return "z-index-50 fixed top-0 left-0 right-0 bottom-0 bg-black/50 flex justify-center items-center p-6";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS classes for the modal dialog container
|
|
||||||
*/
|
|
||||||
get dialogClasses(): string {
|
|
||||||
return "bg-white p-4 rounded-lg w-full max-w-[500px]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS classes for the dialog title
|
* CSS classes for the dialog title
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -212,30 +212,7 @@ export default class FeedFilters extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 50;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dialogFeedFilters.dialog-overlay {
|
#dialogFeedFilters.dialog-overlay {
|
||||||
z-index: 100;
|
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -665,27 +665,3 @@ export default class GiftedDialog extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 50;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -291,27 +291,3 @@ export default class GivenPrompts extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 50;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div v-if="isOpen" class="dialog-overlay">
|
||||||
v-if="isOpen"
|
<div class="dialog">
|
||||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
|
|
||||||
>
|
|
||||||
<div class="bg-white rounded-lg p-6 max-w-2xl w-full mx-4">
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h2 class="text-xl font-bold capitalize">{{ roleName }} Details</h2>
|
<h2 class="text-xl font-bold capitalize">{{ roleName }} Details</h2>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="visible" class="dialog-overlay z-[60]">
|
<div v-if="visible" class="dialog-overlay">
|
||||||
<div class="dialog relative">
|
<div class="dialog relative">
|
||||||
<div class="text-lg text-center font-bold relative">
|
<div class="text-lg text-center font-bold relative">
|
||||||
<h1 id="ViewHeading" class="text-center font-bold">
|
<h1 id="ViewHeading" class="text-center font-bold">
|
||||||
@@ -931,32 +931,6 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 50;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 700px;
|
|
||||||
max-height: 90vh;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add styles for diagnostic panel */
|
/* Add styles for diagnostic panel */
|
||||||
.diagnostic-panel {
|
.diagnostic-panel {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
|
|||||||
@@ -93,27 +93,3 @@ export default class InviteDialog extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 50;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -312,28 +312,3 @@ export default class OfferDialog extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.dialog-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
background: white;
|
|
||||||
padding: 1.5rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
max-width: 500px;
|
|
||||||
width: 90%;
|
|
||||||
max-height: 90vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -307,27 +307,3 @@ export default class OnboardingDialog extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 40;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -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 z-[60]">
|
<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">
|
||||||
@@ -628,34 +628,6 @@ export default class PhotoDialog extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Dialog overlay styling */
|
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 60;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dialog container styling */
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 700px;
|
|
||||||
max-height: 90vh;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Camera preview styling */
|
/* Camera preview styling */
|
||||||
.camera-preview {
|
.camera-preview {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -134,27 +134,3 @@ export default class UserNameDialog extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 50;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
185
src/services/ClipboardService.ts
Normal file
185
src/services/ClipboardService.ts
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import { Capacitor } from "@capacitor/core";
|
||||||
|
import { Clipboard } from "@capacitor/clipboard";
|
||||||
|
import { useClipboard } from "@vueuse/core";
|
||||||
|
import { logger } from "@/utils/logger";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Platform-agnostic clipboard service that handles both web and native platforms
|
||||||
|
* Provides reliable clipboard functionality across all platforms including iOS
|
||||||
|
*/
|
||||||
|
export class ClipboardService {
|
||||||
|
private static instance: ClipboardService | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get singleton instance of ClipboardService
|
||||||
|
*/
|
||||||
|
public static getInstance(): ClipboardService {
|
||||||
|
if (!ClipboardService.instance) {
|
||||||
|
ClipboardService.instance = new ClipboardService();
|
||||||
|
}
|
||||||
|
return ClipboardService.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy text to clipboard with platform-specific handling
|
||||||
|
*
|
||||||
|
* @param text - The text to copy to clipboard
|
||||||
|
* @returns Promise that resolves when copy is complete
|
||||||
|
* @throws Error if copy operation fails
|
||||||
|
*/
|
||||||
|
public async copyToClipboard(text: string): Promise<void> {
|
||||||
|
const platform = Capacitor.getPlatform();
|
||||||
|
const isNative = Capacitor.isNativePlatform();
|
||||||
|
|
||||||
|
logger.debug("[ClipboardService] Copying to clipboard:", {
|
||||||
|
text: text.substring(0, 50) + (text.length > 50 ? "..." : ""),
|
||||||
|
platform,
|
||||||
|
isNative,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isNative && (platform === "ios" || platform === "android")) {
|
||||||
|
// Use native Capacitor clipboard for mobile platforms
|
||||||
|
await this.copyNative(text);
|
||||||
|
} else {
|
||||||
|
// Use web clipboard API for web/desktop platforms
|
||||||
|
await this.copyWeb(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("[ClipboardService] Copy successful", {
|
||||||
|
platform,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("[ClipboardService] Copy failed:", {
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
platform,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy text using native Capacitor clipboard API
|
||||||
|
*
|
||||||
|
* @param text - The text to copy
|
||||||
|
* @returns Promise that resolves when copy is complete
|
||||||
|
*/
|
||||||
|
private async copyNative(text: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await Clipboard.write({
|
||||||
|
string: text,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("[ClipboardService] Native copy failed:", {
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
throw new Error(
|
||||||
|
`Native clipboard copy failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy text using web clipboard API with fallback
|
||||||
|
*
|
||||||
|
* @param text - The text to copy
|
||||||
|
* @returns Promise that resolves when copy is complete
|
||||||
|
*/
|
||||||
|
private async copyWeb(text: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Try VueUse clipboard first (handles some edge cases)
|
||||||
|
const { copy } = useClipboard();
|
||||||
|
await copy(text);
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(
|
||||||
|
"[ClipboardService] VueUse clipboard failed, trying native API:",
|
||||||
|
{
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fallback to native navigator.clipboard
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
} else {
|
||||||
|
throw new Error("Clipboard API not supported in this browser");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read text from clipboard (platform-specific)
|
||||||
|
*
|
||||||
|
* @returns Promise that resolves to the clipboard text
|
||||||
|
* @throws Error if read operation fails
|
||||||
|
*/
|
||||||
|
public async readFromClipboard(): Promise<string> {
|
||||||
|
const platform = Capacitor.getPlatform();
|
||||||
|
const isNative = Capacitor.isNativePlatform();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isNative && (platform === "ios" || platform === "android")) {
|
||||||
|
// Use native Capacitor clipboard for mobile platforms
|
||||||
|
const result = await Clipboard.read();
|
||||||
|
return result.value || "";
|
||||||
|
} else {
|
||||||
|
// Use web clipboard API for web/desktop platforms
|
||||||
|
if (navigator.clipboard && navigator.clipboard.readText) {
|
||||||
|
return await navigator.clipboard.readText();
|
||||||
|
} else {
|
||||||
|
throw new Error("Clipboard read API not supported in this browser");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("[ClipboardService] Read from clipboard failed:", {
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
platform,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if clipboard is supported on current platform
|
||||||
|
*
|
||||||
|
* @returns boolean indicating if clipboard is supported
|
||||||
|
*/
|
||||||
|
public isSupported(): boolean {
|
||||||
|
const platform = Capacitor.getPlatform();
|
||||||
|
const isNative = Capacitor.isNativePlatform();
|
||||||
|
|
||||||
|
if (isNative && (platform === "ios" || platform === "android")) {
|
||||||
|
return true; // Capacitor clipboard should work on native platforms
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check web clipboard support
|
||||||
|
return !!(navigator.clipboard && navigator.clipboard.writeText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to copy text to clipboard
|
||||||
|
* Uses the singleton ClipboardService instance
|
||||||
|
*
|
||||||
|
* @param text - The text to copy to clipboard
|
||||||
|
* @returns Promise that resolves when copy is complete
|
||||||
|
*/
|
||||||
|
export async function copyToClipboard(text: string): Promise<void> {
|
||||||
|
return ClipboardService.getInstance().copyToClipboard(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to read text from clipboard
|
||||||
|
* Uses the singleton ClipboardService instance
|
||||||
|
*
|
||||||
|
* @returns Promise that resolves to the clipboard text
|
||||||
|
*/
|
||||||
|
export async function readFromClipboard(): Promise<string> {
|
||||||
|
return ClipboardService.getInstance().readFromClipboard();
|
||||||
|
}
|
||||||
@@ -140,7 +140,7 @@ import { AxiosError } from "axios";
|
|||||||
import { Buffer } from "buffer/";
|
import { Buffer } from "buffer/";
|
||||||
import QRCodeVue3 from "qr-code-generator-vue3";
|
import QRCodeVue3 from "qr-code-generator-vue3";
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import { useClipboard } from "@vueuse/core";
|
|
||||||
import { QrcodeStream } from "vue-qrcode-reader";
|
import { QrcodeStream } from "vue-qrcode-reader";
|
||||||
|
|
||||||
import QuickNav from "../components/QuickNav.vue";
|
import QuickNav from "../components/QuickNav.vue";
|
||||||
@@ -183,8 +183,6 @@ import {
|
|||||||
NOTIFY_QR_PROCESSING_ERROR,
|
NOTIFY_QR_PROCESSING_ERROR,
|
||||||
createQRContactAddedMessage,
|
createQRContactAddedMessage,
|
||||||
createQRRegistrationSuccessMessage,
|
createQRRegistrationSuccessMessage,
|
||||||
QR_TIMEOUT_SHORT,
|
|
||||||
QR_TIMEOUT_MEDIUM,
|
|
||||||
QR_TIMEOUT_STANDARD,
|
QR_TIMEOUT_STANDARD,
|
||||||
QR_TIMEOUT_LONG,
|
QR_TIMEOUT_LONG,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
@@ -544,11 +542,7 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
did: contact.did,
|
did: contact.did,
|
||||||
name: contact.name,
|
name: contact.name,
|
||||||
});
|
});
|
||||||
this.notify.toast(
|
this.notify.toast("Submitted", NOTIFY_QR_REGISTRATION_SUBMITTED.message);
|
||||||
"Submitted",
|
|
||||||
NOTIFY_QR_REGISTRATION_SUBMITTED.message,
|
|
||||||
QR_TIMEOUT_SHORT,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const regResult = await register(
|
const regResult = await register(
|
||||||
@@ -624,18 +618,15 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Copy the URL to clipboard
|
// Copy the URL to clipboard
|
||||||
useClipboard()
|
const { copyToClipboard } = await import("../services/ClipboardService");
|
||||||
.copy(jwtUrl)
|
await copyToClipboard(jwtUrl);
|
||||||
.then(() => {
|
this.notify.toast(
|
||||||
this.notify.toast(
|
NOTIFY_QR_URL_COPIED.title,
|
||||||
"Copied",
|
NOTIFY_QR_URL_COPIED.message,
|
||||||
NOTIFY_QR_URL_COPIED.message,
|
);
|
||||||
QR_TIMEOUT_MEDIUM,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Failed to generate contact URL:", error);
|
this.$logAndConsole(`Error copying URL to clipboard: ${error}`, true);
|
||||||
this.notify.error("Failed to generate contact URL. Please try again.");
|
this.notify.error("Failed to copy URL to clipboard.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,13 +634,16 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
this.notify.info(NOTIFY_QR_CODE_HELP.message, QR_TIMEOUT_LONG);
|
this.notify.info(NOTIFY_QR_CODE_HELP.message, QR_TIMEOUT_LONG);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCopyDidToClipboard() {
|
async onCopyDidToClipboard() {
|
||||||
//this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing
|
//this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing
|
||||||
useClipboard()
|
try {
|
||||||
.copy(this.activeDid)
|
const { copyToClipboard } = await import("../services/ClipboardService");
|
||||||
.then(() => {
|
await copyToClipboard(this.activeDid);
|
||||||
this.notify.info(NOTIFY_QR_DID_COPIED.message, QR_TIMEOUT_LONG);
|
this.notify.info(NOTIFY_QR_DID_COPIED.message, QR_TIMEOUT_LONG);
|
||||||
});
|
} catch (error) {
|
||||||
|
this.$logAndConsole(`Error copying DID to clipboard: ${error}`, true);
|
||||||
|
this.notify.error("Failed to copy DID to clipboard.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openUserNameDialog() {
|
openUserNameDialog() {
|
||||||
@@ -745,7 +739,6 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
) {
|
) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.notify.confirm(
|
this.notify.confirm(
|
||||||
"Register",
|
|
||||||
"Do you want to register them?",
|
"Do you want to register them?",
|
||||||
{
|
{
|
||||||
onCancel: async (stopAsking?: boolean) => {
|
onCancel: async (stopAsking?: boolean) => {
|
||||||
|
|||||||
@@ -130,10 +130,9 @@ import { JWTPayload } from "did-jwt";
|
|||||||
import * as R from "ramda";
|
import * as R from "ramda";
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
||||||
import { useClipboard } from "@vueuse/core";
|
|
||||||
// Capacitor import removed - using PlatformService instead
|
|
||||||
|
|
||||||
import QuickNav from "../components/QuickNav.vue";
|
import QuickNav from "../components/QuickNav.vue";
|
||||||
|
import { copyToClipboard } from "../services/ClipboardService";
|
||||||
import EntityIcon from "../components/EntityIcon.vue";
|
import EntityIcon from "../components/EntityIcon.vue";
|
||||||
import GiftedDialog from "../components/GiftedDialog.vue";
|
import GiftedDialog from "../components/GiftedDialog.vue";
|
||||||
import OfferDialog from "../components/OfferDialog.vue";
|
import OfferDialog from "../components/OfferDialog.vue";
|
||||||
@@ -1192,12 +1191,14 @@ export default class ContactsView extends Vue {
|
|||||||
});
|
});
|
||||||
// Use production URL for sharing to avoid localhost issues in development
|
// Use production URL for sharing to avoid localhost issues in development
|
||||||
const contactsJwtUrl = `${APP_SERVER}/deep-link/contact-import/${contactsJwt}`;
|
const contactsJwtUrl = `${APP_SERVER}/deep-link/contact-import/${contactsJwt}`;
|
||||||
useClipboard()
|
try {
|
||||||
.copy(contactsJwtUrl)
|
await copyToClipboard(contactsJwtUrl);
|
||||||
.then(() => {
|
// Use notification helper
|
||||||
// Use notification helper
|
this.notify.copied(NOTIFY_CONTACT_LINK_COPIED.message);
|
||||||
this.notify.copied(NOTIFY_CONTACT_LINK_COPIED.message);
|
} catch (error) {
|
||||||
});
|
this.$logAndConsole(`Error copying to clipboard: ${error}`, true);
|
||||||
|
this.notify.error("Failed to copy to clipboard. Please try again.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private showCopySelectionsInfo() {
|
private showCopySelectionsInfo() {
|
||||||
|
|||||||
@@ -831,26 +831,3 @@ export default class DIDView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.dialog-overlay {
|
|
||||||
z-index: 50;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
.dialog {
|
|
||||||
background-color: white;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -144,8 +144,8 @@ export default class ShareMyContactInfoView extends Vue {
|
|||||||
* Copy the contact message to clipboard
|
* Copy the contact message to clipboard
|
||||||
*/
|
*/
|
||||||
private async copyToClipboard(message: string): Promise<void> {
|
private async copyToClipboard(message: string): Promise<void> {
|
||||||
const { useClipboard } = await import("@vueuse/core");
|
const { copyToClipboard } = await import("../services/ClipboardService");
|
||||||
await useClipboard().copy(message);
|
await copyToClipboard(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user