Compare commits

..

16 Commits

Author SHA1 Message Date
631aa468e6 Merge branch 'master' into registration-prompt-parity 2025-09-08 04:37:54 -04:00
ee29b517ce Merge pull request 'feat: implement seed phrase backup reminder system' (#195) from seed-phrase-backup-prompt into master
Reviewed-on: #195
2025-09-08 04:37:33 -04:00
f34c567ab4 Merge branch 'master' into seed-phrase-backup-prompt 2025-09-08 04:37:23 -04:00
bd072d95eb Merge pull request 'Fix offer fulfillment detection + consistencies between ClaimView and ConfirmGiftView' (#167) from claimview-fullfills-offer into master
Reviewed-on: #167
2025-09-08 04:37:03 -04:00
030960dd59 Merge branch 'master' into claimview-fullfills-offer 2025-09-08 04:36:48 -04:00
b138441d10 chore: change logging level to debug for debug messages 2025-09-07 18:34:57 -06:00
Jose Olarte III
ca1190aa47 refactor: modernize registration prompt notification in ContactQRScanShowView
- Replace deprecated notify.confirm() with modern $notify() API
- Add structured notification object with group, type, and title properties
- Extract registration prompt response logic into reusable handleRegistrationPromptResponse method
- Update settings method from $updateSettings to $saveSettings for consistency
- Align implementation with ContactsView.vue for better code consistency

This brings the registration prompt notification in ContactQRScanShowView up to parity with the modern implementation used in ContactsView.
2025-09-05 21:39:47 +08:00
Jose Olarte III
f38ec1daff feat: implement seed phrase backup reminder modal
Add comprehensive seed phrase backup reminder system to encourage users
to secure their identity after creating content.

Core Features:
- Modal dialog with "Backup Identifier Seed" and "Remind me Later" options
- 24-hour localStorage cooldown to prevent notification fatigue
- 1-second delay after success messages for better UX flow
- Focuses on claim creation actions, not confirmations

New Files:
- src/utils/seedPhraseReminder.ts: Core utility for reminder logic
- doc/seed-phrase-reminder-implementation.md: Comprehensive documentation

Trigger Points Added:
- Profile saving (AccountViewView)
- Claim creation (ClaimAddRawView, GiftedDialog, GiftedDetailsView)
- Offer creation (OfferDialog)
- QR code view exit (ContactQRScanFullView, ContactQRScanShowView)

Technical Implementation:
- Uses existing notification group modal system from App.vue
- Integrates with PlatformServiceMixin for account settings access
- Graceful error handling with logging fallbacks
- Non-blocking implementation that doesn't affect main functionality
- Modal stays open indefinitely (timeout: -1) until user interaction

User Experience:
- Non-intrusive reminders that respect user preferences
- Clear call-to-action for security-conscious users
- Seamless integration with existing workflows
- Maintains focus on content creation rather than confirmation actions
2025-09-03 19:50:29 +08:00
Jose Olarte III
ec2cab768b feat: Add seed backup tracking with database migration
- Add hasBackedUpSeed boolean flag to Settings interface
- Create database migration 003_add_hasBackedUpSeed_to_settings
- Update SeedBackupView to set flag when user reveals seed phrase
- Modify DataExportSection to conditionally show notification dot
- Implement robust error handling for database operations

The notification dot on the "Backup Identifier Seed" button only
appears while the user hasn't backed up their seed phrase. Once they
visit SeedBackupView and click "Reveal my Seed Phrase", the setting
is persisted and the notification dot disappears.
2025-09-03 15:52:29 +08:00
Jose Olarte III
1eeb013638 refactor(claims): extract offer fulfillment logic to utility function
Created extractOfferFulfillment utility in libs/util.ts to handle both
array and single object cases for fulfills field. Updated ClaimView and
ConfirmGiftView to use the shared utility, eliminating code duplication
and improving maintainability.
2025-09-01 21:24:46 +08:00
Jose Olarte III
3e5e2cd0bb fix(claims): handle single Offer object in fulfills field for ConfirmGiftView
Updated extractOfferFulfillment to support both array and single object
cases for the fulfills field, matching the fix applied to ClaimView.
Now handles when fulfills contains a single Offer object with @type "Offer".
2025-09-01 21:18:08 +08:00
Jose Olarte III
d87f44b75d fix(claims): handle single Offer object in fulfills field
Updated extractOfferFulfillment to support both array and single object
cases for the fulfills field. Previously only handled array format,
now also checks if fulfills is a single Offer object with @type "Offer".
2025-09-01 21:06:48 +08:00
Jose Olarte III
e5ad71505c Chore: move function to serverUtil
- capitalizeAndInsertSpacesBeforeCapsWithAPrefix() defined in two places, unified and moved to endorserServer.ts
- Use capitalizeAndInsertSpacesBeforeCaps() that's already defined in endorserServer.ts
2025-08-18 17:47:33 +08:00
19f0c270d3 chore: Rename variable for clarity 2025-08-17 14:13:50 -06:00
Jose Olarte III
693173f09d UI: wording and spacing consistencies
- Added grouped conditional spacing to ensure a top margin before fulfills links
- Brought over icons and wording from ConfirmGiftView to ClaimView
2025-08-14 20:12:28 +08:00
Jose Olarte III
a1388539c1 Fix: improve offer fulfillment detection in ClaimView
- Remove outdated fulfillsType logic that was checking for non-PlanAction items
- Keep only the new offer fulfillment extraction from fullClaim.fulfills array
- Apply consistent changes to both ClaimView and ConfirmGiftView

This ensures that "Fulfills Offer..." links appear correctly when gives are created from offers, by directly parsing the fulfills array instead of relying on API-processed fields that only capture the first relationship.
2025-08-14 18:53:12 +08:00
51 changed files with 866 additions and 1429 deletions

View File

@@ -21,7 +21,7 @@ alwaysApply: false
## Purpose ## Purpose
All interactions must _increase the human's competence over time_ while All interactions must *increase the human's competence over time* while
completing the task efficiently. The model may handle menial work and memory completing the task efficiently. The model may handle menial work and memory
extension, but must also promote learning, autonomy, and healthy work habits. extension, but must also promote learning, autonomy, and healthy work habits.
The model should also **encourage human interaction and collaboration** rather The model should also **encourage human interaction and collaboration** rather
@@ -31,7 +31,7 @@ machine-driven steps.
## Principles ## Principles
1. Competence over convenience: finish the task _and_ leave the human more 1. Competence over convenience: finish the task *and* leave the human more
capable next time. capable next time.
@@ -75,7 +75,7 @@ assumptions if unanswered.
### timebox_minutes ### timebox_minutes
_integer or null_ — When set to a positive integer (e.g., `5`), this acts *integer or null* — When set to a positive integer (e.g., `5`), this acts
as a **time budget** guiding the model to prioritize delivering the most as a **time budget** guiding the model to prioritize delivering the most
essential parts of the task within that constraint. essential parts of the task within that constraint.
@@ -91,7 +91,7 @@ Behavior when set:
3. **Signal Skipped Depth** — Omitted details should be listed under 3. **Signal Skipped Depth** — Omitted details should be listed under
_Deferred for depth_. *Deferred for depth*.
4. **Order by Value** — Start with blocking or high-value items, then 4. **Order by Value** — Start with blocking or high-value items, then
@@ -198,7 +198,7 @@ Default: Doer + short Mentor notes.
## Self-Check (model, before responding) ## Self-Check (model, before responding)
- [ ] Task done _and_ at least one competence lever included (≤120 words - [ ] Task done *and* at least one competence lever included (≤120 words
total) total)
- [ ] At least one collaboration/discussion hook present - [ ] At least one collaboration/discussion hook present
- [ ] Output follows the **Output Contract** sections - [ ] Output follows the **Output Contract** sections

View File

@@ -53,7 +53,7 @@ evidence-backed steps**.
- **Verifiable Outputs**: Include expected results, status codes, or - **Verifiable Outputs**: Include expected results, status codes, or
error messages error messages
- **Cite evidence** for _Works/Doesn't_ items (timestamps, filenames, - **Cite evidence** for *Works/Doesn't* items (timestamps, filenames,
line numbers, IDs/status codes, or logs). line numbers, IDs/status codes, or logs).
## Required Sections ## Required Sections
@@ -181,8 +181,8 @@ Before publishing, verify:
--- ---
**Status**: 🚢 ACTIVE — General ruleset extending _Base Context — Human **Status**: 🚢 ACTIVE — General ruleset extending *Base Context — Human
Competence First_ Competence First*
**Priority**: Critical **Priority**: Critical
**Estimated Effort**: Ongoing reference **Estimated Effort**: Ongoing reference

View File

@@ -16,7 +16,7 @@ language: Match repository languages and conventions
where it occurs; avoid new layers, indirection, or patterns unless where it occurs; avoid new layers, indirection, or patterns unless
strictly necessary. strictly necessary.
2. **Keep scope tight.** Implement only what is needed to satisfy the 2. **Keep scope tight.** Implement only what is needed to satisfy the
acceptance criteria and tests for _this_ issue. acceptance criteria and tests for *this* issue.
3. **Avoid speculative abstractions.** Use the **Rule of Three**: 3. **Avoid speculative abstractions.** Use the **Rule of Three**:
don't extract helpers/patterns until the third concrete usage proves don't extract helpers/patterns until the third concrete usage proves
the shape. the shape.
@@ -29,7 +29,7 @@ language: Match repository languages and conventions
7. **Targeted tests only.** Add the smallest set of tests that prove 7. **Targeted tests only.** Add the smallest set of tests that prove
the fix and guard against regression; don't rewrite suites. the fix and guard against regression; don't rewrite suites.
8. **Document the "why enough."** Include a one-paragraph note 8. **Document the "why enough."** Include a one-paragraph note
explaining why this minimal solution is sufficient _now_. explaining why this minimal solution is sufficient *now*.
## Future-Proofing Requires Evidence + Discussion ## Future-Proofing Requires Evidence + Discussion

View File

@@ -9,8 +9,8 @@ alwaysApply: false
**Date**: 2025-08-19 **Date**: 2025-08-19
**Status**: 🎯 **ACTIVE** - Asset management guidelines **Status**: 🎯 **ACTIVE** - Asset management guidelines
_Scope: Assets Only (icons, splashes, image pipelines) — not overall build *Scope: Assets Only (icons, splashes, image pipelines) — not overall build
orchestration_ orchestration*
## Intent ## Intent

View File

@@ -40,7 +40,7 @@ feature development, issue investigations, ADRs, and documentation**.
`2025-08-17`). `2025-08-17`).
- Avoid ambiguous terms like _recently_, _last month_, or _soon_. - Avoid ambiguous terms like *recently*, *last month*, or *soon*.
- For time-based experiments (e.g., A/B tests), always include: - For time-based experiments (e.g., A/B tests), always include:

View File

@@ -19,7 +19,7 @@
- Optionally provide UTC alongside if context requires cross-team clarity. - Optionally provide UTC alongside if context requires cross-team clarity.
- When interpreting relative terms like _now_, _today_, _last week_: - When interpreting relative terms like *now*, *today*, *last week*:
- Resolve them against the **developer's current time**. - Resolve them against the **developer's current time**.

View File

@@ -1,96 +0,0 @@
---
title: Documentation, References, and Model Agent Use
version: 1.1
alwaysApply: true
scope: code, project-plans
---
# Directive on Documentation, References, and Model Agent Use in Code and Project Plans
To ensure clarity, efficiency, and high-value documentation within code and project plans—and to leverage **model agents** (AI- or automation-based assistants) effectively—contributors must follow these rules:
---
## 1. Documentation and References Must Add Clear Value
- Only include documentation, comments, or reference links when they provide _new, meaningful information_ that assists understanding or decision-making.
- Avoid duplicating content already obvious in the codebase, version history, or linked project documents.
---
## 2. Eliminate Redundant or Noisy References
- Remove references that serve no purpose beyond filling space.
- Model agents may automatically flag and suggest removal of trivial references (e.g., links to unchanged boilerplate or self-evident context).
---
## 3. Explicit Role of Model Agents
Model agents are **active participants** in documentation quality control. Their tasks include:
- **Relevance Evaluation**: Automatically analyze references for their substantive contribution before inclusion.
- **Redundancy Detection**: Flag duplicate or trivial references across commits, files, or tasks.
- **Context Linking**: Suggest appropriate higher-level docs (designs, ADRs, meeting notes) when a code change touches multi-stage or cross-team items.
- **Placement Optimization**: Recommend centralization of references (e.g., in plan overviews, ADRs, or merge commit messages) rather than scattered low-value inline references.
- **Consistency Monitoring**: Ensure references align with team standards (e.g., ADR template, architecture repo, or external policy documents).
Contributors must treat agent recommendations as **first-pass reviews** but remain accountable for final human judgment.
---
## 4. Contextual References for Complex Items
- Use **centralized references** for multi-stage features (e.g., architectural docs, research threads).
- Keep inline code comments light; push broader context into centralized documents.
- Model agents may auto-summarize complex chains of discussion and attach them as a single reference point.
---
## 5. Centralization of Broader Context
- Store overarching context (design docs, proposals, workflows) in accessible, well-indexed places.
- Model agents should assist by **generating reference maps** that track where docs are cited across the codebase.
---
## 6. Focused Documentation
- Documentation should explain **why** and **how** decisions are made, not just what was changed.
- Model agents can auto-generate first-pass explanations from commit metadata, diffs, and linked issues—but humans must refine them for accuracy and intent.
---
## 7. Review and Accountability
- Reviewers and team leads must reject submissions containing unnecessary or low-quality documentation.
- Model agent outputs are aids, not replacements—contributors remain responsible for **final clarity and relevance**.
---
## 8. Continuous Improvement and Agent Feedback Loops
- Encourage iterative development of model agents so their evaluations become more precise over time.
- Contributions should include **feedback on agent suggestions** (e.g., accepted, rejected, or corrected) to train better future outputs.
- Agents should log patterns of “rejected” suggestions for refinement.
---
## 9. Workflow Overview (Mermaid Diagram)
```mermaid
flowchart TD
A[Contributor] -->|Writes Code & Draft Docs| B[Model Agent]
B -->|Evaluates References| C{Relevant?}
C -->|Yes| D[Suggest Placement & Context Links]
C -->|No| E[Flag Redundancy / Noise]
D --> F[Contributor Refines Docs]
E --> F
F --> G[Reviewer]
G -->|Approves / Requests Revisions| H[Final Documentation]
G -->|Feedback on Agent Suggestions| B
```
---
✅ **Outcome:** By integrating disciplined contributor standards with **model agent augmentation**, the team achieves documentation that is consistently _relevant, concise, centralized, and decision-focused_. AI ensures coverage and noise reduction, while humans ensure precision and judgment.

View File

@@ -192,7 +192,6 @@ Summary of key concepts and skills.
Where to apply this knowledge next. Where to apply this knowledge next.
``` ```
- [ ] Integration tests - [ ] Integration tests
- [ ] E2E tests - [ ] E2E tests

View File

@@ -16,10 +16,9 @@ inherits: base_context.mdc
**Author**: System/Shared **Author**: System/Shared
**Date**: 2025-08-21 (UTC) **Date**: 2025-08-21 (UTC)
**Status**: 🚢 ACTIVE — General ruleset extending _Base Context — Human Competence First_ **Status**: 🚢 ACTIVE — General ruleset extending *Base Context — Human Competence First*
> **Alignment with Base Context** > **Alignment with Base Context**
>
> - **Purpose fit**: Prioritizes human competence and collaboration while delivering reproducible artifacts. > - **Purpose fit**: Prioritizes human competence and collaboration while delivering reproducible artifacts.
> - **Output Contract**: This directive **adds universal constraints** for any technical topic while **inheriting** the Base Context contract sections. > - **Output Contract**: This directive **adds universal constraints** for any technical topic while **inheriting** the Base Context contract sections.
> - **Toggles honored**: Uses the same toggle semantics; defaults above can be overridden by the caller. > - **Toggles honored**: Uses the same toggle semantics; defaults above can be overridden by the caller.
@@ -27,11 +26,9 @@ inherits: base_context.mdc
--- ---
## Objective ## Objective
Produce a **developer-grade, reproducible guide** for any technical topic that onboards a competent practitioner **without meta narration** and **with evidence-backed steps**. Produce a **developer-grade, reproducible guide** for any technical topic that onboards a competent practitioner **without meta narration** and **with evidence-backed steps**.
## Scope & Constraints ## Scope & Constraints
- **One Markdown document** as the deliverable. - **One Markdown document** as the deliverable.
- Use **absolute dates** in **UTC** (e.g., `2025-08-21T14:22Z`) — avoid “today/yesterday”. - Use **absolute dates** in **UTC** (e.g., `2025-08-21T14:22Z`) — avoid “today/yesterday”.
- Include at least **one diagram** (Mermaid preferred). Choose the most fitting type: - Include at least **one diagram** (Mermaid preferred). Choose the most fitting type:
@@ -40,11 +37,10 @@ Produce a **developer-grade, reproducible guide** for any technical topic that o
- **APIs**: `curl` + one client library (e.g., `httpx` for Python). - **APIs**: `curl` + one client library (e.g., `httpx` for Python).
- **CLIs**: literal command blocks and expected output snippets. - **CLIs**: literal command blocks and expected output snippets.
- **Code**: minimal, self-contained samples (language appropriate). - **Code**: minimal, self-contained samples (language appropriate).
- Cite **evidence** for _Works/Doesnt_ items (timestamps, filenames, line numbers, IDs/status codes, or logs). - Cite **evidence** for *Works/Doesnt* items (timestamps, filenames, line numbers, IDs/status codes, or logs).
- If something is unknown, output `TODO:<missing>` — **never invent**. - If something is unknown, output `TODO:<missing>` — **never invent**.
## Required Sections (extends Base Output Contract) ## Required Sections (extends Base Output Contract)
Follow this exact order **after** the Base Contracts **Objective → Result → Use/Run** headers: Follow this exact order **after** the Base Contracts **Objective → Result → Use/Run** headers:
1. **Context & Scope** 1. **Context & Scope**
@@ -56,9 +52,9 @@ Follow this exact order **after** the Base Contracts **Objective → Result
4. **Architecture / Process Overview** 4. **Architecture / Process Overview**
- Short prose + **one diagram** selected from the list above. - Short prose + **one diagram** selected from the list above.
5. **Interfaces & Contracts (choose one)** 5. **Interfaces & Contracts (choose one)**
- **API-based**: Endpoint table (_Step, Method, Path/URL, Auth, Key Headers/Params, Sample Req/Resp ref_). - **API-based**: Endpoint table (*Step, Method, Path/URL, Auth, Key Headers/Params, Sample Req/Resp ref*).
- **Data/Files**: I/O contract table (_Source, Format, Schema/Columns, Size, Validation rules_). - **Data/Files**: I/O contract table (*Source, Format, Schema/Columns, Size, Validation rules*).
- **Systems/Hardware**: Interfaces table (_Port/Bus, Protocol, Voltage/Timing, Constraints_). - **Systems/Hardware**: Interfaces table (*Port/Bus, Protocol, Voltage/Timing, Constraints*).
6. **Repro: End-to-End Procedure** 6. **Repro: End-to-End Procedure**
- Minimal copy-paste steps with code/commands and **expected outputs**. - Minimal copy-paste steps with code/commands and **expected outputs**.
7. **What Works (with Evidence)** 7. **What Works (with Evidence)**
@@ -73,19 +69,16 @@ Follow this exact order **after** the Base Contracts **Objective → Result
- Canonical docs, specs, tickets, prior analyses. - Canonical docs, specs, tickets, prior analyses.
> **Competence Hooks (per Base Context; keep lightweight):** > **Competence Hooks (per Base Context; keep lightweight):**
> > - *Why this works* (≤3 bullets) — core invariants or guarantees.
> - _Why this works_ (≤3 bullets) — core invariants or guarantees. > - *Common pitfalls* (≤3 bullets) — the traps we saw in evidence.
> - _Common pitfalls_ (≤3 bullets) — the traps we saw in evidence. > - *Next skill unlock* (1 line) — the next capability to implement/learn.
> - _Next skill unlock_ (1 line) — the next capability to implement/learn. > - *Teach-back* (1 line) — prompt the reader to restate the flow/architecture.
> - _Teach-back_ (1 line) — prompt the reader to restate the flow/architecture.
> **Collaboration Hooks (per Base Context):** > **Collaboration Hooks (per Base Context):**
>
> - Name reviewers for **Interfaces & Contracts** and the **diagram**. > - Name reviewers for **Interfaces & Contracts** and the **diagram**.
> - Short **sign-off checklist** before merging/publishing the guide. > - Short **sign-off checklist** before merging/publishing the guide.
## Do / Dont (Base-aligned) ## Do / Dont (Base-aligned)
- **Do** quantify progress only against a defined scope with acceptance criteria. - **Do** quantify progress only against a defined scope with acceptance criteria.
- **Do** include minimal sample payloads/headers or I/O schemas; redact sensitive values. - **Do** include minimal sample payloads/headers or I/O schemas; redact sensitive values.
- **Do** keep commentary lean; if timeboxed, move depth to **Deferred for depth**. - **Do** keep commentary lean; if timeboxed, move depth to **Deferred for depth**.
@@ -93,7 +86,6 @@ Follow this exact order **after** the Base Contracts **Objective → Result
- **Dont** include IDE-specific chatter or internal rules unrelated to the task. - **Dont** include IDE-specific chatter or internal rules unrelated to the task.
## Validation Checklist (self-check before returning) ## Validation Checklist (self-check before returning)
- [ ] All Required Sections present and ordered. - [ ] All Required Sections present and ordered.
- [ ] Diagram compiles (basic Mermaid syntax) and fits the problem. - [ ] Diagram compiles (basic Mermaid syntax) and fits the problem.
- [ ] If API-based, **Auth** and **Key Headers/Params** are listed for each endpoint. - [ ] If API-based, **Auth** and **Key Headers/Params** are listed for each endpoint.
@@ -104,7 +96,6 @@ Follow this exact order **after** the Base Contracts **Objective → Result
- [ ] Base **Output Contract** sections satisfied (Objective/Result/Use/Run/Competence/Collaboration/Assumptions/References). - [ ] Base **Output Contract** sections satisfied (Objective/Result/Use/Run/Competence/Collaboration/Assumptions/References).
## Universal Template (fill-in) ## Universal Template (fill-in)
```markdown ```markdown
# <Title> — Working Notes (As of YYYY-MM-DDTHH:MMZ) # <Title> — Working Notes (As of YYYY-MM-DDTHH:MMZ)
@@ -141,46 +132,37 @@ Follow this exact order **after** the Base Contracts **Objective → Result
``` ```
## Interfaces & Contracts ## Interfaces & Contracts
### If API-based ### If API-based
| Step | Method | Path/URL | Auth | Key Headers/Params | Sample | | Step | Method | Path/URL | Auth | Key Headers/Params | Sample |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| <…> | <…> | <…> | <…> | <…> | below | | <…> | <…> | <…> | <…> | <…> | below |
### If Data/Files ### If Data/Files
| Source | Format | Schema/Columns | Size | Validation | | Source | Format | Schema/Columns | Size | Validation |
|---|---|---|---|---| |---|---|---|---|---|
| <…> | <…> | <…> | <…> | <…> | | <…> | <…> | <…> | <…> | <…> |
### If Systems/Hardware ### If Systems/Hardware
| Interface | Protocol | Timing/Voltage | Constraints | Notes | | Interface | Protocol | Timing/Voltage | Constraints | Notes |
|---|---|---|---|---| |---|---|---|---|---|
| <…> | <…> | <…> | <…> | <…> | | <…> | <…> | <…> | <…> | <…> |
## Repro: End-to-End Procedure ## Repro: End-to-End Procedure
```bash ```bash
# commands / curl examples (redacted where necessary) # commands / curl examples (redacted where necessary)
``` ```
```python ```python
# minimal client library example (language appropriate) # minimal client library example (language appropriate)
``` ```
> Expected output: <snippet/checks> > Expected output: <snippet/checks>
## What Works (Evidence) ## What Works (Evidence)
- ✅ <short statement> - ✅ <short statement>
- **Time**: <YYYY-MM-DDTHH:MMZ> - **Time**: <YYYY-MM-DDTHH:MMZ>
- **Evidence**: file/line/log or request id/status - **Evidence**: file/line/log or request id/status
- **Verify at**: <where> - **Verify at**: <where>
## What Doesnt (Evidence & Hypotheses) ## What Doesnt (Evidence & Hypotheses)
- ❌ <short failure> at `<component/endpoint/file>` - ❌ <short failure> at `<component/endpoint/file>`
- **Time**: <YYYY-MM-DDTHH:MMZ> - **Time**: <YYYY-MM-DDTHH:MMZ>
- **Evidence**: <snippet/id/status> - **Evidence**: <snippet/id/status>
@@ -188,45 +170,37 @@ Follow this exact order **after** the Base Contracts **Objective → Result
- **Next probe**: <short> - **Next probe**: <short>
## Risks, Limits, Assumptions ## Risks, Limits, Assumptions
<bullets: limits, security boundaries, retries/backoff, idempotency, SLOs> <bullets: limits, security boundaries, retries/backoff, idempotency, SLOs>
## Next Steps ## Next Steps
| Owner | Task | Exit Criteria | Target Date (UTC) | | Owner | Task | Exit Criteria | Target Date (UTC) |
|---|---|---|---| |---|---|---|---|
| <name> | <action> | <measurable outcome> | <YYYY-MM-DD> | | <name> | <action> | <measurable outcome> | <YYYY-MM-DD> |
## References ## References
<links/titles> <links/titles>
## Competence Hooks ## Competence Hooks
- *Why this works*: <≤3 bullets>
- _Why this works_: <≤3 bullets> - *Common pitfalls*: <≤3 bullets>
- _Common pitfalls_: <≤3 bullets> - *Next skill unlock*: <1 line>
- _Next skill unlock_: <1 line> - *Teach-back*: <1 line>
- _Teach-back_: <1 line>
## Collaboration Hooks ## Collaboration Hooks
- Reviewers: <names/roles> - Reviewers: <names/roles>
- Sign-off checklist: <≤5 checks> - Sign-off checklist: <≤5 checks>
## Assumptions & Limits ## Assumptions & Limits
<bullets> <bullets>
## Deferred for depth ## Deferred for depth
<park deeper material here to respect timeboxing> <park deeper material here to respect timeboxing>
``` ```
--- ---
**Notes for Implementers:** **Notes for Implementers:**
- Respect Base *Do-Not* (no filler, no invented facts, no censorship).
- Respect Base _Do-Not_ (no filler, no invented facts, no censorship).
- Prefer clarity over completeness when timeboxed; capture unknowns explicitly. - Prefer clarity over completeness when timeboxed; capture unknowns explicitly.
- Apply historical comment management rules (see `.cursor/rules/historical_comment_management.mdc`) - Apply historical comment management rules (see `.cursor/rules/historical_comment_management.mdc`)
- Apply realistic time estimation rules (see `.cursor/rules/realistic_time_estimation.mdc`) - Apply realistic time estimation rules (see `.cursor/rules/realistic_time_estimation.mdc`)

View File

@@ -82,7 +82,6 @@ common investigation pitfalls.
### **Safe Diagnosis Commands** ### **Safe Diagnosis Commands**
✅ **Safe to use during diagnosis:** ✅ **Safe to use during diagnosis:**
- `npm run lint-fix` - Syntax and style checking - `npm run lint-fix` - Syntax and style checking
- `npm run type-check` - TypeScript validation (if available) - `npm run type-check` - TypeScript validation (if available)
- `git status` - Version control status - `git status` - Version control status
@@ -91,7 +90,6 @@ common investigation pitfalls.
- `grep_search` - Text pattern searching - `grep_search` - Text pattern searching
❌ **Never use during diagnosis:** ❌ **Never use during diagnosis:**
- `npm run build:web` - Blocks chat - `npm run build:web` - Blocks chat
- `npm run build:electron` - Blocks chat - `npm run build:electron` - Blocks chat
- `npm run build:capacitor` - Blocks chat - `npm run build:capacitor` - Blocks chat

View File

@@ -36,7 +36,6 @@ that are essential for all AI interactions.
**This meta-rule enforces current workflow mode constraints for all interactions:** **This meta-rule enforces current workflow mode constraints for all interactions:**
### **Current Workflow State** ### **Current Workflow State**
```json ```json
{ {
"workflowState": { "workflowState": {
@@ -63,31 +62,26 @@ that are essential for all AI interactions.
### **Mode-Specific Enforcement** ### **Mode-Specific Enforcement**
**Diagnosis Mode (read_only):** **Diagnosis Mode (read_only):**
- ❌ **Forbidden**: File modification, code creation, build commands, git commits - ❌ **Forbidden**: File modification, code creation, build commands, git commits
- ✅ **Allowed**: File reading, code analysis, investigation, documentation - ✅ **Allowed**: File reading, code analysis, investigation, documentation
- **Response**: Guide user toward investigation and analysis, not implementation - **Response**: Guide user toward investigation and analysis, not implementation
**Fixing Mode (implementation):** **Fixing Mode (implementation):**
- ✅ **Allowed**: File modification, code creation, build commands, testing, git commits - ✅ **Allowed**: File modification, code creation, build commands, testing, git commits
- ❌ **Forbidden**: None (full implementation mode) - ❌ **Forbidden**: None (full implementation mode)
- **Response**: Proceed with implementation and testing - **Response**: Proceed with implementation and testing
**Planning Mode (design_only):** **Planning Mode (design_only):**
- ❌ **Forbidden**: Implementation, coding, building, deployment - ❌ **Forbidden**: Implementation, coding, building, deployment
- ✅ **Allowed**: Analysis, design, estimation, documentation, architecture - ✅ **Allowed**: Analysis, design, estimation, documentation, architecture
- **Response**: Focus on planning and design, not implementation - **Response**: Focus on planning and design, not implementation
**Research Mode (investigation):** **Research Mode (investigation):**
- ❌ **Forbidden**: File modification, implementation, deployment - ❌ **Forbidden**: File modification, implementation, deployment
- ✅ **Allowed**: Investigation, analysis, research, documentation - ✅ **Allowed**: Investigation, analysis, research, documentation
- **Response**: Focus on investigation and analysis - **Response**: Focus on investigation and analysis
**Documentation Mode (writing_only):** **Documentation Mode (writing_only):**
- ❌ **Forbidden**: Implementation, coding, building, deployment - ❌ **Forbidden**: Implementation, coding, building, deployment
- ✅ **Allowed**: Writing, editing, formatting, structuring, reviewing - ✅ **Allowed**: Writing, editing, formatting, structuring, reviewing
- **Response**: Focus on documentation creation and improvement - **Response**: Focus on documentation creation and improvement

View File

@@ -51,7 +51,6 @@ providing technical descriptions.
## When to Use ## When to Use
**Use this meta-rule when**: **Use this meta-rule when**:
- Writing new documentation - Writing new documentation
- Updating existing documentation - Updating existing documentation
- Creating technical guides - Creating technical guides
@@ -108,7 +107,6 @@ providing technical descriptions.
### **Document Structure** ### **Document Structure**
**Mandatory Sections**: **Mandatory Sections**:
- **Overview**: Clear purpose and scope with educational context - **Overview**: Clear purpose and scope with educational context
- **Why This Matters**: Business value and user benefit explanation - **Why This Matters**: Business value and user benefit explanation
- **Core Concepts**: Fundamental understanding before implementation - **Core Concepts**: Fundamental understanding before implementation
@@ -118,7 +116,6 @@ providing technical descriptions.
- **Next Steps**: Where to go from here - **Next Steps**: Where to go from here
**Optional Sections**: **Optional Sections**:
- **Background**: Historical context and evolution - **Background**: Historical context and evolution
- **Alternatives**: Other approaches and trade-offs - **Alternatives**: Other approaches and trade-offs
- **Advanced Topics**: Deep dive into complex scenarios - **Advanced Topics**: Deep dive into complex scenarios
@@ -127,7 +124,6 @@ providing technical descriptions.
### **Writing Style** ### **Writing Style**
**Educational Approach**: **Educational Approach**:
- **Conversational tone**: Write as if explaining to a colleague - **Conversational tone**: Write as if explaining to a colleague
- **Progressive disclosure**: Start simple, add complexity gradually - **Progressive disclosure**: Start simple, add complexity gradually
- **Active voice**: "You can do this" not "This can be done" - **Active voice**: "You can do this" not "This can be done"
@@ -135,7 +131,6 @@ providing technical descriptions.
- **Analogies**: Use familiar concepts to explain complex ideas - **Analogies**: Use familiar concepts to explain complex ideas
**Technical Accuracy**: **Technical Accuracy**:
- **Precise language**: Use exact technical terms consistently - **Precise language**: Use exact technical terms consistently
- **Code examples**: Working, tested code snippets - **Code examples**: Working, tested code snippets
- **Version information**: Specify applicable versions and platforms - **Version information**: Specify applicable versions and platforms
@@ -144,7 +139,6 @@ providing technical descriptions.
### **Content Quality Standards** ### **Content Quality Standards**
**Educational Value**: **Educational Value**:
- [ ] **Concept clarity**: Reader understands the fundamental idea - [ ] **Concept clarity**: Reader understands the fundamental idea
- [ ] **Context relevance**: Reader knows when to apply the knowledge - [ ] **Context relevance**: Reader knows when to apply the knowledge
- [ ] **Practical application**: Reader can implement the solution - [ ] **Practical application**: Reader can implement the solution
@@ -152,7 +146,6 @@ providing technical descriptions.
- [ ] **Next steps**: Reader knows where to continue learning - [ ] **Next steps**: Reader knows where to continue learning
**Technical Accuracy**: **Technical Accuracy**:
- [ ] **Fact verification**: All technical details are correct - [ ] **Fact verification**: All technical details are correct
- [ ] **Code validation**: Examples compile and run correctly - [ ] **Code validation**: Examples compile and run correctly
- [ ] **Version compatibility**: Platform and version requirements clear - [ ] **Version compatibility**: Platform and version requirements clear
@@ -190,7 +183,6 @@ providing technical descriptions.
### **Review Checklist** ### **Review Checklist**
**Educational Quality**: **Educational Quality**:
- [ ] **Clear learning objective**: What will the reader learn? - [ ] **Clear learning objective**: What will the reader learn?
- [ ] **Appropriate complexity**: Matches target audience knowledge - [ ] **Appropriate complexity**: Matches target audience knowledge
- [ ] **Progressive disclosure**: Information builds logically - [ ] **Progressive disclosure**: Information builds logically
@@ -198,7 +190,6 @@ providing technical descriptions.
- [ ] **Common questions**: Anticipates and answers reader questions - [ ] **Common questions**: Anticipates and answers reader questions
**Technical Quality**: **Technical Quality**:
- [ ] **Accuracy**: All technical details verified - [ ] **Accuracy**: All technical details verified
- [ ] **Completeness**: Covers all necessary information - [ ] **Completeness**: Covers all necessary information
- [ ] **Consistency**: Terminology and formatting consistent - [ ] **Consistency**: Terminology and formatting consistent

View File

@@ -9,31 +9,26 @@ alwaysApply: false
**Status**: 🎯 **ACTIVE** - Playwright test debugging guidelines **Status**: 🎯 **ACTIVE** - Playwright test debugging guidelines
## Objective ## Objective
Provide systematic approach for investigating Playwright test failures with focus on UI element conflicts, timing issues, and selector ambiguity. Provide systematic approach for investigating Playwright test failures with focus on UI element conflicts, timing issues, and selector ambiguity.
## Context & Scope ## Context & Scope
- **Audience**: Developers debugging Playwright test failures - **Audience**: Developers debugging Playwright test failures
- **In scope**: Test failure analysis, selector conflicts, UI state investigation, timing issues - **In scope**: Test failure analysis, selector conflicts, UI state investigation, timing issues
- **Out of scope**: Test writing best practices, CI/CD configuration - **Out of scope**: Test writing best practices, CI/CD configuration
## Artifacts & Links ## Artifacts & Links
- Test results: `test-results/` directory - Test results: `test-results/` directory
- Error context: `error-context.md` files with page snapshots - Error context: `error-context.md` files with page snapshots
- Trace files: `trace.zip` files for failed tests - Trace files: `trace.zip` files for failed tests
- HTML reports: Interactive test reports with screenshots - HTML reports: Interactive test reports with screenshots
## Environment & Preconditions ## Environment & Preconditions
- OS/Runtime: Linux/Windows/macOS with Node.js - OS/Runtime: Linux/Windows/macOS with Node.js
- Versions: Playwright test framework, browser drivers - Versions: Playwright test framework, browser drivers
- Services: Local test server (localhost:8080), test data setup - Services: Local test server (localhost:8080), test data setup
- Auth mode: None required for test investigation - Auth mode: None required for test investigation
## Architecture / Process Overview ## Architecture / Process Overview
Playwright test investigation follows a systematic diagnostic workflow that leverages built-in debugging tools and error context analysis. Playwright test investigation follows a systematic diagnostic workflow that leverages built-in debugging tools and error context analysis.
```mermaid ```mermaid
@@ -62,7 +57,6 @@ flowchart TD
## Interfaces & Contracts ## Interfaces & Contracts
### Test Results Structure ### Test Results Structure
| Component | Format | Content | Validation | | Component | Format | Content | Validation |
|---|---|---|---| |---|---|---|---|
| Error Context | Markdown | Page snapshot in YAML | Verify DOM state matches test expectations | | Error Context | Markdown | Page snapshot in YAML | Verify DOM state matches test expectations |
@@ -71,7 +65,6 @@ flowchart TD
| JSON Results | JSON | Machine-readable results | Parse for automated analysis | | JSON Results | JSON | Machine-readable results | Parse for automated analysis |
### Investigation Commands ### Investigation Commands
| Step | Command | Expected Output | Notes | | Step | Command | Expected Output | Notes |
|---|---|---|---| |---|---|---|---|
| Locate failed tests | `find test-results -name "*test-name*"` | Test result directories | Use exact test name patterns | | Locate failed tests | `find test-results -name "*test-name*"` | Test result directories | Use exact test name patterns |
@@ -81,7 +74,6 @@ flowchart TD
## Repro: End-to-End Investigation Procedure ## Repro: End-to-End Investigation Procedure
### 1. Locate Failed Test Results ### 1. Locate Failed Test Results
```bash ```bash
# Find all results for a specific test # Find all results for a specific test
find test-results -name "*test-name*" -type d find test-results -name "*test-name*" -type d
@@ -91,7 +83,6 @@ find test-results -name "error-context.md" | head -5
``` ```
### 2. Analyze Error Context ### 2. Analyze Error Context
```bash ```bash
# Read error context for specific test # Read error context for specific test
cat test-results/test-name-test-description-browser/error-context.md cat test-results/test-name-test-description-browser/error-context.md
@@ -101,7 +92,6 @@ grep -A 10 -B 5 "button.*Yes\|button.*No" test-results/*/error-context.md
``` ```
### 3. Check Trace Files ### 3. Check Trace Files
```bash ```bash
# List available trace files # List available trace files
find test-results -name "*.zip" | grep trace find test-results -name "*.zip" | grep trace
@@ -111,7 +101,6 @@ npx playwright show-trace test-results/test-name/trace.zip
``` ```
### 4. Investigate Selector Issues ### 4. Investigate Selector Issues
```typescript ```typescript
// Check for multiple elements with same text // Check for multiple elements with same text
await page.locator('button:has-text("Yes")').count(); // Should be 1 await page.locator('button:has-text("Yes")').count(); // Should be 1
@@ -121,7 +110,6 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
``` ```
## What Works (Evidence) ## What Works (Evidence)
- ✅ **Error context files** provide page snapshots showing exact DOM state at failure - ✅ **Error context files** provide page snapshots showing exact DOM state at failure
- **Time**: 2025-08-21T14:22Z - **Time**: 2025-08-21T14:22Z
- **Evidence**: `test-results/60-new-activity-New-offers-for-another-user-chromium/error-context.md` shows both alerts visible - **Evidence**: `test-results/60-new-activity-New-offers-for-another-user-chromium/error-context.md` shows both alerts visible
@@ -138,7 +126,6 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
- **Verify at**: Error context markdown files - **Verify at**: Error context markdown files
## What Doesn't (Evidence & Hypotheses) ## What Doesn't (Evidence & Hypotheses)
- ❌ **Generic selectors** fail with multiple similar elements at `test-playwright/testUtils.ts:161` - ❌ **Generic selectors** fail with multiple similar elements at `test-playwright/testUtils.ts:161`
- **Time**: 2025-08-21T14:22Z - **Time**: 2025-08-21T14:22Z
- **Evidence**: `button:has-text("Yes")` matches both "Yes" and "Yes, Export Data" - **Evidence**: `button:has-text("Yes")` matches both "Yes" and "Yes, Export Data"
@@ -152,14 +139,12 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
- **Next probe**: Implement alert queuing or prevent overlapping alerts - **Next probe**: Implement alert queuing or prevent overlapping alerts
## Risks, Limits, Assumptions ## Risks, Limits, Assumptions
- **Trace file size**: Large trace files may impact storage and analysis time - **Trace file size**: Large trace files may impact storage and analysis time
- **Browser compatibility**: Trace viewer requires specific browser support - **Browser compatibility**: Trace viewer requires specific browser support
- **Test isolation**: Shared state between tests may affect investigation results - **Test isolation**: Shared state between tests may affect investigation results
- **Timing sensitivity**: Tests may pass/fail based on system performance - **Timing sensitivity**: Tests may pass/fail based on system performance
## Next Steps ## Next Steps
| Owner | Task | Exit Criteria | Target Date (UTC) | | Owner | Task | Exit Criteria | Target Date (UTC) |
|---|---|---|---| |---|---|---|---|
| Development Team | Fix test selectors for multiple alerts | All tests pass consistently | 2025-08-22 | | Development Team | Fix test selectors for multiple alerts | All tests pass consistently | 2025-08-22 |
@@ -167,25 +152,21 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
| Development Team | Add test IDs to alert buttons | Unique selectors for all UI elements | 2025-08-28 | | Development Team | Add test IDs to alert buttons | Unique selectors for all UI elements | 2025-08-28 |
## References ## References
- [Playwright Trace Viewer Documentation](https://playwright.dev/docs/trace-viewer) - [Playwright Trace Viewer Documentation](https://playwright.dev/docs/trace-viewer)
- [Playwright Test Results](https://playwright.dev/docs/test-reporters) - [Playwright Test Results](https://playwright.dev/docs/test-reporters)
- [Test Investigation Workflow](./research_diagnostic.mdc) - [Test Investigation Workflow](./research_diagnostic.mdc)
## Competence Hooks ## Competence Hooks
- **Why this works**: Systematic investigation leverages Playwright's built-in debugging tools to identify root causes - **Why this works**: Systematic investigation leverages Playwright's built-in debugging tools to identify root causes
- **Common pitfalls**: Generic selectors fail with multiple similar elements; timing issues create race conditions; alert stacking causes UI conflicts - **Common pitfalls**: Generic selectors fail with multiple similar elements; timing issues create race conditions; alert stacking causes UI conflicts
- **Next skill unlock**: Implement unique test IDs and handle alert dismissal order in test flows - **Next skill unlock**: Implement unique test IDs and handle alert dismissal order in test flows
- **Teach-back**: "How would you investigate a Playwright test failure using error context, trace files, and page snapshots?" - **Teach-back**: "How would you investigate a Playwright test failure using error context, trace files, and page snapshots?"
## Collaboration Hooks ## Collaboration Hooks
- **Reviewers**: QA team, test automation engineers - **Reviewers**: QA team, test automation engineers
- **Sign-off checklist**: Error context analyzed, trace files reviewed, root cause identified, fix implemented and tested - **Sign-off checklist**: Error context analyzed, trace files reviewed, root cause identified, fix implemented and tested
## Assumptions & Limits ## Assumptions & Limits
- Test results directory structure follows Playwright conventions - Test results directory structure follows Playwright conventions
- Trace files are enabled in configuration (`trace: "retain-on-failure"`) - Trace files are enabled in configuration (`trace: "retain-on-failure"`)
- Error context files contain valid YAML page snapshots - Error context files contain valid YAML page snapshots
@@ -197,7 +178,6 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
**Priority**: High **Priority**: High
**Maintainer**: Development team **Maintainer**: Development team
**Next Review**: 2025-09-21 **Next Review**: 2025-09-21
# Playwright Test Investigation — Harbor Pilot Directive # Playwright Test Investigation — Harbor Pilot Directive
**Author**: Matthew Raymer **Author**: Matthew Raymer
@@ -205,31 +185,26 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
**Status**: 🎯 **ACTIVE** - Playwright test debugging guidelines **Status**: 🎯 **ACTIVE** - Playwright test debugging guidelines
## Objective ## Objective
Provide systematic approach for investigating Playwright test failures with focus on UI element conflicts, timing issues, and selector ambiguity. Provide systematic approach for investigating Playwright test failures with focus on UI element conflicts, timing issues, and selector ambiguity.
## Context & Scope ## Context & Scope
- **Audience**: Developers debugging Playwright test failures - **Audience**: Developers debugging Playwright test failures
- **In scope**: Test failure analysis, selector conflicts, UI state investigation, timing issues - **In scope**: Test failure analysis, selector conflicts, UI state investigation, timing issues
- **Out of scope**: Test writing best practices, CI/CD configuration - **Out of scope**: Test writing best practices, CI/CD configuration
## Artifacts & Links ## Artifacts & Links
- Test results: `test-results/` directory - Test results: `test-results/` directory
- Error context: `error-context.md` files with page snapshots - Error context: `error-context.md` files with page snapshots
- Trace files: `trace.zip` files for failed tests - Trace files: `trace.zip` files for failed tests
- HTML reports: Interactive test reports with screenshots - HTML reports: Interactive test reports with screenshots
## Environment & Preconditions ## Environment & Preconditions
- OS/Runtime: Linux/Windows/macOS with Node.js - OS/Runtime: Linux/Windows/macOS with Node.js
- Versions: Playwright test framework, browser drivers - Versions: Playwright test framework, browser drivers
- Services: Local test server (localhost:8080), test data setup - Services: Local test server (localhost:8080), test data setup
- Auth mode: None required for test investigation - Auth mode: None required for test investigation
## Architecture / Process Overview ## Architecture / Process Overview
Playwright test investigation follows a systematic diagnostic workflow that leverages built-in debugging tools and error context analysis. Playwright test investigation follows a systematic diagnostic workflow that leverages built-in debugging tools and error context analysis.
```mermaid ```mermaid
@@ -258,7 +233,6 @@ flowchart TD
## Interfaces & Contracts ## Interfaces & Contracts
### Test Results Structure ### Test Results Structure
| Component | Format | Content | Validation | | Component | Format | Content | Validation |
|---|---|---|---| |---|---|---|---|
| Error Context | Markdown | Page snapshot in YAML | Verify DOM state matches test expectations | | Error Context | Markdown | Page snapshot in YAML | Verify DOM state matches test expectations |
@@ -267,7 +241,6 @@ flowchart TD
| JSON Results | JSON | Machine-readable results | Parse for automated analysis | | JSON Results | JSON | Machine-readable results | Parse for automated analysis |
### Investigation Commands ### Investigation Commands
| Step | Command | Expected Output | Notes | | Step | Command | Expected Output | Notes |
|---|---|---|---| |---|---|---|---|
| Locate failed tests | `find test-results -name "*test-name*"` | Test result directories | Use exact test name patterns | | Locate failed tests | `find test-results -name "*test-name*"` | Test result directories | Use exact test name patterns |
@@ -277,7 +250,6 @@ flowchart TD
## Repro: End-to-End Investigation Procedure ## Repro: End-to-End Investigation Procedure
### 1. Locate Failed Test Results ### 1. Locate Failed Test Results
```bash ```bash
# Find all results for a specific test # Find all results for a specific test
find test-results -name "*test-name*" -type d find test-results -name "*test-name*" -type d
@@ -287,7 +259,6 @@ find test-results -name "error-context.md" | head -5
``` ```
### 2. Analyze Error Context ### 2. Analyze Error Context
```bash ```bash
# Read error context for specific test # Read error context for specific test
cat test-results/test-name-test-description-browser/error-context.md cat test-results/test-name-test-description-browser/error-context.md
@@ -297,7 +268,6 @@ grep -A 10 -B 5 "button.*Yes\|button.*No" test-results/*/error-context.md
``` ```
### 3. Check Trace Files ### 3. Check Trace Files
```bash ```bash
# List available trace files # List available trace files
find test-results -name "*.zip" | grep trace find test-results -name "*.zip" | grep trace
@@ -307,7 +277,6 @@ npx playwright show-trace test-results/test-name/trace.zip
``` ```
### 4. Investigate Selector Issues ### 4. Investigate Selector Issues
```typescript ```typescript
// Check for multiple elements with same text // Check for multiple elements with same text
await page.locator('button:has-text("Yes")').count(); // Should be 1 await page.locator('button:has-text("Yes")').count(); // Should be 1
@@ -317,7 +286,6 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
``` ```
## What Works (Evidence) ## What Works (Evidence)
- ✅ **Error context files** provide page snapshots showing exact DOM state at failure - ✅ **Error context files** provide page snapshots showing exact DOM state at failure
- **Time**: 2025-08-21T14:22Z - **Time**: 2025-08-21T14:22Z
- **Evidence**: `test-results/60-new-activity-New-offers-for-another-user-chromium/error-context.md` shows both alerts visible - **Evidence**: `test-results/60-new-activity-New-offers-for-another-user-chromium/error-context.md` shows both alerts visible
@@ -334,7 +302,6 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
- **Verify at**: Error context markdown files - **Verify at**: Error context markdown files
## What Doesn't (Evidence & Hypotheses) ## What Doesn't (Evidence & Hypotheses)
- ❌ **Generic selectors** fail with multiple similar elements at `test-playwright/testUtils.ts:161` - ❌ **Generic selectors** fail with multiple similar elements at `test-playwright/testUtils.ts:161`
- **Time**: 2025-08-21T14:22Z - **Time**: 2025-08-21T14:22Z
- **Evidence**: `button:has-text("Yes")` matches both "Yes" and "Yes, Export Data" - **Evidence**: `button:has-text("Yes")` matches both "Yes" and "Yes, Export Data"
@@ -348,14 +315,12 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
- **Next probe**: Implement alert queuing or prevent overlapping alerts - **Next probe**: Implement alert queuing or prevent overlapping alerts
## Risks, Limits, Assumptions ## Risks, Limits, Assumptions
- **Trace file size**: Large trace files may impact storage and analysis time - **Trace file size**: Large trace files may impact storage and analysis time
- **Browser compatibility**: Trace viewer requires specific browser support - **Browser compatibility**: Trace viewer requires specific browser support
- **Test isolation**: Shared state between tests may affect investigation results - **Test isolation**: Shared state between tests may affect investigation results
- **Timing sensitivity**: Tests may pass/fail based on system performance - **Timing sensitivity**: Tests may pass/fail based on system performance
## Next Steps ## Next Steps
| Owner | Task | Exit Criteria | Target Date (UTC) | | Owner | Task | Exit Criteria | Target Date (UTC) |
|---|---|---|---| |---|---|---|---|
| Development Team | Fix test selectors for multiple alerts | All tests pass consistently | 2025-08-22 | | Development Team | Fix test selectors for multiple alerts | All tests pass consistently | 2025-08-22 |
@@ -363,25 +328,21 @@ await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes"
| Development Team | Add test IDs to alert buttons | Unique selectors for all UI elements | 2025-08-28 | | Development Team | Add test IDs to alert buttons | Unique selectors for all UI elements | 2025-08-28 |
## References ## References
- [Playwright Trace Viewer Documentation](https://playwright.dev/docs/trace-viewer) - [Playwright Trace Viewer Documentation](https://playwright.dev/docs/trace-viewer)
- [Playwright Test Results](https://playwright.dev/docs/test-reporters) - [Playwright Test Results](https://playwright.dev/docs/test-reporters)
- [Test Investigation Workflow](./research_diagnostic.mdc) - [Test Investigation Workflow](./research_diagnostic.mdc)
## Competence Hooks ## Competence Hooks
- **Why this works**: Systematic investigation leverages Playwright's built-in debugging tools to identify root causes - **Why this works**: Systematic investigation leverages Playwright's built-in debugging tools to identify root causes
- **Common pitfalls**: Generic selectors fail with multiple similar elements; timing issues create race conditions; alert stacking causes UI conflicts - **Common pitfalls**: Generic selectors fail with multiple similar elements; timing issues create race conditions; alert stacking causes UI conflicts
- **Next skill unlock**: Implement unique test IDs and handle alert dismissal order in test flows - **Next skill unlock**: Implement unique test IDs and handle alert dismissal order in test flows
- **Teach-back**: "How would you investigate a Playwright test failure using error context, trace files, and page snapshots?" - **Teach-back**: "How would you investigate a Playwright test failure using error context, trace files, and page snapshots?"
## Collaboration Hooks ## Collaboration Hooks
- **Reviewers**: QA team, test automation engineers - **Reviewers**: QA team, test automation engineers
- **Sign-off checklist**: Error context analyzed, trace files reviewed, root cause identified, fix implemented and tested - **Sign-off checklist**: Error context analyzed, trace files reviewed, root cause identified, fix implemented and tested
## Assumptions & Limits ## Assumptions & Limits
- Test results directory structure follows Playwright conventions - Test results directory structure follows Playwright conventions
- Trace files are enabled in configuration (`trace: "retain-on-failure"`) - Trace files are enabled in configuration (`trace: "retain-on-failure"`)
- Error context files contain valid YAML page snapshots - Error context files contain valid YAML page snapshots

View File

@@ -73,7 +73,7 @@
### Avoid ### Avoid
- Vague: _improved, enhanced, better_ - Vague: *improved, enhanced, better*
- Trivialities: tiny docs, one-liners, pure lint cleanups (separate, - Trivialities: tiny docs, one-liners, pure lint cleanups (separate,

View File

@@ -18,16 +18,16 @@ npm run lint-fix || {
exit 1 exit 1
} }
# Build Architecture Guard - DISABLED # Then run Build Architecture Guard
# echo "🏗️ Running Build Architecture Guard..." echo "🏗️ Running Build Architecture Guard..."
# bash ./scripts/build-arch-guard.sh --staged || { bash ./scripts/build-arch-guard.sh --staged || {
# echo echo
# echo "❌ Build Architecture Guard failed. Please fix the issues and try again." echo "❌ Build Architecture Guard failed. Please fix the issues and try again."
# echo "💡 To bypass this check for emergency commits, use:" echo "💡 To bypass this check for emergency commits, use:"
# echo " git commit --no-verify" echo " git commit --no-verify"
# echo echo
# exit 1 exit 1
# } }
echo "✅ All pre-commit checks passed!" echo "✅ All pre-commit checks passed!"

View File

@@ -5,28 +5,23 @@
# #
. "$(dirname -- "$0")/_/husky.sh" . "$(dirname -- "$0")/_/husky.sh"
echo "🔍 Pre-push checks..." echo "🔍 Running Build Architecture Guard (pre-push)..."
# Build Architecture Guard - DISABLED # Get the remote branch we're pushing to
# echo "🔍 Running Build Architecture Guard (pre-push)..." REMOTE_BRANCH="origin/$(git rev-parse --abbrev-ref HEAD)"
#
# # Get the remote branch we're pushing to
# REMOTE_BRANCH="origin/$(git rev-parse --abbrev-ref HEAD)"
#
# # Check if remote branch exists
# if git show-ref --verify --quiet "refs/remotes/$REMOTE_BRANCH"; then
# RANGE="$REMOTE_BRANCH...HEAD"
# else
# # If remote branch doesn't exist, check last commit
# RANGE="HEAD~1..HEAD"
# fi
#
# bash ./scripts/build-arch-guard.sh --range "$RANGE" || {
# echo
# echo "💡 To bypass this check for emergency pushes, use:"
# echo " git push --no-verify"
# echo
# exit 1
# }
echo "✅ Pre-push checks passed!" # Check if remote branch exists
if git show-ref --verify --quiet "refs/remotes/$REMOTE_BRANCH"; then
RANGE="$REMOTE_BRANCH...HEAD"
else
# If remote branch doesn't exist, check last commit
RANGE="HEAD~1..HEAD"
fi
bash ./scripts/build-arch-guard.sh --range "$RANGE" || {
echo
echo "💡 To bypass this check for emergency pushes, use:"
echo " git push --no-verify"
echo
exit 1
}

View File

@@ -1,56 +1,27 @@
{ {
"MD013": false, "MD013": {
"MD033": false, "line_length": 80,
"MD041": false, "code_blocks": false,
"MD024": { "tables": false,
"siblings_only": true "headings": false
},
"MD029": {
"style": "ordered"
},
"MD007": {
"indent": 2
},
"MD012": {
"maximum": 1
}, },
"MD012": true,
"MD022": true, "MD022": true,
"MD025": true,
"MD026": {
"punctuation": ".,;:!"
},
"MD030": {
"ul_single": 1,
"ol_single": 1,
"ul_multi": 1,
"ol_multi": 1
},
"MD031": true, "MD031": true,
"MD032": true, "MD032": true,
"MD034": true,
"MD035": {
"style": "---"
},
"MD036": false,
"MD037": true,
"MD038": true,
"MD039": true,
"MD040": true,
"MD042": true,
"MD043": false,
"MD044": false,
"MD045": true,
"MD046": {
"style": "fenced"
},
"MD047": true, "MD047": true,
"MD048": { "MD009": true,
"style": "backtick" "MD010": true,
}, "MD004": { "style": "dash" },
"MD049": { "MD029": { "style": "ordered" },
"style": "underscore" "MD041": false,
}, "MD025": false,
"MD050": { "MD024": false,
"style": "asterisk" "MD036": false,
} "MD003": false,
"MD040": false,
"MD055": false,
"MD056": false,
"MD034": false,
"MD023": false
} }

View File

@@ -93,7 +93,6 @@ The Build Architecture Guard protects your build system by enforcing documentati
#### Protected File Patterns #### Protected File Patterns
The guard monitors these sensitive paths: The guard monitors these sensitive paths:
- `vite.config.*` - Build configuration - `vite.config.*` - Build configuration
- `scripts/**` - Build and utility scripts - `scripts/**` - Build and utility scripts
- `electron/**` - Desktop application code - `electron/**` - Desktop application code
@@ -133,7 +132,6 @@ npm run guard:setup
#### Troubleshooting #### Troubleshooting
If you encounter `mapfile: command not found` errors: If you encounter `mapfile: command not found` errors:
```bash ```bash
# Ensure script is executable # Ensure script is executable
chmod +x scripts/build-arch-guard.sh chmod +x scripts/build-arch-guard.sh
@@ -272,7 +270,6 @@ Start the development server using `npm run build:web:dev` or `npm run build:web
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`?** **Why Use `serve`?**
- **Production Testing**: Test your optimized production build locally before deployment - **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`) - **SPA Routing Validation**: Verify deep linking and navigation work correctly (handles routes like `/discover`, `/account`)
- **Performance Testing**: Test the minified and optimized build locally - **Performance Testing**: Test the minified and optimized build locally
@@ -338,18 +335,15 @@ All web build commands use the `./scripts/build-web.sh` script, which provides:
The `serve` functionality provides a local HTTP server for testing production builds: The `serve` functionality provides a local HTTP server for testing production builds:
**What It Does:** **What It Does:**
1. **Builds** the application using Vite 1. **Builds** the application using Vite
2. **Serves** the built files from the `dist/` directory 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.) 3. **Handles SPA Routing** - serves `index.html` for all routes (fixes 404s on `/discover`, `/account`, etc.)
**Server Options:** **Server Options:**
- **Primary**: `npx serve -s dist -l 8080` (recommended - full SPA support) - **Primary**: `npx serve -s dist -l 8080` (recommended - full SPA support)
- **Fallback**: Python HTTP server (limited SPA routing support) - **Fallback**: Python HTTP server (limited SPA routing support)
**Use Cases:** **Use Cases:**
- Testing production builds before deployment - Testing production builds before deployment
- Validating SPA routing behavior - Validating SPA routing behavior
- Performance testing of optimized builds - Performance testing of optimized builds
@@ -1263,13 +1257,11 @@ npm run assets:validate
##### What Gets Validated ##### What Gets Validated
**Source Assets (Required):** **Source Assets (Required):**
- `resources/icon.png` - App icon source - `resources/icon.png` - App icon source
- `resources/splash.png` - Splash screen source - `resources/splash.png` - Splash screen source
- `resources/splash_dark.png` - Dark mode splash source - `resources/splash_dark.png` - Dark mode splash source
**Android Resources (Generated):** **Android Resources (Generated):**
- `android/app/src/main/res/drawable/splash.png` - Splash screen drawable - `android/app/src/main/res/drawable/splash.png` - Splash screen drawable
- `android/app/src/main/res/mipmap-*/ic_launcher.png` - App icons for all densities - `android/app/src/main/res/mipmap-*/ic_launcher.png` - App icons for all densities
- `android/app/src/main/res/mipmap-*/ic_launcher_round.png` - Round app icons for all densities - `android/app/src/main/res/mipmap-*/ic_launcher_round.png` - Round app icons for all densities
@@ -2714,7 +2706,6 @@ configuration files in the repository.
### 2025-08-21 - Cursor Rules Refactoring and Build System Updates ### 2025-08-21 - Cursor Rules Refactoring and Build System Updates
#### Package Dependencies Updated #### Package Dependencies Updated
- **Added**: `markdownlint-cli2` v0.18.1 - Modern markdown linting with improved performance - **Added**: `markdownlint-cli2` v0.18.1 - Modern markdown linting with improved performance
- **Added**: `@commitlint/cli` v18.6.1 - Conventional commit message validation - **Added**: `@commitlint/cli` v18.6.1 - Conventional commit message validation
- **Added**: `@commitlint/config-conventional` v18.6.2 - Conventional commit standards - **Added**: `@commitlint/config-conventional` v18.6.2 - Conventional commit standards
@@ -2722,33 +2713,28 @@ configuration files in the repository.
- **Updated**: `lint-staged` v15.2.2 - Pre-commit linting automation - **Updated**: `lint-staged` v15.2.2 - Pre-commit linting automation
#### Build Script Improvements #### Build Script Improvements
- **Markdown Linting**: Replaced custom markdown scripts with `markdownlint-cli2` - **Markdown Linting**: Replaced custom markdown scripts with `markdownlint-cli2`
- **Before**: `./scripts/fix-markdown.sh` and `./scripts/validate-markdown.sh` - **Before**: `./scripts/fix-markdown.sh` and `./scripts/validate-markdown.sh`
- **After**: `markdownlint-cli2 --fix` and `markdownlint-cli2` - **After**: `markdownlint-cli2 --fix` and `markdownlint-cli2`
- **Benefits**: Faster execution, better error reporting, modern markdown standards - **Benefits**: Faster execution, better error reporting, modern markdown standards
#### Lint-Staged Configuration Enhanced #### Lint-Staged Configuration Enhanced
- **Added**: Markdown file linting to pre-commit hooks - **Added**: Markdown file linting to pre-commit hooks
- **Pattern**: `*.{md,markdown,mdc}` files now automatically formatted - **Pattern**: `*.{md,markdown,mdc}` files now automatically formatted
- **Command**: `markdownlint-cli2 --fix` runs before each commit - **Command**: `markdownlint-cli2 --fix` runs before each commit
- **Coverage**: All markdown files including `.mdc` cursor rules - **Coverage**: All markdown files including `.mdc` cursor rules
#### Commit Message Standards #### Commit Message Standards
- **Added**: Conventional commit validation via commitlint - **Added**: Conventional commit validation via commitlint
- **Configuration**: Extends `@commitlint/config-conventional` - **Configuration**: Extends `@commitlint/config-conventional`
- **Enforcement**: Ensures consistent commit message format across the project - **Enforcement**: Ensures consistent commit message format across the project
#### Node.js Version Requirements #### Node.js Version Requirements
- **Updated**: Minimum Node.js version requirements for new dependencies - **Updated**: Minimum Node.js version requirements for new dependencies
- **markdownlint-cli2**: Requires Node.js >=20 - **markdownlint-cli2**: Requires Node.js >=20
- **Various utilities**: Require Node.js >=18 for modern ES features - **Various utilities**: Require Node.js >=18 for modern ES features
#### Build Process Impact #### Build Process Impact
- **No Breaking Changes**: All existing build commands continue to work - **No Breaking Changes**: All existing build commands continue to work
- **Improved Quality**: Better markdown formatting and commit message standards - **Improved Quality**: Better markdown formatting and commit message standards
- **Enhanced Automation**: More comprehensive pre-commit validation - **Enhanced Automation**: More comprehensive pre-commit validation
@@ -2759,7 +2745,6 @@ configuration files in the repository.
### 2025-08-21 - Commitlint Configuration Refinement ### 2025-08-21 - Commitlint Configuration Refinement
#### Commit Message Validation Improvements #### Commit Message Validation Improvements
- **Modified**: Commitlint configuration moved from `package.json` to dedicated `commitlint.config.js` - **Modified**: Commitlint configuration moved from `package.json` to dedicated `commitlint.config.js`
- **Enhanced**: Strict validation rules downgraded from errors to warnings - **Enhanced**: Strict validation rules downgraded from errors to warnings
- **Before**: `subject-case` and `subject-full-stop` rules caused red error messages - **Before**: `subject-case` and `subject-full-stop` rules caused red error messages
@@ -2767,7 +2752,6 @@ configuration files in the repository.
- **Benefit**: Eliminates confusing red error messages while maintaining commit quality guidance - **Benefit**: Eliminates confusing red error messages while maintaining commit quality guidance
#### Configuration Structure #### Configuration Structure
- **File**: `commitlint.config.js` - Dedicated commitlint configuration - **File**: `commitlint.config.js` - Dedicated commitlint configuration
- **Extends**: `@commitlint/config-conventional` - Standard conventional commit rules - **Extends**: `@commitlint/config-conventional` - Standard conventional commit rules
- **Custom Rules**: - **Custom Rules**:
@@ -2778,7 +2762,6 @@ configuration files in the repository.
- Current: Problematic rules set to warning level (1) - Current: Problematic rules set to warning level (1)
#### User Experience Impact #### User Experience Impact
- **Before**: Red error messages on every push with strict commit rules - **Before**: Red error messages on every push with strict commit rules
- **After**: Yellow warning messages that provide guidance without disruption - **After**: Yellow warning messages that provide guidance without disruption
- **Workflow**: Commits and pushes continue to work while maintaining quality standards - **Workflow**: Commits and pushes continue to work while maintaining quality standards
@@ -2789,7 +2772,6 @@ configuration files in the repository.
### 2025-08-26 - Capacitor Plugin Additions ### 2025-08-26 - Capacitor Plugin Additions
#### New Capacitor Plugins Added #### New Capacitor Plugins Added
- **Added**: `@capacitor/clipboard` v6.0.2 - Clipboard functionality for mobile platforms - **Added**: `@capacitor/clipboard` v6.0.2 - Clipboard functionality for mobile platforms
- **Purpose**: Enable copy/paste operations on mobile devices - **Purpose**: Enable copy/paste operations on mobile devices
- **Platforms**: iOS and Android - **Platforms**: iOS and Android
@@ -2803,27 +2785,23 @@ configuration files in the repository.
- **Integration**: Automatically included in mobile builds - **Integration**: Automatically included in mobile builds
#### Android Build System Updates #### Android Build System Updates
- **Modified**: `android/capacitor.settings.gradle` - Added new plugin project includes - **Modified**: `android/capacitor.settings.gradle` - Added new plugin project includes
- **Added**: `:capacitor-clipboard` project directory mapping - **Added**: `:capacitor-clipboard` project directory mapping
- **Added**: `:capacitor-status-bar` project directory mapping - **Added**: `:capacitor-status-bar` project directory mapping
- **Impact**: New plugins now properly integrated into Android build process - **Impact**: New plugins now properly integrated into Android build process
#### Package Dependencies #### Package Dependencies
- **Updated**: `package.json` - Added new Capacitor plugin dependencies - **Updated**: `package.json` - Added new Capacitor plugin dependencies
- **Updated**: `package-lock.json` - Locked dependency versions for consistency - **Updated**: `package-lock.json` - Locked dependency versions for consistency
- **Version**: All new plugins use Capacitor 6.x compatible versions - **Version**: All new plugins use Capacitor 6.x compatible versions
#### Build Process Impact #### Build Process Impact
- **No Breaking Changes**: Existing build commands continue to work unchanged - **No Breaking Changes**: Existing build commands continue to work unchanged
- **Enhanced Mobile Features**: New clipboard and status bar capabilities available - **Enhanced Mobile Features**: New clipboard and status bar capabilities available
- **Automatic Integration**: Plugins automatically included in mobile builds - **Automatic Integration**: Plugins automatically included in mobile builds
- **Platform Support**: Both iOS and Android builds now include new functionality - **Platform Support**: Both iOS and Android builds now include new functionality
#### Testing Requirements #### Testing Requirements
- **Mobile Builds**: Verify new plugins integrate correctly in iOS and Android builds - **Mobile Builds**: Verify new plugins integrate correctly in iOS and Android builds
- **Functionality**: Test clipboard operations and status bar management on devices - **Functionality**: Test clipboard operations and status bar management on devices
- **Fallback**: Ensure graceful degradation when plugins are unavailable - **Fallback**: Ensure graceful degradation when plugins are unavailable

View File

@@ -66,17 +66,14 @@ test-image.tar a1b2c3d4e5f6...
``` ```
### Docs ### Docs
- [x] **BUILDING.md** updated (sections): Docker deployment - [x] **BUILDING.md** updated (sections): Docker deployment
- [x] Troubleshooting updated: Added Docker troubleshooting section - [x] Troubleshooting updated: Added Docker troubleshooting section
### Rollback ### Rollback
- [x] Verified steps to restore previous behavior: - [x] Verified steps to restore previous behavior:
1. `git revert HEAD` 1. `git revert HEAD`
2. `docker rmi test-image` 2. `docker rmi test-image`
3. Restore previous BUILDING.md 3. Restore previous BUILDING.md
``` ```
--- ---

View File

@@ -11,7 +11,7 @@ See [ClickUp](https://sharing.clickup.com/9014278710/l/h/8cmnyhp-174/10573fec74e
Quick start: Quick start:
- For setup, we recommend [pkgx](https://pkgx.dev), which installs what you need (either automatically or with the `dev` command). Core dependencies are typescript & npm; when building for other platforms, you'll need other things such as those in the pkgx.yaml & BUILDING.md files. * For setup, we recommend [pkgx](https://pkgx.dev), which installs what you need (either automatically or with the `dev` command). Core dependencies are typescript & npm; when building for other platforms, you'll need other things such as those in the pkgx.yaml & BUILDING.md files.
```bash ```bash
npm install npm install
@@ -90,7 +90,6 @@ VITE_LOG_LEVEL=debug npm run dev
See [Logging Configuration Guide](doc/logging-configuration.md) for complete details. See [Logging Configuration Guide](doc/logging-configuration.md) for complete details.
### Quick Usage ### Quick Usage
```bash ```bash
# Run the database clearing script # Run the database clearing script
./scripts/clear-database.sh ./scripts/clear-database.sh
@@ -103,19 +102,16 @@ npm run build:web:dev # For Web
### What It Does ### What It Does
#### **Electron (Desktop App)** #### **Electron (Desktop App)**
- Automatically finds and clears the SQLite database files - Automatically finds and clears the SQLite database files
- Works on Linux, macOS, and Windows - Works on Linux, macOS, and Windows
- Clears all data and forces fresh migrations on next startup - Clears all data and forces fresh migrations on next startup
#### **Web Browser** #### **Web Browser**
- Provides instructions for using custom browser data directories - Provides instructions for using custom browser data directories
- Shows manual clearing via browser DevTools - Shows manual clearing via browser DevTools
- Ensures reliable database clearing without browser complications - Ensures reliable database clearing without browser complications
### Safety Features ### Safety Features
-**Interactive Script**: Guides you through the process -**Interactive Script**: Guides you through the process
-**Platform Detection**: Automatically detects your OS -**Platform Detection**: Automatically detects your OS
-**Clear Instructions**: Step-by-step guidance for each platform -**Clear Instructions**: Step-by-step guidance for each platform
@@ -124,7 +120,6 @@ npm run build:web:dev # For Web
### Manual Commands (if needed) ### Manual Commands (if needed)
#### **Electron Database Location** #### **Electron Database Location**
```bash ```bash
# Linux # Linux
rm -rf ~/.config/TimeSafari/* rm -rf ~/.config/TimeSafari/*
@@ -137,7 +132,6 @@ rmdir /s /q %APPDATA%\TimeSafari
``` ```
#### **Web Browser (Custom Data Directory)** #### **Web Browser (Custom Data Directory)**
```bash ```bash
# Create isolated browser profile # Create isolated browser profile
mkdir ~/timesafari-dev-data mkdir ~/timesafari-dev-data
@@ -150,7 +144,6 @@ URL generation across all environments. This prevents localhost URLs from
appearing in shared links during development. appearing in shared links during development.
### Key Features ### Key Features
-**Production URLs for Sharing**: All copy link buttons use production domain -**Production URLs for Sharing**: All copy link buttons use production domain
-**Environment-Specific Internal URLs**: Internal operations use appropriate -**Environment-Specific Internal URLs**: Internal operations use appropriate
environment URLs environment URLs
@@ -234,7 +227,6 @@ npm run test:prerequisites
- **Build failures**: Run `npm run check:dependencies` to diagnose environment issues - **Build failures**: Run `npm run check:dependencies` to diagnose environment issues
**Required Versions**: **Required Versions**:
- Node.js: 18+ (LTS recommended) - Node.js: 18+ (LTS recommended)
- npm: 8+ (comes with Node.js) - npm: 8+ (comes with Node.js)
- Platform-specific tools: Android Studio, Xcode (for mobile builds) - Platform-specific tools: Android Studio, Xcode (for mobile builds)
@@ -254,26 +246,25 @@ To add a Font Awesome icon, add to `fontawesome.ts` and reference with
### Reference Material ### Reference Material
- Notifications can be type of `toast` (self-dismiss), `info`, `success`, `warning`, and `danger`. * Notifications can be type of `toast` (self-dismiss), `info`, `success`, `warning`, and `danger`.
They are done via [notiwind](https://www.npmjs.com/package/notiwind) and set up in App.vue. They are done via [notiwind](https://www.npmjs.com/package/notiwind) and set up in App.vue.
- [Customize Vue configuration](https://cli.vuejs.org/config/). * [Customize Vue configuration](https://cli.vuejs.org/config/).
- If you are deploying in a subdirectory, add it to `publicPath` in vue.config.js, eg: `publicPath: "/app/time-tracker/",` * If you are deploying in a subdirectory, add it to `publicPath` in vue.config.js, eg: `publicPath: "/app/time-tracker/",`
### Code Organization ### Code Organization
The project uses a centralized approach to type definitions and interfaces: The project uses a centralized approach to type definitions and interfaces:
- `src/interfaces/` - Contains all TypeScript interfaces and type definitions * `src/interfaces/` - Contains all TypeScript interfaces and type definitions
- `deepLinks.ts` - Deep linking type system and Zod validation schemas * `deepLinks.ts` - Deep linking type system and Zod validation schemas
- `give.ts` - Give-related interfaces and type definitions * `give.ts` - Give-related interfaces and type definitions
- `claims.ts` - Claim-related interfaces and verifiable credentials * `claims.ts` - Claim-related interfaces and verifiable credentials
- `common.ts` - Shared interfaces and utility types * `common.ts` - Shared interfaces and utility types
- Other domain-specific interface files * Other domain-specific interface files
Key principles: Key principles:
- All interfaces and types are defined in the interfaces folder - All interfaces and types are defined in the interfaces folder
- Zod schemas are used for runtime validation and type generation - Zod schemas are used for runtime validation and type generation
- Domain-specific interfaces are separated into their own files - Domain-specific interfaces are separated into their own files
@@ -284,11 +275,11 @@ Key principles:
The application uses a platform-agnostic database layer with Vue mixins for service access: The application uses a platform-agnostic database layer with Vue mixins for service access:
- `src/services/PlatformService.ts` - Database interface definition * `src/services/PlatformService.ts` - Database interface definition
- `src/services/PlatformServiceFactory.ts` - Platform-specific service factory * `src/services/PlatformServiceFactory.ts` - Platform-specific service factory
- `src/services/AbsurdSqlDatabaseService.ts` - SQLite implementation * `src/services/AbsurdSqlDatabaseService.ts` - SQLite implementation
- `src/utils/PlatformServiceMixin.ts` - Vue mixin for database access with caching * `src/utils/PlatformServiceMixin.ts` - Vue mixin for database access with caching
- `src/db/` - Legacy Dexie database (migration in progress) * `src/db/` - Legacy Dexie database (migration in progress)
**Development Guidelines**: **Development Guidelines**:
@@ -325,11 +316,11 @@ timesafari/
Gifts make the world go 'round! Gifts make the world go 'round!
- [WebStorm by JetBrains](https://www.jetbrains.com/webstorm/) for the free open-source license * [WebStorm by JetBrains](https://www.jetbrains.com/webstorm/) for the free open-source license
- [Máximo Fernández](https://medium.com/@maxfarenas) for the 3D [code](https://github.com/maxfer03/vue-three-ns) and [explanatory post](https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80) * [Máximo Fernández](https://medium.com/@maxfarenas) for the 3D [code](https://github.com/maxfer03/vue-three-ns) and [explanatory post](https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80)
- [Many tools & libraries](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/src/branch/master/package.json#L10) such as Nodejs.org, IntelliJ Idea, Veramo.io, Vuejs.org, threejs.org * [Many tools & libraries](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/src/branch/master/package.json#L10) such as Nodejs.org, IntelliJ Idea, Veramo.io, Vuejs.org, threejs.org
- [Bush 3D model](https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439) * [Bush 3D model](https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439)
- [Forest floor image](https://www.goodfreephotos.com/albums/textures/leafy-autumn-forest-floor.jpg) * [Forest floor image](https://www.goodfreephotos.com/albums/textures/leafy-autumn-forest-floor.jpg)
- Time Safari logo assisted by [DALL-E in ChatGPT](https://chat.openai.com/g/g-2fkFE8rbu-dall-e) * Time Safari logo assisted by [DALL-E in ChatGPT](https://chat.openai.com/g/g-2fkFE8rbu-dall-e)
- [DiceBear](https://www.dicebear.com/licenses/) and [Avataaars](https://www.dicebear.com/styles/avataaars/#details) for human-looking identicons * [DiceBear](https://www.dicebear.com/licenses/) and [Avataaars](https://www.dicebear.com/styles/avataaars/#details) for human-looking identicons
- Some gratitude prompts thanks to [Develop Good Habits](https://www.developgoodhabits.com/gratitude-journal-prompts/) * Some gratitude prompts thanks to [Develop Good Habits](https://www.developgoodhabits.com/gratitude-journal-prompts/)

View File

@@ -5,33 +5,33 @@
We can't trust iOS IndexedDB to persist. I want to start delivering an app to people now, in preparation for presentations mid-June: Rotary on June 12 and Porcfest on June 17. We can't trust iOS IndexedDB to persist. I want to start delivering an app to people now, in preparation for presentations mid-June: Rotary on June 12 and Porcfest on June 17.
- Apple WebKit puts a [7-day cap on IndexedDB](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/). * Apple WebKit puts a [7-day cap on IndexedDB](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/).
- The web standards expose a `persist` method to mark memory as persistent, and [supposedly WebView supports it](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persisted), but too many other things indicate it's not reliable. I've talked with [ChatGPT](https://chatgpt.com/share/68322f40-84c8-8007-b213-855f7962989a) & Venice & Claude (in Cursor); [this answer from Perplexity](https://www.perplexity.ai/search/which-platforms-prompt-the-use-HUQLqy4qQD2cRbkmO4CgHg) says that most platforms don't prompt and Safari doesn't support it; I don't know if that means WebKit as well. * The web standards expose a `persist` method to mark memory as persistent, and [supposedly WebView supports it](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persisted), but too many other things indicate it's not reliable. I've talked with [ChatGPT](https://chatgpt.com/share/68322f40-84c8-8007-b213-855f7962989a) & Venice & Claude (in Cursor); [this answer from Perplexity](https://www.perplexity.ai/search/which-platforms-prompt-the-use-HUQLqy4qQD2cRbkmO4CgHg) says that most platforms don't prompt and Safari doesn't support it; I don't know if that means WebKit as well.
- Capacitor says [not to trust it on iOS](https://capacitorjs.com/docs/v6/guides/storage). * Capacitor says [not to trust it on iOS](https://capacitorjs.com/docs/v6/guides/storage).
Also, with sensitive data, the accounts info should be encrypted. Also, with sensitive data, the accounts info should be encrypted.
# Options # Options
- There is a community [SQLite plugin for Capacitor](https://github.com/capacitor-community/sqlite) with encryption by [SQLCipher](https://github.com/sqlcipher/sqlcipher). * There is a community [SQLite plugin for Capacitor](https://github.com/capacitor-community/sqlite) with encryption by [SQLCipher](https://github.com/sqlcipher/sqlcipher).
- [This tutorial](https://jepiqueau.github.io/2023/09/05/Ionic7Vue-SQLite-CRUD-App.html#part-1---web---table-of-contents) shows how that plugin works for web as well as native. * [This tutorial](https://jepiqueau.github.io/2023/09/05/Ionic7Vue-SQLite-CRUD-App.html#part-1---web---table-of-contents) shows how that plugin works for web as well as native.
- Capacitor abstracts [user preferences in an API](https://capacitorjs.com/docs/apis/preferences), which uses different underlying libraries on iOS & Android. Unfortunately, it won't do any filtering or searching, and is only meant for small amounts of data. (It could be used for settings and for identifiers, but contacts will grow and image blobs won't work.) * Capacitor abstracts [user preferences in an API](https://capacitorjs.com/docs/apis/preferences), which uses different underlying libraries on iOS & Android. Unfortunately, it won't do any filtering or searching, and is only meant for small amounts of data. (It could be used for settings and for identifiers, but contacts will grow and image blobs won't work.)
- There are hints that Capacitor offers another custom storage API but all I could find was that Preferences API. * There are hints that Capacitor offers another custom storage API but all I could find was that Preferences API.
- [Ionic Storage](https://ionic.io/docs/secure-storage) is an enterprise solution, which also supports encryption. * [Ionic Storage](https://ionic.io/docs/secure-storage) is an enterprise solution, which also supports encryption.
- Not an option yet: Dexie may support SQLite in [a future version](https://dexie.org/roadmap/dexie5.0). * Not an option yet: Dexie may support SQLite in [a future version](https://dexie.org/roadmap/dexie5.0).
# Current Plan # Current Plan
- Implement SQLite for Capacitor & web, with encryption. That will allow us to test quickly and keep the same interface for native & web, but we don't deal with migrations for current web users. * Implement SQLite for Capacitor & web, with encryption. That will allow us to test quickly and keep the same interface for native & web, but we don't deal with migrations for current web users.
- After that is delivered, write a migration for current web users from IndexedDB to SQLite. * After that is delivered, write a migration for current web users from IndexedDB to SQLite.
# Current method calls # Current method calls

View File

@@ -1,21 +0,0 @@
# Glossary
**T (slot time)** — The local wall-clock time a notification should fire (e.g., 08:00).
**Tlead** — The moment **`prefetchLeadMinutes`** before **T** when the system *attempts* a **single** background prefetch. Tlead **controls prefetch attempts, not arming**; locals are pre-armed earlier to guarantee closed-app delivery.
**Rolling window** — Always keep **today's remaining** (and tomorrow if iOS pending caps allow) locals **armed** so the OS can deliver while the app is closed.
**TTL (time-to-live)** — Maximum allowed payload age **at fire time**. If `T fetchedAt > ttlSeconds`, we **skip** arming for that T.
**Shared DB (default)** — The app and plugin open the **same SQLite file**; the app owns schema/migrations, the plugin performs short writes with WAL.
**WAL (Write-Ahead Logging)** — SQLite journaling mode that permits concurrent reads during writes; recommended for foreground-read + background-write.
**`PRAGMA user_version`** — An integer the app increments on each migration; the plugin **checks** (does not migrate) to ensure compatibility.
**Exact alarm (Android)** — Minute-precise alarm via `AlarmManager.setExactAndAllowWhileIdle`, subject to policy and permission.
**Windowed alarm (Android)** — Batched/inexact alarm via `setWindow(start,len)`; we target **±10 minutes** when exact alarms are unavailable.
**Start-on-Login** — Electron feature that automatically launches the application when the user logs into their system, enabling background notification scheduling and delivery after system reboot.

View File

@@ -330,7 +330,6 @@ Track the effectiveness of your Build Architecture Guard:
## 📝 **Changelog** ## 📝 **Changelog**
### 2025-08-22 - Shell Compatibility Fix ### 2025-08-22 - Shell Compatibility Fix
- **Fixed**: Replaced `mapfile` command with portable alternative for cross-shell compatibility - **Fixed**: Replaced `mapfile` command with portable alternative for cross-shell compatibility
- **Impact**: Resolves "mapfile: command not found" errors in pre-commit hooks - **Impact**: Resolves "mapfile: command not found" errors in pre-commit hooks
- **Files**: `scripts/build-arch-guard.sh` - **Files**: `scripts/build-arch-guard.sh`

View File

@@ -1,11 +1,77 @@
# TimeSafari — Native-First Notification System (Clean Pack) — 2025-09-07 # TimeSafari Docs
This pack contains a single-version **Native-First** documentation set with a clear definition of **Tlead** and aligned terminology. ## Generating PDF from Markdown on OSx
**Native-First =** OS-scheduled **background prefetch at Tlead** + **pre-armed one-shot local notifications**. Web-push is retired. This uses Pandoc and BasicTex (LaTeX) Installed through Homebrew.
**Included files** ### Set Up
- `notification-system.md` (merged comprehensive guide) ```bash
- `web-push-cleanup-guide.md` (cleanup instructions) brew install pandoc
- `GLOSSARY.md` (definitions incl. **T** and **Tlead**)
brew install basictex
# Setting up LaTex packages
# First update tlmgr
sudo tlmgr update --self
# Then install LaTex packages
sudo tlmgr install bbding
sudo tlmgr install enumitem
sudo tlmgr install environ
sudo tlmgr install fancyhdr
sudo tlmgr install framed
sudo tlmgr install import
sudo tlmgr install lastpage # Enables Page X of Y
sudo tlmgr install mdframed
sudo tlmgr install multirow
sudo tlmgr install needspace
sudo tlmgr install ntheorem
sudo tlmgr install tabu
sudo tlmgr install tcolorbox
sudo tlmgr install textpos
sudo tlmgr install titlesec
sudo tlmgr install titling # Required for the fancy headers used
sudo tlmgr install threeparttable
sudo tlmgr install trimspaces
sudo tlmgr install tocloft # Required for \tableofcontents generation
sudo tlmgr install varwidth
sudo tlmgr install wrapfig
# Install fonts
sudo tlmgr install cmbright
sudo tlmgr install collection-fontsrecommended # And set up fonts
sudo tlmgr install fira
sudo tlmgr install fontaxes
sudo tlmgr install libertine # The main font the doc uses
sudo tlmgr install opensans
sudo tlmgr install sourceserifpro
```
#### References
The following guide was adapted to this project except that we install with Brew and have a few more packages.
Guide: <https://daniel.feldroy.com/posts/setting-up-latex-on-mac-os-x>
### Usage
Use the `pandoc` command to generate a PDF.
```bash
pandoc usage-guide.md -o usage-guide.pdf
```
And you can open the PDF with the `open` command.
```bash
open usage-guide.pdf
```
Or use this one-liner
```bash
pandoc usage-guide.md -o usage-guide.pdf && open usage-guide.pdf
```

View File

@@ -117,25 +117,25 @@ async function getDatabaseService() {
## Files Modified ## Files Modified
1. **src/interfaces/worker-messages.ts** _(NEW)_ 1. **src/interfaces/worker-messages.ts** *(NEW)*
- Type definitions for worker communication - Type definitions for worker communication
- Request and response message interfaces - Request and response message interfaces
2. **src/registerSQLWorker.js** _(MAJOR REWRITE)_ 2. **src/registerSQLWorker.js** *(MAJOR REWRITE)*
- Message-based operation handling - Message-based operation handling
- **Fixed circular dependency with lazy loading** - **Fixed circular dependency with lazy loading**
- Proper error handling and response formatting - Proper error handling and response formatting
3. **src/services/platforms/WebPlatformService.ts** _(MAJOR REWRITE)_ 3. **src/services/platforms/WebPlatformService.ts** *(MAJOR REWRITE)*
- Worker-only database access - Worker-only database access
- Message sending and response handling - Message sending and response handling
- Timeout and error management - Timeout and error management
4. **src/main.web.ts** _(SIMPLIFIED)_ 4. **src/main.web.ts** *(SIMPLIFIED)*
- Removed duplicate worker creation - Removed duplicate worker creation
- Simplified initialization flow - Simplified initialization flow
5. **WORKER_ONLY_DATABASE_IMPLEMENTATION.md** _(NEW)_ 5. **WORKER_ONLY_DATABASE_IMPLEMENTATION.md** *(NEW)*
- Complete documentation of changes - Complete documentation of changes
## Benefits ## Benefits

View File

@@ -11,14 +11,12 @@ The Android Asset Validation System automatically detects and fixes missing Andr
## Problem Solved ## Problem Solved
Previously, Android builds would fail with errors like: Previously, Android builds would fail with errors like:
``` ```
error: resource drawable/splash (aka app.timesafari.app:drawable/splash) not found. error: resource drawable/splash (aka app.timesafari.app:drawable/splash) not found.
error: resource mipmap/ic_launcher (aka app.timesafari.app:mipmap/ic_launcher) not found. error: resource mipmap/ic_launcher (aka app.timesafari.app:mipmap/ic_launcher) not found.
``` ```
This happened when: This happened when:
- Source assets existed but weren't generated into Android resources - Source assets existed but weren't generated into Android resources
- Android resource directories were missing - Android resource directories were missing
- Asset generation tools weren't run before building - Asset generation tools weren't run before building
@@ -47,19 +45,16 @@ npm run build:android:studio
### What Gets Validated ### What Gets Validated
#### Source Assets (Required) #### Source Assets (Required)
- `resources/icon.png` - App icon source - `resources/icon.png` - App icon source
- `resources/splash.png` - Splash screen source - `resources/splash.png` - Splash screen source
- `resources/splash_dark.png` - Dark mode splash source - `resources/splash_dark.png` - Dark mode splash source
#### Android Resources (Generated) #### Android Resources (Generated)
- `android/app/src/main/res/drawable/splash.png` - Splash screen drawable - `android/app/src/main/res/drawable/splash.png` - Splash screen drawable
- `android/app/src/main/res/mipmap-*/ic_launcher.png` - App icons for all densities - `android/app/src/main/res/mipmap-*/ic_launcher.png` - App icons for all densities
- `android/app/src/main/res/mipmap-*/ic_launcher_round.png` - Round app icons for all densities - `android/app/src/main/res/mipmap-*/ic_launcher_round.png` - Round app icons for all densities
### Density Levels Checked ### Density Levels Checked
- `mipmap-mdpi` (1x) - `mipmap-mdpi` (1x)
- `mipmap-hdpi` (1.5x) - `mipmap-hdpi` (1.5x)
- `mipmap-xhdpi` (2x) - `mipmap-xhdpi` (2x)
@@ -69,7 +64,6 @@ npm run build:android:studio
## Usage ## Usage
### Automatic Validation (Recommended) ### Automatic Validation (Recommended)
The validation runs automatically during all Android builds: The validation runs automatically during all Android builds:
```bash ```bash
@@ -84,7 +78,6 @@ npm run build:android:debug
``` ```
### Manual Validation ### Manual Validation
Run validation only to check/fix assets: Run validation only to check/fix assets:
```bash ```bash
@@ -96,7 +89,6 @@ npm run assets:validate:android
``` ```
### Validation Only (No Regeneration) ### Validation Only (No Regeneration)
Check configuration without fixing: Check configuration without fixing:
```bash ```bash
@@ -106,7 +98,6 @@ npm run assets:validate
## Error Handling ## Error Handling
### Missing Source Assets ### Missing Source Assets
If source assets are missing, the build fails with clear error messages: If source assets are missing, the build fails with clear error messages:
``` ```
@@ -117,7 +108,6 @@ If source assets are missing, the build fails with clear error messages:
``` ```
### Missing Generated Resources ### Missing Generated Resources
If generated resources are missing, they're automatically regenerated: If generated resources are missing, they're automatically regenerated:
``` ```
@@ -129,7 +119,6 @@ If generated resources are missing, they're automatically regenerated:
``` ```
### Generation Failure ### Generation Failure
If regeneration fails, helpful guidance is provided: If regeneration fails, helpful guidance is provided:
``` ```
@@ -142,7 +131,6 @@ If regeneration fails, helpful guidance is provided:
## Integration Points ## Integration Points
### Build Script Integration ### Build Script Integration
The validation is integrated into the main build process: The validation is integrated into the main build process:
```bash ```bash
@@ -155,7 +143,6 @@ validate_android_assets || {
``` ```
### NPM Scripts ### NPM Scripts
New npm scripts for asset management: New npm scripts for asset management:
```json ```json
@@ -169,20 +156,17 @@ New npm scripts for asset management:
## Benefits ## Benefits
### For Developers ### For Developers
- **No More Build Failures**: Automatic detection and fixing of missing resources - **No More Build Failures**: Automatic detection and fixing of missing resources
- **Faster Development**: No need to manually run asset generation tools - **Faster Development**: No need to manually run asset generation tools
- **Clear Error Messages**: Helpful guidance when issues occur - **Clear Error Messages**: Helpful guidance when issues occur
- **Consistent Results**: Same validation on all development machines - **Consistent Results**: Same validation on all development machines
### For CI/CD ### For CI/CD
- **Reliable Builds**: Consistent asset validation across environments - **Reliable Builds**: Consistent asset validation across environments
- **Early Detection**: Catches issues before they reach production - **Early Detection**: Catches issues before they reach production
- **Automated Fixes**: Self-healing builds when possible - **Automated Fixes**: Self-healing builds when possible
### For Project Maintenance ### For Project Maintenance
- **Reduced Support**: Fewer "build doesn't work" issues - **Reduced Support**: Fewer "build doesn't work" issues
- **Documentation**: Clear requirements for required assets - **Documentation**: Clear requirements for required assets
- **Standardization**: Consistent asset structure across the project - **Standardization**: Consistent asset structure across the project
@@ -192,27 +176,21 @@ New npm scripts for asset management:
### Common Issues ### Common Issues
#### "No assets found in the asset path" #### "No assets found in the asset path"
This occurs when the `assets/` directory is empty. The validation system automatically copies source assets and regenerates them. This occurs when the `assets/` directory is empty. The validation system automatically copies source assets and regenerates them.
#### "Failed to generate Android assets" #### "Failed to generate Android assets"
Check that: Check that:
- Source assets exist in `resources/` - Source assets exist in `resources/`
- `@capacitor/assets` is installed - `@capacitor/assets` is installed
- You have write permissions to the Android directories - You have write permissions to the Android directories
#### "Asset generation completed but some resources are still missing" #### "Asset generation completed but some resources are still missing"
This indicates a problem with the asset generation tool. Try: This indicates a problem with the asset generation tool. Try:
1. Running `npm install` to ensure dependencies are up to date 1. Running `npm install` to ensure dependencies are up to date
2. Manually running `npx @capacitor/assets generate` 2. Manually running `npx @capacitor/assets generate`
3. Checking the asset generation logs for specific errors 3. Checking the asset generation logs for specific errors
### Manual Recovery ### Manual Recovery
If automatic regeneration fails, you can manually create the missing resources: If automatic regeneration fails, you can manually create the missing resources:
```bash ```bash
@@ -235,14 +213,12 @@ rm assets/icon.png assets/splash.png assets/splash_dark.png
## Future Enhancements ## Future Enhancements
### Planned Improvements ### Planned Improvements
- **iOS Asset Validation**: Extend validation to iOS assets - **iOS Asset Validation**: Extend validation to iOS assets
- **Asset Quality Checks**: Validate image dimensions and formats - **Asset Quality Checks**: Validate image dimensions and formats
- **Performance Optimization**: Cache validation results - **Performance Optimization**: Cache validation results
- **CI/CD Integration**: Add validation to GitHub Actions - **CI/CD Integration**: Add validation to GitHub Actions
### Configuration Options ### Configuration Options
- **Custom Asset Paths**: Support for different asset directory structures - **Custom Asset Paths**: Support for different asset directory structures
- **Validation Rules**: Configurable validation requirements - **Validation Rules**: Configurable validation requirements
- **Skip Options**: Ability to skip validation for specific scenarios - **Skip Options**: Ability to skip validation for specific scenarios

View File

@@ -122,4 +122,4 @@ export default class HomeView extends Vue {
--- ---
_This decision was made based on the current codebase architecture and team expertise. The mixin approach provides the best balance of performance, developer experience, and architectural consistency for the TimeSafari application._ *This decision was made based on the current codebase architecture and team expertise. The mixin approach provides the best balance of performance, developer experience, and architectural consistency for the TimeSafari application.*

View File

@@ -92,5 +92,5 @@ Multiple stack traces showing Vue router navigation and component mounting cycle
3. Address API/server issues in separate debugging session 3. Address API/server issues in separate debugging session
--- ---
_Log Entry by: Migration Assistant_ *Log Entry by: Migration Assistant*
_Session: ProjectsView.vue Triple Migration Pattern_ *Session: ProjectsView.vue Triple Migration Pattern*

View File

@@ -32,7 +32,6 @@ you apply 1-3 meta-rules that automatically include everything you need.
### **Step 1: Always Start with Core Always-On** ### **Step 1: Always Start with Core Always-On**
**Every single interaction** starts with: **Every single interaction** starts with:
``` ```
meta_core_always_on.mdc meta_core_always_on.mdc
``` ```
@@ -66,14 +65,12 @@ meta_core_always_on + meta_research + meta_bug_diagnosis
**Important**: Meta-rules represent **workflow phases**, not a rigid sequence. You can: **Important**: Meta-rules represent **workflow phases**, not a rigid sequence. You can:
### **Jump Between Phases Freely** ### **Jump Between Phases Freely**
- **Start with diagnosis** if you already know the problem - **Start with diagnosis** if you already know the problem
- **Go back to research** if your fix reveals new issues - **Go back to research** if your fix reveals new issues
- **Switch to planning** mid-implementation if scope changes - **Switch to planning** mid-implementation if scope changes
- **Document at any phase** - not just at the end - **Document at any phase** - not just at the end
### **Mode Switching by Invoking Meta-Rules** ### **Mode Switching by Invoking Meta-Rules**
Each meta-rule invocation **automatically switches your workflow mode**: Each meta-rule invocation **automatically switches your workflow mode**:
``` ```
@@ -83,13 +80,11 @@ Planning Mode → Invoke @meta_feature_implementation → Implementation Mode
``` ```
### **Phase Constraints, Not Sequence Constraints** ### **Phase Constraints, Not Sequence Constraints**
- **Within each phase**: Clear constraints on what you can/cannot do - **Within each phase**: Clear constraints on what you can/cannot do
- **Between phases**: Complete freedom to move as needed - **Between phases**: Complete freedom to move as needed
- **No forced order**: Choose the phase that matches your current need - **No forced order**: Choose the phase that matches your current need
### **Example of Flexible Workflow** ### **Example of Flexible Workflow**
``` ```
1. Start with @meta_research (investigation mode) 1. Start with @meta_research (investigation mode)
2. Jump to @meta_bug_diagnosis (diagnosis mode) 2. Jump to @meta_bug_diagnosis (diagnosis mode)
@@ -108,19 +103,16 @@ Planning Mode → Invoke @meta_feature_implementation → Implementation Mode
**Scenario**: User reports that the contact list isn't loading properly **Scenario**: User reports that the contact list isn't loading properly
**Initial Meta-Rule Selection**: **Initial Meta-Rule Selection**:
``` ```
meta_core_always_on + meta_research + meta_bug_diagnosis meta_core_always_on + meta_research + meta_bug_diagnosis
``` ```
**What This Gives You**: **What This Gives You**:
- **Core Always-On**: Human competence focus, time standards, context - **Core Always-On**: Human competence focus, time standards, context
- **Research**: Systematic investigation methodology, evidence collection - **Research**: Systematic investigation methodology, evidence collection
- **Bug Diagnosis**: Defect analysis framework, root cause identification - **Bug Diagnosis**: Defect analysis framework, root cause identification
**Flexible Workflow**: **Flexible Workflow**:
1. Apply core always-on for foundation 1. Apply core always-on for foundation
2. Use research meta-rule for systematic investigation 2. Use research meta-rule for systematic investigation
3. Switch to bug diagnosis when you have enough evidence 3. Switch to bug diagnosis when you have enough evidence
@@ -133,19 +125,16 @@ meta_core_always_on + meta_research + meta_bug_diagnosis
**Scenario**: Building a new contact search feature **Scenario**: Building a new contact search feature
**Meta-Rule Selection**: **Meta-Rule Selection**:
``` ```
meta_core_always_on + meta_feature_planning + meta_feature_implementation meta_core_always_on + meta_feature_planning + meta_feature_implementation
``` ```
**What This Gives You**: **What This Gives You**:
- **Core Always-On**: Foundation principles and context - **Core Always-On**: Foundation principles and context
- **Feature Planning**: Requirements analysis, architecture planning - **Feature Planning**: Requirements analysis, architecture planning
- **Feature Implementation**: Development workflow, testing strategy - **Feature Implementation**: Development workflow, testing strategy
**Iterative Workflow**: **Iterative Workflow**:
1. Start with core always-on 1. Start with core always-on
2. Use feature planning for design and requirements 2. Use feature planning for design and requirements
3. Switch to feature implementation for coding and testing 3. Switch to feature implementation for coding and testing
@@ -158,18 +147,15 @@ meta_core_always_on + meta_feature_planning + meta_feature_implementation
**Scenario**: Writing a migration guide for the new database system **Scenario**: Writing a migration guide for the new database system
**Meta-Rule Selection**: **Meta-Rule Selection**:
``` ```
meta_core_always_on + meta_documentation meta_core_always_on + meta_documentation
``` ```
**What This Gives You**: **What This Gives You**:
- **Core Always-On**: Foundation and context - **Core Always-On**: Foundation and context
- **Documentation**: Educational focus, templates, quality standards - **Documentation**: Educational focus, templates, quality standards
**Parallel Workflow**: **Parallel Workflow**:
1. Apply core always-on for foundation 1. Apply core always-on for foundation
2. Use documentation meta-rule for educational content creation 2. Use documentation meta-rule for educational content creation
3. **Can research** while documenting if you need more information 3. **Can research** while documenting if you need more information
@@ -212,35 +198,27 @@ Each meta-rule includes success criteria. Use these to validate your work:
## Common Meta-Rule Combinations ## Common Meta-Rule Combinations
### **Research + Diagnosis** ### **Research + Diagnosis**
``` ```
meta_core_always_on + meta_research + meta_bug_diagnosis meta_core_always_on + meta_research + meta_bug_diagnosis
``` ```
**Use for**: Complex bug investigations requiring systematic analysis **Use for**: Complex bug investigations requiring systematic analysis
### **Planning + Implementation** ### **Planning + Implementation**
``` ```
meta_core_always_on + meta_feature_planning + meta_feature_implementation meta_core_always_on + meta_feature_planning + meta_feature_implementation
``` ```
**Use for**: End-to-end feature development from concept to deployment **Use for**: End-to-end feature development from concept to deployment
### **Research + Planning** ### **Research + Planning**
``` ```
meta_core_always_on + meta_research + meta_feature_planning meta_core_always_on + meta_research + meta_feature_planning
``` ```
**Use for**: Feasibility research and solution design **Use for**: Feasibility research and solution design
### **Documentation + Context** ### **Documentation + Context**
``` ```
meta_core_always_on + meta_documentation + [context-specific] meta_core_always_on + meta_documentation + [context-specific]
``` ```
**Use for**: Creating comprehensive, educational documentation **Use for**: Creating comprehensive, educational documentation
## Best Practices ## Best Practices

View File

@@ -1,231 +0,0 @@
# TimeSafari — Native-First Notification System
**Status:** Ready for implementation
**Date:** 2025-09-07
**Author:** Matthew Raymer
---
## Executive Summary
Ship a **single, Native-First** notification system: OS-scheduled **background prefetch at Tlead** + **pre-armed** local notifications. Web-push is retired.
### What we deliver
- **Closed-app delivery:** Pre-armed locals fire even if the app is closed.
- **Freshness:** One prefetch attempt per slot at **Tlead**; ETag/TTL controls; skip when stale.
- **Android precision:** Exact alarms with permission; windowed fallback (±10m) otherwise.
- **Resilience:** Re-arm after reboot/time-change (Android receivers; iOS on next wake/silent push).
- **Cross-platform:** Same TS API (iOS/Android/Electron). Electron is best-effort while running.
### Success signals
- High delivery reliability, minute-precision on Android with permission.
- Prefetch budget hit rate at **Tlead**; zero stale deliveries beyond TTL.
---
## Strategic Plan
### Goal
Deliver 1..M daily notifications with **OS background prefetch at Tlead** and **rolling-window safety** so messages display with fresh content even when the app is closed.
### Tenets
- **Reliability first:** OS delivers once scheduled; no JS at delivery time.
- **Freshness with guardrails:** Prefetch at **Tlead**; enforce **TTL-at-fire**; ETag-aware.
- **Single system:** One TS API; native adapters swap under the hood.
- **Platform honesty:** Android exactness via permission; iOS best-effort budget.
### Architecture (high level)
App (Vue/TS) → Orchestrator (policy) → Native Adapters:
- **SchedulerNative** — AlarmManager (Android) / UNUserNotificationCenter (iOS)
- **BackgroundPrefetchNative** — WorkManager (Android) / BGTaskScheduler (+ silent push) (iOS)
- **DataStore** — SQLite
**Storage (single shared DB):** The app and the native plugin will use **the same SQLite database file**. The app owns schema/migrations; the plugin opens the same file with WAL enabled and performs short, serialized writes. This keeps one source of truth for payloads, delivery logs, and config.
### SQLite Ownership & Concurrency
* **One DB file:** The plugin opens the **same path** the app uses (no second DB).
* **Migrations owned by app:** The app executes schema migrations and bumps `PRAGMA user_version`. The plugin **never** migrates; it **asserts** the expected version.
* **WAL mode:** Open DB with `journal_mode=WAL`, `synchronous=NORMAL`, `busy_timeout=5000`, `foreign_keys=ON`. WAL allows foreground reads while a background job commits quickly.
* **Single-writer discipline:** Background jobs write in **short transactions** (UPSERT per slot), then return.
* **Encryption (optional):** If using SQLCipher, the **same key** is used by both app and plugin. Do not mix encrypted and unencrypted openings.
### Scheduling & Tlead
- **Arm** a rolling window (today + tomorrow within iOS cap).
- **Attempt** a single **online-first** fetch per slot at **Tlead = T prefetchLeadMinutes**.
- If prefetch is skipped, the armed local **still fires** using cached content.
### Policies
- **TTL-at-fire:** If (T fetchedAt) > `ttlSeconds`**skip** arming.
- **Android exactness:** Request `SCHEDULE_EXACT_ALARM`; fallback **±10m** window.
- **Reboot/time change:** Android receivers re-arm next 24h; iOS on next wake/silent push.
- **No delivery-time mutation:** iOS locals cannot be mutated by NSE; render before scheduling.
---
## Implementation Guide
### 1) Interfaces (TS stable)
- **SchedulerNative**: `scheduleExact({slotId, whenMs, title, body, extra})`, `scheduleWindow(..., windowLenMs)`, `cancelBySlot`, `rescheduleAll`, `capabilities()`
- **BackgroundPrefetchNative**: `schedulePrefetch(slotId, atMs)`, `cancelPrefetch(slotId)`
- **DataStore**: SQLite adapters (notif_contents, notif_deliveries, notif_config)
- **Public API**: `configure`, `requestPermissions`, `runFullPipelineNow`, `reschedule`, `getState`
### DB Path & Adapter Configuration
* **Configure option:** `dbPath: string` (absolute path or platform alias) is passed from JS to the plugin during `configure()`.
* **Shared tables:**
* `notif_contents(slot_id, payload_json, fetched_at, etag, …)`
* `notif_deliveries(slot_id, fire_at, delivered_at, status, error_code, …)`
* `notif_config(k, v)`
* **Open settings:**
* `journal_mode=WAL`
* `synchronous=NORMAL`
* `busy_timeout=5000`
* `foreign_keys=ON`
**Type (TS) extension**
```ts
export type ConfigureOptions = {
// …existing fields…
dbPath: string; // shared DB file the plugin will open
storage: 'shared'; // canonical value; plugin-owned DB is not used
};
```
**Plugin side (pseudo)**
```kotlin
// Android open
val db = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE)
db.execSQL("PRAGMA journal_mode=WAL")
db.execSQL("PRAGMA synchronous=NORMAL")
db.execSQL("PRAGMA foreign_keys=ON")
db.execSQL("PRAGMA busy_timeout=5000")
// Verify schema version
val uv = rawQuery("PRAGMA user_version").use { it.moveToFirst(); it.getInt(0) }
require(uv >= MIN_EXPECTED_VERSION) { "Schema version too old" }
```
```swift
// iOS open (FMDB / SQLite3)
// Set WAL via PRAGMA after open; check user_version the same way.
```
### 2) Templating & Arming
- Render `title/body` **before** scheduling; pass via **SchedulerNative**.
- Route all arming through **SchedulerNative** to centralize Android exact/window semantics.
### 3) Tlead (single attempt)
**Tlead governs prefetch, not arming.** We **arm** one-shot locals as part of the rolling window so closed-app delivery is guaranteed. At **Tlead = T prefetchLeadMinutes**, the **native background job** attempts **one** 12s ETag-aware fetch. If fresh content arrives and will not violate **TTL-at-fire**, we (re)arm the upcoming slot; if the OS skips the wake, the pre-armed local still fires with cached content.
- Compute Tlead = `whenMs - prefetchLeadMinutes*60_000`.
- `BackgroundPrefetchNative.schedulePrefetch(slotId, atMs=Tlead)`.
- On wake: **ETag** fetch (timeout **12s**), persist, optionally cancel & re-arm if within TTL.
- Never fetch at delivery time.
### 4) TTL-at-fire
**TTL-at-fire:** Before arming for time **T**, compute `T fetchedAt`. If that exceeds `ttlSeconds`, **do not arm** (skip). This prevents posting stale notifications when the app has been closed for a long time.
`if (whenMs - fetchedAt) > ttlSeconds*1000 → skip`
### 5) Android specifics
- Request `SCHEDULE_EXACT_ALARM`; deep-link if denied; fallback to `setWindow(start,len)` (±10m).
- Receivers: `BOOT_COMPLETED`, `TIMEZONE_CHANGED`, `TIME_SET` → recompute & re-arm for next 24h and schedule Tlead prefetch.
### 6) iOS specifics
- `BGTaskScheduler` for Tlead prefetch (best-effort). Optional silent push nudge.
- Locals: `UNCalendarNotificationTrigger` (one-shots); no NSE mutation for locals.
### 7) Network & Timeouts
- Content fetch: **12s** timeout; single attempt at Tlead; ETag/304 respected.
- ACK/Error: **8s** timeout, fire-and-forget.
### 8) Electron
- Notifications while app is running; recommend **Start-on-Login**. No true background scheduling when fully closed.
### 9) Telemetry
- Record `scheduled|shown|error`; ACK deliveries (8s timeout); include slot/times/TZ/app version.
---
## Capability Matrix
| Capability | Android (Native) | iOS (Native) | Electron | Web |
|---|---|---|---|---|
| Multi-daily locals (closed app) | ✅ | ✅ | ✅ (app running) | — |
| Prefetch at Tlead (app closed) | ✅ WorkManager | ⚠️ BGTask (best-effort) | ✅ (app running) | — |
| Re-arm after reboot/time-change | ✅ Receivers | ⚠️ On next wake/silent push | ✅ Start-on-Login | — |
| Minute-precision alarms | ✅ with exact permission | ❌ not guaranteed | ✅ timer best-effort | — |
| Delivery-time mutation for locals | ❌ | ❌ | — | — |
| ETag/TTL enforcement | ✅ | ✅ | ✅ | — |
| Rolling-window safety | ✅ | ✅ | ✅ | — |
---
## Acceptance Criteria
### Core
- **Closed-app delivery:** Armed locals fire at T with last rendered content. No delivery-time network.
- **Tlead prefetch:** Single background attempt at **Tlead**; if skipped, delivery still occurs from cache.
- **TTL-at-fire:** No armed local violates TTL at T.
### Android
- **Exact permission path:** With `SCHEDULE_EXACT_ALARM` → within ±1m; else **±10m** window.
- **Reboot recovery:** After reboot, receivers re-arm next 24h and schedule Tlead prefetch.
- **TZ/DST change:** Recompute & re-arm; future slots align to new wall-clock.
### iOS
- **BGTask budget respected:** Prefetch often runs but may be skipped; delivery still occurs via rolling window.
- **Force-quit caveat:** No background execution after user terminate; delivery still occurs if pre-armed.
### Electron
- **Running-app rule:** Delivery only while app runs; with Start-on-Login, after reboot the orchestrator re-arms and subsequent slots deliver.
### Network
- Content fetch timeout **12s**; ACK/Error **8s**; no retries inside lead; ETag honored.
### Observability
- Log/telemetry for `scheduled|shown|error`; ACK payload includes slot, times, device TZ, app version.
### DB Sharing
* **Shared DB visibility:** A background prefetch writes `notif_contents`; the foreground UI **immediately** reads the same row.
* **WAL overlap:** With the app reading while the plugin commits, no user-visible blocking occurs.
* **Version safety:** If `user_version` is behind, the plugin emits an error and does not write (protects against partial installs).
---
## Web-Push Cleanup
Web-push functionality has been retired due to unreliability. All web-push related code paths and documentation sections should be removed or marked as deprecated. See `web-push-cleanup-guide.md` for detailed cleanup steps.
---
_This document consolidates the Native-First notification system strategy, implementation details, capabilities, and acceptance criteria into a single comprehensive reference._

View File

@@ -0,0 +1,181 @@
# Seed Phrase Backup Reminder Implementation
## Overview
This implementation adds a modal dialog that reminds users to back up their seed phrase if they haven't done so yet. The reminder appears after specific user actions and includes a 24-hour cooldown to avoid being too intrusive.
## Features
- **Modal Dialog**: Uses the existing notification group modal system from `App.vue`
- **Smart Timing**: Only shows when `hasBackedUpSeed = false`
- **24-Hour Cooldown**: Uses localStorage to prevent showing more than once per day
- **Action-Based Triggers**: Shows after specific user actions
- **User Choice**: "Backup Identifier Seed" or "Remind me Later" options
## Implementation Details
### Core Utility (`src/utils/seedPhraseReminder.ts`)
The main utility provides:
- `shouldShowSeedReminder(hasBackedUpSeed)`: Checks if reminder should be shown
- `markSeedReminderShown()`: Updates localStorage timestamp
- `createSeedReminderNotification()`: Creates the modal configuration
- `showSeedPhraseReminder(hasBackedUpSeed, notifyFunction)`: Main function to show reminder
### Trigger Points
The reminder is shown after these user actions:
**Note**: The reminder is triggered by **claim creation** actions, not claim confirmations. This focuses on when users are actively creating new content rather than just confirming existing claims.
1. **Profile Saving** (`AccountViewView.vue`)
- After clicking "Save Profile" button
- Only when profile save is successful
2. **Claim Creation** (Multiple views)
- `ClaimAddRawView.vue`: After submitting raw claims
- `GiftedDialog.vue`: After creating gifts/claims
- `GiftedDetailsView.vue`: After recording gifts/claims
- `OfferDialog.vue`: After creating offers
3. **QR Code Views Exit**
- `ContactQRScanFullView.vue`: When exiting via back button
- `ContactQRScanShowView.vue`: When exiting via back button
### Modal Configuration
```typescript
{
group: "modal",
type: "confirm",
title: "Backup Your Identifier Seed?",
text: "It looks like you haven't backed up your identifier seed yet. It's important to back it up as soon as possible to secure your identity.",
yesText: "Backup Identifier Seed",
noText: "Remind me Later",
onYes: () => navigate to /seed-backup,
onNo: () => mark as shown for 24 hours,
onCancel: () => mark as shown for 24 hours
}
```
**Important**: The modal is configured with `timeout: -1` to ensure it stays open until the user explicitly interacts with one of the buttons. This prevents the dialog from closing automatically.
### Cooldown Mechanism
- **Storage Key**: `seedPhraseReminderLastShown`
- **Cooldown Period**: 24 hours (24 * 60 * 60 * 1000 milliseconds)
- **Implementation**: localStorage with timestamp comparison
- **Fallback**: Shows reminder if timestamp is invalid or missing
## User Experience
### When Reminder Appears
- User has not backed up their seed phrase (`hasBackedUpSeed = false`)
- At least 24 hours have passed since last reminder
- User performs one of the trigger actions
- **1-second delay** after the success message to allow users to see the confirmation
### User Options
1. **"Backup Identifier Seed"**: Navigates to `/seed-backup` page
2. **"Remind me Later"**: Dismisses and won't show again for 24 hours
3. **Cancel/Close**: Same behavior as "Remind me Later"
### Frequency Control
- **First Time**: Always shows if user hasn't backed up
- **Subsequent**: Only shows after 24-hour cooldown
- **Automatic Reset**: When user completes seed backup (`hasBackedUpSeed = true`)
## Technical Implementation
### Error Handling
- Graceful fallback if localStorage operations fail
- Logging of errors for debugging
- Non-blocking implementation (doesn't affect main functionality)
### Integration Points
- **Platform Service**: Uses `$accountSettings()` to check backup status
- **Notification System**: Integrates with existing `$notify` system
- **Router**: Uses `window.location.href` for navigation
### Performance Considerations
- Minimal localStorage operations
- No blocking operations
- Efficient timestamp comparisons
- **Timing Behavior**: 1-second delay before showing reminder to improve user experience flow
## Testing
### Manual Testing Scenarios
1. **First Time User**
- Create new account
- Perform trigger action (save profile, create claim, exit QR view)
- Verify reminder appears
2. **Repeat User (Within 24h)**
- Perform trigger action
- Verify reminder does NOT appear
3. **Repeat User (After 24h)**
- Wait 24+ hours
- Perform trigger action
- Verify reminder appears again
4. **User Who Has Backed Up**
- Complete seed backup
- Perform trigger action
- Verify reminder does NOT appear
5. **QR Code View Exit**
- Navigate to QR code view (full or show)
- Exit via back button
- Verify reminder appears (if conditions are met)
### Browser Testing
- Test localStorage functionality
- Verify timestamp handling
- Check navigation to seed backup page
## Future Enhancements
### Potential Improvements
1. **Customizable Cooldown**: Allow users to set reminder frequency
2. **Progressive Urgency**: Increase reminder frequency over time
3. **Analytics**: Track reminder effectiveness and user response
4. **A/B Testing**: Test different reminder messages and timing
### Configuration Options
- Reminder frequency settings
- Custom reminder messages
- Different trigger conditions
- Integration with other notification systems
## Maintenance
### Monitoring
- Check localStorage usage in browser dev tools
- Monitor user feedback about reminder frequency
- Track navigation success to seed backup page
### Updates
- Modify reminder text in `createSeedReminderNotification()`
- Adjust cooldown period in `REMINDER_COOLDOWN_MS` constant
- Add new trigger points as needed
## Conclusion
This implementation provides a non-intrusive way to remind users about seed phrase backup while respecting their preferences and avoiding notification fatigue. The 24-hour cooldown ensures users aren't overwhelmed while maintaining the importance of the security reminder.
The feature is fully integrated with the existing codebase architecture and follows established patterns for notifications, error handling, and user interaction.

View File

@@ -1,551 +0,0 @@
# TimeSafari Web-Push Cleanup Guide
**Status:** 🚀 Native-First Implementation
**Date:** 2025-01-27T14:30Z (UTC)
**Author:** Matthew Raymer
**Scope:** Web-push code cleanup and deprecation
**Goal:** Remove or quarantine all web-push code paths and mark as deprecated.
---
## Executive Summary
This document provides a comprehensive cleanup guide for removing web-push code
paths from TimeSafari. Web-push has been retired for unreliability, and the
system now focuses on native mobile reliability with Electron best-effort support.
---
## Cleanup Strategy
### Phase 1: Identify Web-Push Code Paths
#### Service Worker Files
- [ ] `sw_scripts/notification-click.js` - Mark as deprecated
- [ ] `sw_scripts/` directory - Review for web-push dependencies
- [ ] Service worker registration code - Remove or quarantine
#### Web-Specific Code
- [ ] Web push notification handlers
- [ ] Service worker event listeners
- [ ] Web notification API usage
- [ ] Push subscription management
#### Configuration Files
- [ ] VitePWA plugin configuration
- [ ] Service worker build configuration
- [ ] Web push manifest files
### Phase 2: Mark as Deprecated
#### Code Comments
```javascript
// DEPRECATED: Web-push notification handling
// This code is kept for reference but not used in production
// Replaced by Native-First notification system
```
#### Documentation Updates
- [ ] Mark web-push sections as deprecated
- [ ] Add deprecation notices
- [ ] Update README files
- [ ] Update API documentation
### Phase 3: Remove or Quarantine
#### Complete Removal
- [ ] Web push subscription code
- [ ] Service worker notification handlers
- [ ] Web-specific notification APIs
- [ ] Push message handling
#### Quarantine (Keep for Reference)
- [ ] Service worker registration code
- [ ] Web push configuration
- [ ] Historical web-push tests
---
## Detailed Cleanup Tasks
### 1. Service Worker Cleanup
#### Files to Deprecate
**`sw_scripts/notification-click.js`**
```javascript
// DEPRECATED: Service worker notification handling
// This code is kept for reference but not used in production
// Replaced by Native-First notification system
// Original web-push notification click handler
self.addEventListener('notificationclick', (event) => {
// DEPRECATED: Web-push only
event.notification.close();
const slotId = event.notification.data?.slotId;
const route = slotId ? '/#/daily' : '/#/notifications';
event.waitUntil(
clients.openWindow(route).catch(() => {
return clients.openWindow('/');
})
);
});
```
**Service Worker Registration**
```javascript
// DEPRECATED: Service worker registration
// This code is kept for reference but not used in production
// Replaced by Native-First notification system
if ('serviceWorker' in navigator && process.env.VITE_PLATFORM === 'web') {
// DEPRECATED: Web-push only
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker registered:', registration);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
}
```
### 2. Web Push API Cleanup
#### Push Subscription Management
```javascript
// DEPRECATED: Web push subscription management
// This code is kept for reference but not used in production
// Replaced by Native-First notification system
class WebPushManager {
// DEPRECATED: Web-push only
async subscribeToPush() {
// Implementation kept for reference
}
// DEPRECATED: Web-push only
async unsubscribeFromPush() {
// Implementation kept for reference
}
}
```
#### Push Message Handling
```javascript
// DEPRECATED: Push message handling
// This code is kept for reference but not used in production
// Replaced by Native-First notification system
self.addEventListener('push', (event) => {
// DEPRECATED: Web-push only
const data = event.data ? event.data.json() : {};
const options = {
body: data.body,
icon: '/icon-192x192.png',
badge: '/badge-72x72.png',
data: data
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
```
### 3. Configuration Cleanup
#### VitePWA Plugin Configuration
```javascript
// DEPRECATED: VitePWA plugin configuration
// This configuration is kept for reference but not used in production
// Replaced by Native-First notification system
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
VitePWA({
// DEPRECATED: Web-push only
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg}']
},
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'],
manifest: {
name: 'TimeSafari',
short_name: 'TimeSafari',
description: 'TimeSafari App',
theme_color: '#ffffff',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
}
]
}
})
]
})
```
#### Service Worker Build Configuration
```javascript
// DEPRECATED: Service worker build configuration
// This configuration is kept for reference but not used in production
// Replaced by Native-First notification system
export default defineConfig({
build: {
rollupOptions: {
input: {
// DEPRECATED: Web-push only
sw: 'sw_scripts/notification-click.js'
}
}
}
})
```
### 4. Test Cleanup
#### Web Push Tests
```javascript
// DEPRECATED: Web push tests
// These tests are kept for reference but not used in production
// Replaced by Native-First notification system
describe('Web Push Notifications (DEPRECATED)', () => {
// DEPRECATED: Web-push only
it('should handle push notifications', async () => {
// Test implementation kept for reference
});
// DEPRECATED: Web-push only
it('should handle notification clicks', async () => {
// Test implementation kept for reference
});
});
```
#### Service Worker Tests
```javascript
// DEPRECATED: Service worker tests
// These tests are kept for reference but not used in production
// Replaced by Native-First notification system
describe('Service Worker (DEPRECATED)', () => {
// DEPRECATED: Web-push only
it('should register service worker', async () => {
// Test implementation kept for reference
});
// DEPRECATED: Web-push only
it('should handle push events', async () => {
// Test implementation kept for reference
});
});
```
### 5. Documentation Cleanup
#### README Updates
```markdown
# TimeSafari Native-First Notification System
## Web-Push Status: DEPRECATED
Web-push has been retired for unreliability. The system now focuses on native mobile reliability with Electron best-effort support.
### Deprecated Features
- ❌ Web push notifications
- ❌ Service worker notification handling
- ❌ Web notification API
### Active Features
- ✅ Native mobile notifications (Android/iOS)
- ✅ Electron notifications (best-effort)
- ✅ OS-scheduled background prefetch
- ✅ Rolling window safety
```
#### API Documentation Updates
```markdown
## Notification API (Native-First)
### Deprecated Methods
- `subscribeToPush()` - DEPRECATED: Web-push only
- `unsubscribeFromPush()` - DEPRECATED: Web-push only
- `handlePushMessage()` - DEPRECATED: Web-push only
### Active Methods
- `scheduleExact()` - Native exact scheduling
- `scheduleWindow()` - Native windowed scheduling
- `schedulePrefetch()` - Native background prefetch
```
---
## File-by-File Cleanup Checklist
### Service Worker Files
- [ ] `sw_scripts/notification-click.js` - Mark as deprecated
- [ ] `sw_scripts/` directory - Review for web-push dependencies
- [ ] Service worker build configuration - Remove or quarantine
### Web-Specific Code
- [ ] `src/main.web.ts` - Remove service worker registration
- [ ] `src/services/webPush.ts` - Mark as deprecated
- [ ] `src/utils/serviceWorker.ts` - Mark as deprecated
- [ ] Web notification API usage - Remove or quarantine
### Configuration Files
- [ ] `vite.config.web.mts` - Remove VitePWA plugin
- [ ] `package.json` - Remove web-push dependencies
- [ ] `public/manifest.json` - Mark as deprecated
- [ ] Service worker build scripts - Remove or quarantine
### Test Files
- [ ] `test-playwright/web-push.spec.ts` - Mark as deprecated
- [ ] `test/services/webPush.test.ts` - Mark as deprecated
- [ ] Service worker tests - Mark as deprecated
### Documentation Files
- [ ] `README.md` - Update to reflect native-first approach
- [ ] `doc/web-push.md` - Mark as deprecated
- [ ] API documentation - Remove web-push references
---
## Dependencies to Remove
### NPM Packages
```json
{
"dependencies": {
// DEPRECATED: Web-push only
"web-push": "^7.4.0",
"vite-plugin-pwa": "^0.17.0"
},
"devDependencies": {
// DEPRECATED: Web-push only
"workbox-webpack-plugin": "^6.5.0"
}
}
```
### Build Scripts
```json
{
"scripts": {
// DEPRECATED: Web-push only
"build:sw": "workbox generateSW",
"test:sw": "jest --testPathPattern=serviceWorker"
}
}
```
---
## Migration Guide
### From Web-Push to Native-First
#### Step 1: Remove Web-Push Dependencies
```bash
# Remove web-push packages
npm uninstall web-push vite-plugin-pwa workbox-webpack-plugin
# Remove service worker files
rm -rf sw_scripts/
rm -f public/sw.js
rm -f public/workbox-*.js
```
#### Step 2: Update Configuration
```javascript
// Remove VitePWA plugin from vite.config.web.mts
export default defineConfig({
plugins: [
// Remove VitePWA plugin
// VitePWA({ ... })
]
})
```
#### Step 3: Update Service Registration
```javascript
// Remove service worker registration from main.web.ts
// if ('serviceWorker' in navigator) {
// navigator.serviceWorker.register('/sw.js')
// }
```
#### Step 4: Update Tests
```javascript
// Remove web-push tests
// describe('Web Push Notifications', () => { ... })
```
---
## Verification Checklist
### Code Removal Verification
- [ ] No web-push imports remain
- [ ] No service worker registration code
- [ ] No push subscription management
- [ ] No web notification API usage
- [ ] No VitePWA plugin configuration
### Documentation Verification
- [ ] All web-push references marked as deprecated
- [ ] README updated to reflect native-first approach
- [ ] API documentation updated
- [ ] Test documentation updated
### Build Verification
- [ ] Web build succeeds without service worker
- [ ] No service worker files generated
- [ ] No web-push dependencies in bundle
- [ ] Native builds work correctly
### Test Verification
- [ ] Web-push tests are marked as deprecated
- [ ] Native notification tests pass
- [ ] No web-push test failures
- [ ] Test suite runs successfully
---
## Rollback Plan
### Emergency Rollback
If native-first implementation fails, web-push code can be restored:
#### 1. **Restore Dependencies**
```bash
npm install web-push vite-plugin-pwa workbox-webpack-plugin
```
#### 2. **Restore Service Worker Files**
```bash
git checkout HEAD~1 -- sw_scripts/
git checkout HEAD~1 -- public/sw.js
```
#### 3. **Restore Configuration**
```bash
git checkout HEAD~1 -- vite.config.web.mts
git checkout HEAD~1 -- package.json
```
#### 4. **Restore Tests**
```bash
git checkout HEAD~1 -- test-playwright/web-push.spec.ts
git checkout HEAD~1 -- test/services/webPush.test.ts
```
### Rollback Verification
- [ ] Web-push functionality restored
- [ ] Service worker registration works
- [ ] Push notifications work
- [ ] Tests pass
---
## Post-Cleanup Tasks
### Code Review
- [ ] Review all changes for completeness
- [ ] Verify no web-push code remains
- [ ] Check for orphaned references
- [ ] Validate native-first implementation
### Testing
- [ ] Run full test suite
- [ ] Verify native notifications work
- [ ] Check Electron functionality
- [ ] Validate mobile builds
### Documentation
- [ ] Update all documentation
- [ ] Remove web-push references
- [ ] Update API documentation
- [ ] Update user guides
---
## Success Criteria
### Complete Web-Push Removal
- [ ] All web-push code marked as deprecated
- [ ] Service worker files quarantined
- [ ] Dependencies removed
- [ ] Configuration updated
### Native-First Implementation
- [ ] Native notifications work on Android
- [ ] Native notifications work on iOS
- [ ] Electron notifications work
- [ ] Background prefetch works
### Documentation Updated
- [ ] All docs reflect native-first approach
- [ ] Web-push marked as deprecated
- [ ] Migration guide provided
- [ ] Rollback plan documented
---
_This cleanup guide provides comprehensive instructions for removing web-push
code paths from TimeSafari. Web-push has been retired for unreliability, and the
system now focuses on native mobile reliability with Electron best-effort support._

View File

@@ -5,15 +5,12 @@
**Status**: 🎯 **ACTIVE** - Z-index layering standards **Status**: 🎯 **ACTIVE** - Z-index layering standards
## Objective ## Objective
Establish consistent z-index values across the TimeSafari application to ensure proper layering of UI elements. Establish consistent z-index values across the TimeSafari application to ensure proper layering of UI elements.
## Result ## Result
This document defines the z-index hierarchy for all UI components. This document defines the z-index hierarchy for all UI components.
## Use/Run ## Use/Run
Reference these values when implementing new components or modifying existing ones to maintain consistent layering. Reference these values when implementing new components or modifying existing ones to maintain consistent layering.
## Z-Index Hierarchy ## Z-Index Hierarchy

View File

@@ -136,6 +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"
}, },
"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",

View File

@@ -16,6 +16,12 @@ messages * - Conditional UI based on platform capabilities * * @component *
:to="{ name: 'seed-backup' }" :to="{ name: 'seed-backup' }"
:class="backupButtonClasses" :class="backupButtonClasses"
> >
<!-- Notification dot - show while the user has not yet backed up their seed phrase -->
<font-awesome
v-if="!hasBackedUpSeed"
icon="circle"
class="absolute -right-[8px] -top-[8px] text-rose-500 text-[14px] border border-white rounded-full"
></font-awesome>
Backup Identifier Seed Backup Identifier Seed
</router-link> </router-link>
@@ -98,6 +104,12 @@ export default class DataExportSection extends Vue {
*/ */
isExporting = false; isExporting = false;
/**
* Flag indicating if the user has backed up their seed phrase
* Used to control the visibility of the notification dot
*/
hasBackedUpSeed = false;
/** /**
* Notification helper for consistent notification patterns * Notification helper for consistent notification patterns
* Created as a getter to ensure $notify is available when called * Created as a getter to ensure $notify is available when called
@@ -129,7 +141,7 @@ export default class DataExportSection extends Vue {
* CSS classes for the backup button (router link) * CSS classes for the backup button (router link)
*/ */
get backupButtonClasses(): string { get backupButtonClasses(): string {
return "block w-full text-center text-md 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-1.5 py-2 rounded-md mb-2 mt-2"; return "block relative w-full text-center text-md 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-1.5 py-2 rounded-md mb-2 mt-2";
} }
/** /**
@@ -218,6 +230,22 @@ export default class DataExportSection extends Vue {
created() { created() {
this.notify = createNotifyHelpers(this.$notify); this.notify = createNotifyHelpers(this.$notify);
this.loadSeedBackupStatus();
}
/**
* Loads the seed backup status from account settings
* Updates the hasBackedUpSeed flag to control notification dot visibility
*/
private async loadSeedBackupStatus(): Promise<void> {
try {
const settings = await this.$accountSettings();
this.hasBackedUpSeed = !!settings.hasBackedUpSeed;
} catch (err: unknown) {
logger.error("Failed to load seed backup status:", err);
// Default to false (show notification dot) if we can't load the setting
this.hasBackedUpSeed = false;
}
} }
} }
</script> </script>

View File

@@ -82,6 +82,7 @@ import GiftDetailsStep from "../components/GiftDetailsStep.vue";
import { PlanData } from "../interfaces/records"; import { PlanData } from "../interfaces/records";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS, NotifyFunction } from "@/utils/notify"; import { createNotifyHelpers, TIMEOUTS, NotifyFunction } from "@/utils/notify";
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
import { import {
NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT, NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT,
NOTIFY_GIFT_ERROR_NO_DESCRIPTION, NOTIFY_GIFT_ERROR_NO_DESCRIPTION,
@@ -411,6 +412,15 @@ export default class GiftedDialog extends Vue {
); );
} else { } else {
this.safeNotify.success("That gift was recorded.", TIMEOUTS.VERY_LONG); this.safeNotify.success("That gift was recorded.", TIMEOUTS.VERY_LONG);
// Show seed phrase backup reminder if needed
try {
const settings = await this.$accountSettings();
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
} catch (error) {
logger.error("Error checking seed backup status:", error);
}
if (this.callbackOnSuccess) { if (this.callbackOnSuccess) {
this.callbackOnSuccess(amount); this.callbackOnSuccess(amount);
} }

View File

@@ -64,6 +64,7 @@ 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 { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
import { import {
NOTIFY_OFFER_SETTINGS_ERROR, NOTIFY_OFFER_SETTINGS_ERROR,
NOTIFY_OFFER_RECORDING, NOTIFY_OFFER_RECORDING,
@@ -299,6 +300,14 @@ export default class OfferDialog extends Vue {
); );
} else { } else {
this.notify.success(NOTIFY_OFFER_SUCCESS.message, TIMEOUTS.VERY_LONG); this.notify.success(NOTIFY_OFFER_SUCCESS.message, TIMEOUTS.VERY_LONG);
// Show seed phrase backup reminder if needed
try {
const settings = await this.$accountSettings();
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
} catch (error) {
logger.error("Error checking seed backup status:", error);
}
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {

View File

@@ -124,6 +124,12 @@ const MIGRATIONS = [
ALTER TABLE contacts ADD COLUMN iViewContent BOOLEAN DEFAULT TRUE; ALTER TABLE contacts ADD COLUMN iViewContent BOOLEAN DEFAULT TRUE;
`, `,
}, },
{
name: "003_add_hasBackedUpSeed_to_settings",
sql: `
ALTER TABLE settings ADD COLUMN hasBackedUpSeed BOOLEAN DEFAULT FALSE;
`,
},
]; ];
/** /**

View File

@@ -29,6 +29,7 @@ export type Settings = {
finishedOnboarding?: boolean; // the user has completed the onboarding process finishedOnboarding?: boolean; // the user has completed the onboarding process
firstName?: string; // user's full name, may be null if unwanted for a particular account firstName?: string; // user's full name, may be null if unwanted for a particular account
hasBackedUpSeed?: boolean; // tracks whether the user has backed up their seed phrase
hideRegisterPromptOnNewContact?: boolean; hideRegisterPromptOnNewContact?: boolean;
isRegistered?: boolean; isRegistered?: boolean;
// imageServer?: string; // if we want to allow modification then we should make image functionality optional -- or at least customizable // imageServer?: string; // if we want to allow modification then we should make image functionality optional -- or at least customizable

View File

@@ -1313,6 +1313,28 @@ export const capitalizeAndInsertSpacesBeforeCaps = (text: string) => {
: text[0].toUpperCase() + text.substr(1).replace(/([A-Z])/g, " $1"); : text[0].toUpperCase() + text.substr(1).replace(/([A-Z])/g, " $1");
}; };
/**
* Formats type string for display by adding spaces before capitals
* and optionally adds an appropriate article prefix (a/an)
*
* @param text - Text to format
* @returns Formatted string with article prefix
*/
export const capitalizeAndInsertSpacesBeforeCapsWithAPrefix = (
text: string,
): string => {
const word = capitalizeAndInsertSpacesBeforeCaps(text);
if (word) {
// if the word starts with a vowel, use "an" instead of "a"
const firstLetter = word[0].toLowerCase();
const vowels = ["a", "e", "i", "o", "u"];
const particle = vowels.includes(firstLetter) ? "an" : "a";
return particle + " " + word;
} else {
return "";
}
};
/** /**
return readable summary of claim, or something generic return readable summary of claim, or something generic

View File

@@ -160,6 +160,41 @@ export const isGiveAction = (
return isGiveClaimType(veriClaim.claimType); return isGiveClaimType(veriClaim.claimType);
}; };
export interface OfferFulfillment {
offerHandleId: string;
offerType: string;
}
/**
* Extract offer fulfillment information from the fulfills field
* Handles both array and single object cases
*/
export const extractOfferFulfillment = (fulfills: any): OfferFulfillment | null => {
if (!fulfills) {
return null;
}
// Handle both array and single object cases
let offerFulfill = null;
if (Array.isArray(fulfills)) {
// Find the Offer in the fulfills array
offerFulfill = fulfills.find((item) => item["@type"] === "Offer");
} else if (fulfills["@type"] === "Offer") {
// fulfills is a single Offer object
offerFulfill = fulfills;
}
if (offerFulfill) {
return {
offerHandleId: offerFulfill.identifier,
offerType: offerFulfill["@type"],
};
}
return null;
};
export const shortDid = (did: string) => { export const shortDid = (did: string) => {
if (did.startsWith("did:peer:")) { if (did.startsWith("did:peer:")) {
return ( return (

View File

@@ -36,7 +36,7 @@ export class WebInlineQRScanner implements QRScannerService {
// Generate a short random ID for this scanner instance // Generate a short random ID for this scanner instance
this.id = Math.random().toString(36).substring(2, 8).toUpperCase(); this.id = Math.random().toString(36).substring(2, 8).toUpperCase();
this.options = options ?? {}; this.options = options ?? {};
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Initializing scanner with options:`, `[WebInlineQRScanner:${this.id}] Initializing scanner with options:`,
{ {
...this.options, ...this.options,
@@ -49,7 +49,7 @@ export class WebInlineQRScanner implements QRScannerService {
this.context = this.canvas.getContext("2d", { willReadFrequently: true }); this.context = this.canvas.getContext("2d", { willReadFrequently: true });
this.video = document.createElement("video"); this.video = document.createElement("video");
this.video.setAttribute("playsinline", "true"); // Required for iOS this.video.setAttribute("playsinline", "true"); // Required for iOS
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] DOM elements created successfully`, `[WebInlineQRScanner:${this.id}] DOM elements created successfully`,
); );
} }
@@ -60,7 +60,7 @@ export class WebInlineQRScanner implements QRScannerService {
this.cameraStateListeners.forEach((listener) => { this.cameraStateListeners.forEach((listener) => {
try { try {
listener.onStateChange(state, message); listener.onStateChange(state, message);
logger.info( logger.debug(
`[WebInlineQRScanner:${this.id}] Camera state changed to: ${state}`, `[WebInlineQRScanner:${this.id}] Camera state changed to: ${state}`,
{ {
state, state,
@@ -89,7 +89,7 @@ export class WebInlineQRScanner implements QRScannerService {
async checkPermissions(): Promise<boolean> { async checkPermissions(): Promise<boolean> {
try { try {
this.updateCameraState("initializing", "Checking camera permissions..."); this.updateCameraState("initializing", "Checking camera permissions...");
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Checking camera permissions...`, `[WebInlineQRScanner:${this.id}] Checking camera permissions...`,
); );
@@ -99,7 +99,7 @@ export class WebInlineQRScanner implements QRScannerService {
const permissions = await navigator.permissions.query({ const permissions = await navigator.permissions.query({
name: "camera" as PermissionName, name: "camera" as PermissionName,
}); });
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Permission state from Permissions API:`, `[WebInlineQRScanner:${this.id}] Permission state from Permissions API:`,
permissions.state, permissions.state,
); );
@@ -165,7 +165,7 @@ export class WebInlineQRScanner implements QRScannerService {
"initializing", "initializing",
"Requesting camera permissions...", "Requesting camera permissions...",
); );
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Requesting camera permissions...`, `[WebInlineQRScanner:${this.id}] Requesting camera permissions...`,
); );
@@ -175,7 +175,7 @@ export class WebInlineQRScanner implements QRScannerService {
(device) => device.kind === "videoinput", (device) => device.kind === "videoinput",
); );
logger.error(`[WebInlineQRScanner:${this.id}] Found video devices:`, { logger.debug(`[WebInlineQRScanner:${this.id}] Found video devices:`, {
count: videoDevices.length, count: videoDevices.length,
devices: videoDevices.map((d) => ({ id: d.deviceId, label: d.label })), devices: videoDevices.map((d) => ({ id: d.deviceId, label: d.label })),
userAgent: navigator.userAgent, userAgent: navigator.userAgent,
@@ -188,7 +188,7 @@ export class WebInlineQRScanner implements QRScannerService {
} }
// Try to get a stream with specific constraints // Try to get a stream with specific constraints
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Requesting camera stream with constraints:`, `[WebInlineQRScanner:${this.id}] Requesting camera stream with constraints:`,
{ {
facingMode: "environment", facingMode: "environment",
@@ -210,7 +210,7 @@ export class WebInlineQRScanner implements QRScannerService {
// Stop the test stream immediately // Stop the test stream immediately
stream.getTracks().forEach((track) => { stream.getTracks().forEach((track) => {
logger.error(`[WebInlineQRScanner:${this.id}] Stopping test track:`, { logger.debug(`[WebInlineQRScanner:${this.id}] Stopping test track:`, {
kind: track.kind, kind: track.kind,
label: track.label, label: track.label,
readyState: track.readyState, readyState: track.readyState,
@@ -275,12 +275,12 @@ export class WebInlineQRScanner implements QRScannerService {
async isSupported(): Promise<boolean> { async isSupported(): Promise<boolean> {
try { try {
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Checking browser support...`, `[WebInlineQRScanner:${this.id}] Checking browser support...`,
); );
// Check for secure context first // Check for secure context first
if (!window.isSecureContext) { if (!window.isSecureContext) {
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Camera access requires HTTPS (secure context)`, `[WebInlineQRScanner:${this.id}] Camera access requires HTTPS (secure context)`,
); );
return false; return false;
@@ -300,7 +300,7 @@ export class WebInlineQRScanner implements QRScannerService {
(device) => device.kind === "videoinput", (device) => device.kind === "videoinput",
); );
logger.error(`[WebInlineQRScanner:${this.id}] Device support check:`, { logger.debug(`[WebInlineQRScanner:${this.id}] Device support check:`, {
hasSecureContext: window.isSecureContext, hasSecureContext: window.isSecureContext,
hasMediaDevices: !!navigator.mediaDevices, hasMediaDevices: !!navigator.mediaDevices,
hasGetUserMedia: !!navigator.mediaDevices?.getUserMedia, hasGetUserMedia: !!navigator.mediaDevices?.getUserMedia,
@@ -379,7 +379,7 @@ export class WebInlineQRScanner implements QRScannerService {
// Log scan attempt every 100 frames or 1 second // Log scan attempt every 100 frames or 1 second
if (this.scanAttempts % 100 === 0 || timeSinceLastScan >= 1000) { if (this.scanAttempts % 100 === 0 || timeSinceLastScan >= 1000) {
logger.error(`[WebInlineQRScanner:${this.id}] Scanning frame:`, { logger.debug(`[WebInlineQRScanner:${this.id}] Scanning frame:`, {
attempt: this.scanAttempts, attempt: this.scanAttempts,
dimensions: { dimensions: {
width: this.canvas.width, width: this.canvas.width,
@@ -421,7 +421,7 @@ export class WebInlineQRScanner implements QRScannerService {
!code.data || !code.data ||
code.data.length === 0; code.data.length === 0;
logger.error(`[WebInlineQRScanner:${this.id}] QR Code detected:`, { logger.debug(`[WebInlineQRScanner:${this.id}] QR Code detected:`, {
data: code.data, data: code.data,
location: code.location, location: code.location,
attempts: this.scanAttempts, attempts: this.scanAttempts,
@@ -512,13 +512,13 @@ export class WebInlineQRScanner implements QRScannerService {
this.scanAttempts = 0; this.scanAttempts = 0;
this.lastScanTime = Date.now(); this.lastScanTime = Date.now();
this.updateCameraState("initializing", "Starting camera..."); this.updateCameraState("initializing", "Starting camera...");
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Starting scan with options:`, `[WebInlineQRScanner:${this.id}] Starting scan with options:`,
this.options, this.options,
); );
// Get camera stream with options // Get camera stream with options
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Requesting camera stream...`, `[WebInlineQRScanner:${this.id}] Requesting camera stream...`,
); );
this.stream = await navigator.mediaDevices.getUserMedia({ this.stream = await navigator.mediaDevices.getUserMedia({
@@ -531,7 +531,7 @@ export class WebInlineQRScanner implements QRScannerService {
this.updateCameraState("active", "Camera is active"); this.updateCameraState("active", "Camera is active");
logger.error(`[WebInlineQRScanner:${this.id}] Camera stream obtained:`, { logger.debug(`[WebInlineQRScanner:${this.id}] Camera stream obtained:`, {
tracks: this.stream.getTracks().map((t) => ({ tracks: this.stream.getTracks().map((t) => ({
kind: t.kind, kind: t.kind,
label: t.label, label: t.label,
@@ -550,14 +550,14 @@ export class WebInlineQRScanner implements QRScannerService {
this.video.style.display = "none"; this.video.style.display = "none";
} }
await this.video.play(); await this.video.play();
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Video element started playing`, `[WebInlineQRScanner:${this.id}] Video element started playing`,
); );
} }
// Emit stream to component // Emit stream to component
this.events.emit("stream", this.stream); this.events.emit("stream", this.stream);
logger.error(`[WebInlineQRScanner:${this.id}] Stream event emitted`); logger.debug(`[WebInlineQRScanner:${this.id}] Stream event emitted`);
// Start QR code scanning // Start QR code scanning
this.scanQRCode(); this.scanQRCode();
@@ -595,7 +595,7 @@ export class WebInlineQRScanner implements QRScannerService {
} }
try { try {
logger.error(`[WebInlineQRScanner:${this.id}] Stopping scan`, { logger.debug(`[WebInlineQRScanner:${this.id}] Stopping scan`, {
scanAttempts: this.scanAttempts, scanAttempts: this.scanAttempts,
duration: Date.now() - this.lastScanTime, duration: Date.now() - this.lastScanTime,
}); });
@@ -604,7 +604,7 @@ export class WebInlineQRScanner implements QRScannerService {
if (this.animationFrameId !== null) { if (this.animationFrameId !== null) {
cancelAnimationFrame(this.animationFrameId); cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null; this.animationFrameId = null;
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Animation frame cancelled`, `[WebInlineQRScanner:${this.id}] Animation frame cancelled`,
); );
} }
@@ -613,13 +613,13 @@ export class WebInlineQRScanner implements QRScannerService {
if (this.video) { if (this.video) {
this.video.pause(); this.video.pause();
this.video.srcObject = null; this.video.srcObject = null;
logger.error(`[WebInlineQRScanner:${this.id}] Video element stopped`); logger.debug(`[WebInlineQRScanner:${this.id}] Video element stopped`);
} }
// Stop all tracks in the stream // Stop all tracks in the stream
if (this.stream) { if (this.stream) {
this.stream.getTracks().forEach((track) => { this.stream.getTracks().forEach((track) => {
logger.error(`[WebInlineQRScanner:${this.id}] Stopping track:`, { logger.debug(`[WebInlineQRScanner:${this.id}] Stopping track:`, {
kind: track.kind, kind: track.kind,
label: track.label, label: track.label,
readyState: track.readyState, readyState: track.readyState,
@@ -631,7 +631,7 @@ export class WebInlineQRScanner implements QRScannerService {
// Emit stream stopped event // Emit stream stopped event
this.events.emit("stream", null); this.events.emit("stream", null);
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Stream stopped event emitted`, `[WebInlineQRScanner:${this.id}] Stream stopped event emitted`,
); );
} catch (error) { } catch (error) {
@@ -643,17 +643,17 @@ export class WebInlineQRScanner implements QRScannerService {
throw error; throw error;
} finally { } finally {
this.isScanning = false; this.isScanning = false;
logger.error(`[WebInlineQRScanner:${this.id}] Scan stopped successfully`); logger.debug(`[WebInlineQRScanner:${this.id}] Scan stopped successfully`);
} }
} }
addListener(listener: ScanListener): void { addListener(listener: ScanListener): void {
logger.error(`[WebInlineQRScanner:${this.id}] Adding scan listener`); logger.debug(`[WebInlineQRScanner:${this.id}] Adding scan listener`);
this.scanListener = listener; this.scanListener = listener;
} }
onStream(callback: (stream: MediaStream | null) => void): void { onStream(callback: (stream: MediaStream | null) => void): void {
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Adding stream event listener`, `[WebInlineQRScanner:${this.id}] Adding stream event listener`,
); );
this.events.on("stream", callback); this.events.on("stream", callback);
@@ -661,24 +661,24 @@ export class WebInlineQRScanner implements QRScannerService {
async cleanup(): Promise<void> { async cleanup(): Promise<void> {
try { try {
logger.error(`[WebInlineQRScanner:${this.id}] Starting cleanup`); logger.debug(`[WebInlineQRScanner:${this.id}] Starting cleanup`);
await this.stopScan(); await this.stopScan();
this.events.removeAllListeners(); this.events.removeAllListeners();
logger.error(`[WebInlineQRScanner:${this.id}] Event listeners removed`); logger.debug(`[WebInlineQRScanner:${this.id}] Event listeners removed`);
// Clean up DOM elements // Clean up DOM elements
if (this.video) { if (this.video) {
this.video.remove(); this.video.remove();
this.video = null; this.video = null;
logger.error(`[WebInlineQRScanner:${this.id}] Video element removed`); logger.debug(`[WebInlineQRScanner:${this.id}] Video element removed`);
} }
if (this.canvas) { if (this.canvas) {
this.canvas.remove(); this.canvas.remove();
this.canvas = null; this.canvas = null;
logger.error(`[WebInlineQRScanner:${this.id}] Canvas element removed`); logger.debug(`[WebInlineQRScanner:${this.id}] Canvas element removed`);
} }
this.context = null; this.context = null;
logger.error( logger.debug(
`[WebInlineQRScanner:${this.id}] Cleanup completed successfully`, `[WebInlineQRScanner:${this.id}] Cleanup completed successfully`,
); );
} catch (error) { } catch (error) {

View File

@@ -0,0 +1,90 @@
import { NotificationIface } from "@/constants/app";
const SEED_REMINDER_KEY = "seedPhraseReminderLastShown";
const REMINDER_COOLDOWN_MS = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
/**
* Checks if the seed phrase backup reminder should be shown
* @param hasBackedUpSeed - Whether the user has backed up their seed phrase
* @returns true if the reminder should be shown, false otherwise
*/
export function shouldShowSeedReminder(hasBackedUpSeed: boolean): boolean {
// Don't show if user has already backed up
if (hasBackedUpSeed) {
return false;
}
// Check localStorage for last shown time
const lastShown = localStorage.getItem(SEED_REMINDER_KEY);
if (!lastShown) {
return true; // First time, show the reminder
}
try {
const lastShownTime = parseInt(lastShown, 10);
const now = Date.now();
const timeSinceLastShown = now - lastShownTime;
// Show if more than 24 hours have passed
return timeSinceLastShown >= REMINDER_COOLDOWN_MS;
} catch (error) {
// If there's an error parsing the timestamp, show the reminder
return true;
}
}
/**
* Marks the seed phrase reminder as shown by updating localStorage
*/
export function markSeedReminderShown(): void {
localStorage.setItem(SEED_REMINDER_KEY, Date.now().toString());
}
/**
* Creates the seed phrase backup reminder notification
* @returns NotificationIface configuration for the reminder modal
*/
export function createSeedReminderNotification(): NotificationIface {
return {
group: "modal",
type: "confirm",
title: "Backup Your Identifier Seed?",
text: "It looks like you haven't backed up your identifier seed yet. It's important to back it up as soon as possible to secure your identity.",
yesText: "Backup Identifier Seed",
noText: "Remind me Later",
onYes: async () => {
// Navigate to seed backup page
window.location.href = "/seed-backup";
},
onNo: async () => {
// Mark as shown so it won't appear again for 24 hours
markSeedReminderShown();
},
onCancel: async () => {
// Mark as shown so it won't appear again for 24 hours
markSeedReminderShown();
},
};
}
/**
* Shows the seed phrase backup reminder if conditions are met
* @param hasBackedUpSeed - Whether the user has backed up their seed phrase
* @param notifyFunction - Function to show notifications
* @returns true if the reminder was shown, false otherwise
*/
export function showSeedPhraseReminder(
hasBackedUpSeed: boolean,
notifyFunction: (notification: NotificationIface, timeout?: number) => void,
): boolean {
if (shouldShowSeedReminder(hasBackedUpSeed)) {
const notification = createSeedReminderNotification();
// Add 1-second delay before showing the modal to allow success message to be visible
setTimeout(() => {
// Pass -1 as timeout to ensure modal stays open until user interaction
notifyFunction(notification, -1);
}, 1000);
return true;
}
return false;
}

View File

@@ -811,6 +811,7 @@ 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 { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView"; import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView";
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
import { import {
AccountSettings, AccountSettings,
isApiError, isApiError,
@@ -1695,6 +1696,14 @@ export default class AccountViewView extends Vue {
); );
if (success) { if (success) {
this.notify.success(ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_SAVED); this.notify.success(ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_SAVED);
// Show seed phrase backup reminder if needed
try {
const settings = await this.$accountSettings();
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
} catch (error) {
logger.error("Error checking seed backup status:", error);
}
} else { } else {
this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_SAVE_ERROR); this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_SAVE_ERROR);
} }

View File

@@ -41,6 +41,7 @@ import { Router, RouteLocationNormalizedLoaded } from "vue-router";
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 { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
// Type guard for API responses // Type guard for API responses
function isApiResponse(response: unknown): response is AxiosResponse { function isApiResponse(response: unknown): response is AxiosResponse {
@@ -223,6 +224,14 @@ export default class ClaimAddRawView extends Vue {
); );
if (result.success) { if (result.success) {
this.notify.success("Claim submitted.", TIMEOUTS.LONG); this.notify.success("Claim submitted.", TIMEOUTS.LONG);
// Show seed phrase backup reminder if needed
try {
const settings = await this.$accountSettings();
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
} catch (error) {
logger.error("Error checking seed backup status:", error);
}
} else { } else {
logger.error("Got error submitting the claim:", result); logger.error("Got error submitting the claim:", result);
this.notify.error( this.notify.error(

View File

@@ -24,7 +24,9 @@
<div class="flex columns-3"> <div class="flex columns-3">
<h2 class="text-md font-bold w-full"> <h2 class="text-md font-bold w-full">
{{ {{
capitalizeAndInsertSpacesBeforeCaps(veriClaim.claimType || "") serverUtil.capitalizeAndInsertSpacesBeforeCaps(
veriClaim.claimType || "",
)
}} }}
<button <button
v-if="canEditClaim" v-if="canEditClaim"
@@ -106,9 +108,9 @@
</div> </div>
<!-- Fullfills Links --> <!-- Fullfills Links -->
<div class="mt-4 empty:hidden">
<!-- fullfills links for a give --> <!-- fullfills links for a give -->
<div v-if="detailsForGive?.fulfillsPlanHandleId" class="mt-4"> <div v-if="detailsForGive?.fulfillsPlanHandleId">
<router-link <router-link
:to=" :to="
'/project/' + '/project/' +
@@ -116,30 +118,35 @@
" "
class="text-blue-500 mt-2" class="text-blue-500 mt-2"
> >
Fulfills a bigger plan... This fulfills a bigger plan
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</router-link> </router-link>
</div> </div>
<!-- if there's another, it's probably fulfilling an offer, too -->
<div <!-- Show offer fulfillment if this give fulfills an offer -->
v-if=" <div v-if="detailsForGiveOfferFulfillment?.offerHandleId">
detailsForGive?.fulfillsType &&
detailsForGive?.fulfillsType !== 'PlanAction' &&
detailsForGive?.fulfillsHandleId
"
>
<!-- router-link to /claim/ only changes URL path --> <!-- router-link to /claim/ only changes URL path -->
<a <a
class="text-blue-500 mt-4 cursor-pointer" class="text-blue-500 mt-4 cursor-pointer"
@click=" @click="
showDifferentClaimPage(detailsForGive?.fulfillsHandleId) showDifferentClaimPage(
detailsForGiveOfferFulfillment.offerHandleId,
)
" "
> >
Fulfills This fulfills
{{ {{
capitalizeAndInsertSpacesBeforeCaps( serverUtil.capitalizeAndInsertSpacesBeforeCapsWithAPrefix(
detailsForGive.fulfillsType, detailsForGiveOfferFulfillment.offerType || "Offer",
) )
}}... }}
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</a> </a>
</div> </div>
@@ -152,12 +159,16 @@
" "
class="text-blue-500 mt-4" class="text-blue-500 mt-4"
> >
Offered to a bigger plan... Offered to a bigger plan
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</router-link> </router-link>
</div> </div>
<!-- Providers --> <!-- Providers -->
<div v-if="providersForGive?.length > 0" class="mt-4"> <div v-if="providersForGive?.length > 0">
<span>Other assistance provided by:</span> <span>Other assistance provided by:</span>
<ul class="ml-4"> <ul class="ml-4">
<li <li
@@ -171,7 +182,11 @@
class="text-blue-500 mt-4 cursor-pointer" class="text-blue-500 mt-4 cursor-pointer"
@click="handleProviderClick(provider)" @click="handleProviderClick(provider)"
> >
an activity... an activity
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</a> </a>
</div> </div>
</div> </div>
@@ -182,6 +197,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="mt-2"> <div class="mt-2">
<font-awesome icon="comment" class="text-slate-400" /> <font-awesome icon="comment" class="text-slate-400" />
{{ issuerName }} posted that. {{ issuerName }} posted that.
@@ -556,6 +572,17 @@ export default class ClaimView extends Vue {
fulfillsPlanHandleId?: string; fulfillsPlanHandleId?: string;
fulfillsType?: string; fulfillsType?: string;
fulfillsHandleId?: string; fulfillsHandleId?: string;
fullClaim?: {
fulfills?: Array<{
"@type": string;
identifier?: string;
}>;
};
} | null = null;
// Additional offer information extracted from the fulfills array
detailsForGiveOfferFulfillment: {
offerHandleId?: string;
offerType?: string;
} | null = null; } | null = null;
detailsForOffer: { fulfillsPlanHandleId?: string } | null = null; detailsForOffer: { fulfillsPlanHandleId?: string } | null = null;
// Project information for fulfillsPlanHandleId // Project information for fulfillsPlanHandleId
@@ -689,6 +716,7 @@ export default class ClaimView extends Vue {
this.confsVisibleToIdList = []; this.confsVisibleToIdList = [];
this.detailsForGive = null; this.detailsForGive = null;
this.detailsForOffer = null; this.detailsForOffer = null;
this.detailsForGiveOfferFulfillment = null;
this.projectInfo = null; this.projectInfo = null;
this.fullClaim = null; this.fullClaim = null;
this.fullClaimDump = ""; this.fullClaimDump = "";
@@ -701,6 +729,15 @@ export default class ClaimView extends Vue {
this.veriClaimDidsVisible = {}; this.veriClaimDidsVisible = {};
} }
/**
* Extract offer fulfillment information from the fulfills array
*/
extractOfferFulfillment() {
this.detailsForGiveOfferFulfillment = libsUtil.extractOfferFulfillment(
this.detailsForGive?.fullClaim?.fulfills
);
}
// ================================================= // =================================================
// UTILITY METHODS // UTILITY METHODS
// ================================================= // =================================================
@@ -758,13 +795,6 @@ export default class ClaimView extends Vue {
this.canShare = !!navigator.share; this.canShare = !!navigator.share;
} }
// insert a space before any capital letters except the initial letter
// (and capitalize initial letter, just in case)
capitalizeAndInsertSpacesBeforeCaps(text: string): string {
if (!text) return "";
return text[0].toUpperCase() + text.substr(1).replace(/([A-Z])/g, " $1");
}
totalConfirmers() { totalConfirmers() {
return ( return (
this.numConfsNotVisible + this.numConfsNotVisible +
@@ -821,6 +851,8 @@ export default class ClaimView extends Vue {
}); });
if (giveResp.status === 200 && giveResp.data.data?.length > 0) { if (giveResp.status === 200 && giveResp.data.data?.length > 0) {
this.detailsForGive = giveResp.data.data[0]; this.detailsForGive = giveResp.data.data[0];
// Extract offer information from the fulfills array
this.extractOfferFulfillment();
} else { } else {
await this.$logError( await this.$logError(
"Error getting detailed give info: " + JSON.stringify(giveResp), "Error getting detailed give info: " + JSON.stringify(giveResp),

View File

@@ -96,13 +96,15 @@
</div> </div>
<!-- Fullfills Links --> <!-- Fullfills Links -->
<div class="mt-4">
<!-- fullfills links for a give --> <!-- fullfills links for a give -->
<div v-if="giveDetails?.fulfillsPlanHandleId" class="mt-2"> <div v-if="giveDetails?.fulfillsPlanHandleId">
<router-link <router-link
:to=" :to="
'/project/' + '/project/' +
encodeURIComponent(giveDetails?.fulfillsPlanHandleId || '') encodeURIComponent(
giveDetails?.fulfillsPlanHandleId || '',
)
" "
class="text-blue-500 mt-2 cursor-pointer" class="text-blue-500 mt-2 cursor-pointer"
> >
@@ -113,26 +115,23 @@
/> />
</router-link> </router-link>
</div> </div>
<!-- if there's another, it's probably fulfilling an offer, too -->
<div <!-- Show offer fulfillment if this give fulfills an offer -->
v-if=" <div v-if="giveDetailsOfferFulfillment?.offerHandleId">
giveDetails?.fulfillsType &&
giveDetails?.fulfillsType !== 'PlanAction' &&
giveDetails?.fulfillsHandleId
"
>
<!-- router-link to /claim/ only changes URL path --> <!-- router-link to /claim/ only changes URL path -->
<router-link <router-link
:to=" :to="
'/claim/' + '/claim/' +
encodeURIComponent(giveDetails?.fulfillsHandleId || '') encodeURIComponent(
giveDetailsOfferFulfillment.offerHandleId || '',
)
" "
class="text-blue-500 mt-2 cursor-pointer" class="text-blue-500 mt-2 cursor-pointer"
> >
This fulfills This fulfills
{{ {{
capitalizeAndInsertSpacesBeforeCapsWithAPrefix( serverUtil.capitalizeAndInsertSpacesBeforeCapsWithAPrefix(
giveDetails?.fulfillsType || "", giveDetailsOfferFulfillment.offerType || "Offer",
) )
}} }}
<font-awesome <font-awesome
@@ -145,6 +144,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="mt-2"> <div class="mt-2">
<font-awesome icon="comment" class="text-slate-400" /> <font-awesome icon="comment" class="text-slate-400" />
{{ issuerName }} posted that. {{ issuerName }} posted that.
@@ -493,6 +493,11 @@ export default class ConfirmGiftView extends Vue {
confsVisibleErrorMessage = ""; confsVisibleErrorMessage = "";
confsVisibleToIdList: string[] = []; // list of DIDs that can see any confirmer confsVisibleToIdList: string[] = []; // list of DIDs that can see any confirmer
giveDetails?: GiveSummaryRecord; giveDetails?: GiveSummaryRecord;
// Additional offer information extracted from the fulfills array
giveDetailsOfferFulfillment: {
offerHandleId?: string;
offerType?: string;
} | null = null;
giverName = ""; giverName = "";
issuerName = ""; issuerName = "";
isLoading = false; isLoading = false;
@@ -648,6 +653,8 @@ export default class ConfirmGiftView extends Vue {
if (resp.status === 200) { if (resp.status === 200) {
this.giveDetails = resp.data.data[0]; this.giveDetails = resp.data.data[0];
// Extract offer information from the fulfills array
this.extractOfferFulfillment();
} else { } else {
throw new Error("Error getting detailed give info: " + resp.status); throw new Error("Error getting detailed give info: " + resp.status);
} }
@@ -707,6 +714,15 @@ export default class ConfirmGiftView extends Vue {
} }
} }
/**
* Extract offer fulfillment information from the fulfills array
*/
private extractOfferFulfillment() {
this.giveDetailsOfferFulfillment = libsUtil.extractOfferFulfillment(
this.giveDetails?.fullClaim?.fulfills
);
}
/** /**
* Fetches confirmer information for the claim * Fetches confirmer information for the claim
*/ */
@@ -849,27 +865,6 @@ export default class ConfirmGiftView extends Vue {
); );
} }
/**
* Formats type string for display by adding spaces before capitals
* Optionally adds a prefix
*
* @param text - Text to format
* @param prefix - Optional prefix to add
* @returns Formatted string
*/
capitalizeAndInsertSpacesBeforeCapsWithAPrefix(text: string): string {
const word = this.capitalizeAndInsertSpacesBeforeCaps(text);
if (word) {
// if the word starts with a vowel, use "an" instead of "a"
const firstLetter = word[0].toLowerCase();
const vowels = ["a", "e", "i", "o", "u"];
const particle = vowels.includes(firstLetter) ? "an" : "a";
return particle + " " + word;
} else {
return "";
}
}
/** /**
* Initiates sharing of claim information * Initiates sharing of claim information
* Handles share functionality based on platform capabilities * Handles share functionality based on platform capabilities
@@ -894,11 +889,5 @@ export default class ConfirmGiftView extends Vue {
this.veriClaim = serverUtil.BLANK_GENERIC_SERVER_RECORD; this.veriClaim = serverUtil.BLANK_GENERIC_SERVER_RECORD;
this.veriClaimDump = ""; this.veriClaimDump = "";
} }
capitalizeAndInsertSpacesBeforeCaps(text: string) {
return !text
? ""
: text[0].toUpperCase() + text.substr(1).replace(/([A-Z])/g, " $1");
}
} }
</script> </script>

View File

@@ -144,6 +144,7 @@ import {
QR_TIMEOUT_LONG, QR_TIMEOUT_LONG,
} from "@/constants/notifications"; } from "@/constants/notifications";
import { createNotifyHelpers, NotifyFunction } from "../utils/notify"; import { createNotifyHelpers, NotifyFunction } from "../utils/notify";
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
interface QRScanResult { interface QRScanResult {
rawValue?: string; rawValue?: string;
@@ -622,6 +623,15 @@ export default class ContactQRScanFull extends Vue {
*/ */
async handleBack() { async handleBack() {
await this.cleanupScanner(); await this.cleanupScanner();
// Show seed phrase backup reminder if needed
try {
const settings = await this.$accountSettings();
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
} catch (error) {
logger.error("Error checking seed backup status:", error);
}
this.$router.back(); this.$router.back();
} }

View File

@@ -163,6 +163,7 @@ import { QRScannerFactory } from "@/services/QRScanner/QRScannerFactory";
import { CameraState } from "@/services/QRScanner/types"; import { CameraState } from "@/services/QRScanner/types";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers } from "@/utils/notify"; import { createNotifyHelpers } from "@/utils/notify";
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
import { import {
NOTIFY_QR_INITIALIZATION_ERROR, NOTIFY_QR_INITIALIZATION_ERROR,
NOTIFY_QR_CAMERA_IN_USE, NOTIFY_QR_CAMERA_IN_USE,
@@ -319,6 +320,15 @@ export default class ContactQRScanShow extends Vue {
async handleBack(): Promise<void> { async handleBack(): Promise<void> {
await this.cleanupScanner(); await this.cleanupScanner();
// Show seed phrase backup reminder if needed
try {
const settings = await this.$accountSettings();
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
} catch (error) {
logger.error("Error checking seed backup status:", error);
}
this.$router.back(); this.$router.back();
} }
@@ -738,24 +748,17 @@ export default class ContactQRScanShow extends Vue {
!contact.registered !contact.registered
) { ) {
setTimeout(() => { setTimeout(() => {
this.notify.confirm( this.$notify(
"Do you want to register them?",
{ {
group: "modal",
type: "confirm",
title: "Register",
text: "Do you want to register them?",
onCancel: async (stopAsking?: boolean) => { onCancel: async (stopAsking?: boolean) => {
if (stopAsking) { await this.handleRegistrationPromptResponse(stopAsking);
await this.$updateSettings({
hideRegisterPromptOnNewContact: stopAsking,
});
this.hideRegisterPromptOnNewContact = stopAsking;
}
}, },
onNo: async (stopAsking?: boolean) => { onNo: async (stopAsking?: boolean) => {
if (stopAsking) { await this.handleRegistrationPromptResponse(stopAsking);
await this.$updateSettings({
hideRegisterPromptOnNewContact: stopAsking,
});
this.hideRegisterPromptOnNewContact = stopAsking;
}
}, },
onYes: async () => { onYes: async () => {
await this.register(contact); await this.register(contact);
@@ -885,6 +888,17 @@ export default class ContactQRScanShow extends Vue {
videoElement.style.transform = shouldMirror ? "scaleX(-1)" : "none"; videoElement.style.transform = shouldMirror ? "scaleX(-1)" : "none";
} }
} }
private async handleRegistrationPromptResponse(
stopAsking?: boolean,
): Promise<void> {
if (stopAsking) {
await this.$saveSettings({
hideRegisterPromptOnNewContact: stopAsking,
});
this.hideRegisterPromptOnNewContact = stopAsking;
}
}
} }
</script> </script>

View File

@@ -280,6 +280,7 @@ import { logger } from "../utils/logger";
import { Contact } from "@/db/tables/contacts"; import { Contact } from "@/db/tables/contacts";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
import { import {
NOTIFY_GIFTED_DETAILS_RETRIEVAL_ERROR, NOTIFY_GIFTED_DETAILS_RETRIEVAL_ERROR,
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM, NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM,
@@ -770,6 +771,15 @@ export default class GiftedDetails extends Vue {
NOTIFY_GIFTED_DETAILS_GIFT_RECORDED.message, NOTIFY_GIFTED_DETAILS_GIFT_RECORDED.message,
TIMEOUTS.SHORT, TIMEOUTS.SHORT,
); );
// Show seed phrase backup reminder if needed
try {
const settings = await this.$accountSettings();
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
} catch (error) {
logger.error("Error checking seed backup status:", error);
}
localStorage.removeItem("imageUrl"); localStorage.removeItem("imageUrl");
if (this.destinationPathAfter) { if (this.destinationPathAfter) {
(this.$router as Router).push({ path: this.destinationPathAfter }); (this.$router as Router).push({ path: this.destinationPathAfter });

View File

@@ -231,9 +231,24 @@ export default class SeedBackupView extends Vue {
/** /**
* Reveals the seed phrase to the user * Reveals the seed phrase to the user
* Sets showSeed to true to display the sensitive seed phrase data * Sets showSeed to true to display the sensitive seed phrase data
* Updates the hasBackedUpSeed setting to true to track that user has backed up
*/ */
revealSeed(): void { async revealSeed(): Promise<void> {
this.showSeed = true; this.showSeed = true;
// Update the account setting to track that user has backed up their seed
try {
const settings = await this.$accountSettings();
if (settings.activeDid) {
await this.$saveUserSettings(settings.activeDid, {
hasBackedUpSeed: true,
});
}
} catch (err: unknown) {
logger.error("Failed to update hasBackedUpSeed setting:", err);
// Don't show error to user as this is not critical to the main functionality
// The seed phrase is still revealed, just the tracking won't work
}
} }
/** /**