The frontend component of PuffPastry, meant to interface with the backend, which in turn interfaces with the smart contract.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. ---
  2. sessionId: session-261005-211542-1soe
  3. ---
  4. # Requirements
  5. ### Overview & Goals
  6. To maximize application utility and deliver an optimal user experience, users should not have to manually reconnect their Freighter wallet or re-authenticate after every browser refresh. The objective is to enhance `src/composables/useSession.ts` to manage and persist wallet connection data and authentication tokens across page reloads, making session state globally available and reactive across all UI components.
  7. ### Scope
  8. - **In Scope**:
  9. - Expanding `src/composables/useSession.ts` into a reactive session composable managing wallet address, user ID, auth token, and connection lifecycle.
  10. - Hydrating wallet and authentication state from `localStorage` on initial application load and browser refresh.
  11. - Providing centralized methods in `useSession.ts` for connecting, disconnecting, checking Freighter status, and formatting wallet displays.
  12. - Refactoring `src/components/BrandBar.vue` and `src/App.vue` to leverage `useSession()` for unified session state management.
  13. - Ensuring outgoing API requests via OpenAPI client continue to resolve persisted tokens seamlessly.
  14. - **Out of Scope**:
  15. - Modifying backend authentication endpoints or token validation mechanisms.
  16. - Modifying Freighter browser extension APIs or Stellar SDK internal logic.
  17. ### User Stories
  18. - As a platform user, I want my wallet connection and authentication status to persist when I refresh the page so that I do not need to repeatedly click "Connect Wallet" and approve popups.
  19. - As a user, I want the header brand bar to immediately reflect my connected wallet address on page load so that I know my active session is valid.
  20. - As a developer, I want a single source of truth for wallet session state in `useSession.ts` so that components across the application can reliably access authentication details without duplicate storage or Freighter queries.
  21. ### Functional Requirements
  22. - **Automatic State Hydration**: On application mount or composable initialization, `useSession` checks `localStorage` for existing `authToken` and cached wallet address, verifies Freighter extension availability, and synchronizes reactive state.
  23. - **Unified Connect Flow**: `connectWallet()` requests Freighter permission (`setAllowed()`), retrieves the public key (`getAddress()`), logs in via `AuthService.loginFreighter`, stores the token in `localStorage`, and updates reactive state.
  24. - **Clean Disconnect Flow**: `disconnectWallet()` removes authentication and wallet data from `localStorage` and resets reactive variables to unauthenticated state.
  25. - **Reactive UI Synchronization**: `BrandBar.vue` dynamically displays either "Connect Wallet" or the truncated address based on `useSession` reactive state.
  26. ### Non-Functional Requirements
  27. - **Resilience & Fault Tolerance**: If storage is corrupted, inaccessible, or the JWT is invalid/expired, `useSession` safely resets state without throwing unhandled exceptions.
  28. - **Performance**: Hydration must occur synchronously/microtask-level during initial render so UI does not flicker between unauthenticated and authenticated states.
  29. # Technical Design
  30. ### Current Implementation
  31. - `src/composables/useSession.ts` is currently a minimal stub containing only `hasFreighter = async () => freighter.isConnected()`.
  32. - `src/components/BrandBar.vue` independently executes Freighter login, sets `localStorage.setItem("authToken", ...)`, and updates a local component ref `userText`. When the user refreshes, this component state is lost and resets to `'Connect Wallet'`.
  33. - `src/App.vue` calls `freighter.getAddress()` in `onBeforeMount` and stores it in an isolated local ref `address`.
  34. - `src/utils/auth.ts` provides `getAuthToken()`, `parseJwt()`, and `getUserIdFromJwt()`, retrieving tokens from `localStorage`.
  35. ### Key Decisions
  36. - **Shared Module-Level Reactive State in `useSession.ts`**:
  37. - *Decision*: Maintain `walletAddress`, `authToken`, `userId`, `isConnecting`, and `isInitialized` as module-level `ref`s within `useSession.ts` (consistent with patterns used in `useModal.ts` and `useLoading.ts`).
  38. - *Rationale*: Guarantees all consumer components share the exact same reactive session state without requiring redundant Pinia store overhead or desynchronized state copies.
  39. - **Storage Strategy**:
  40. - *Decision*: Persist `authToken` under the standard `'authToken'` key in `localStorage` and derive/cache `stellarAddress` and `userId` directly from JWT payload parsing (`parseJwt`) and Freighter sync.
  41. - *Rationale*: Keeps `localStorage` minimal, avoids stale divergent data, and maintains full compatibility with `src/main.ts` (`OpenAPI.TOKEN`) and `src/utils/auth.ts`.
  42. - **Eager Hydration & Verification**:
  43. - *Decision*: Restore state immediately from `localStorage` on initial access, followed by asynchronous verification with Freighter (`freighter.isAllowed()`).
  44. - *Rationale*: Eliminates UI layout shifts or "flash of unauthenticated content" while still ensuring the browser extension remains connected.
  45. ### Proposed Changes
  46. 1. **`src/composables/useSession.ts`**:
  47. - Add reactive state: `walletAddress: ref<string | null>`, `authToken: ref<string | null>`, `userId: ref<number | undefined>`, `isConnecting: ref<boolean>`.
  48. - Add computed helpers: `isAuthenticated: computed(() => !!authToken.value && !!walletAddress.value)`, `truncatedAddress: computed(() => walletAddress.value ? truncateWallet(walletAddress.value) : '')`.
  49. - Implement `initSession()` to restore token from `localStorage`, decode JWT for `stellarAddress` / `userId`, and verify with Freighter if available.
  50. - Implement `connectWallet()` and `disconnectWallet()`.
  51. - Implement `hasFreighter()` to check extension availability.
  52. 2. **`src/components/BrandBar.vue`**:
  53. - Replace local `userText` logic with `const { isAuthenticated, truncatedAddress, isConnecting, connectWallet, disconnectWallet } = useSession()`.
  54. - Compute button text: show `truncatedAddress` if authenticated, `"Connecting..."` if connecting, or `"Connect Wallet"`.
  55. 3. **`src/App.vue`**:
  56. - Initialize session via `initSession()` in `onBeforeMount` / `onMounted`.
  57. - Remove isolated, unused `address` ref.
  58. ### Data Models / Contracts
  59. ```typescript
  60. export interface SessionState {
  61. walletAddress: string | null;
  62. authToken: string | null;
  63. userId: number | undefined;
  64. isAuthenticated: boolean;
  65. isConnecting: boolean;
  66. hasFreighter: () => Promise<boolean>;
  67. connectWallet: () => Promise<boolean>;
  68. disconnectWallet: () => void;
  69. initSession: () => Promise<void>;
  70. truncatedAddress: string;
  71. }
  72. ```
  73. ### Components
  74. - `src/composables/useSession.ts`: Core session state, persistence, and wallet connection management.
  75. - `src/components/BrandBar.vue`: Primary consumer for wallet connect button and truncated address display.
  76. - `src/App.vue`: Top-level application shell invoking session initialization during application bootstrap.
  77. - `src/utils/auth.ts`: Helper utilities utilized by `useSession.ts` for token resolution and JWT decoding.
  78. ### File Structure
  79. - `src/composables/useSession.ts` (Modified: add full reactive session management and local persistence)
  80. - `src/components/BrandBar.vue` (Modified: wire to `useSession`)
  81. - `src/App.vue` (Modified: initialize session on mount and clean up duplicate state)
  82. ### Architecture Diagram
  83. ```mermaid
  84. graph LR
  85. subgraph UI Layer
  86. BB[BrandBar.vue]
  87. APP[App.vue]
  88. MOD[Proposal/Amendment Modals]
  89. end
  90. subgraph State & Session
  91. US[useSession.ts Composable]
  92. AUTH[src/utils/auth.ts]
  93. end
  94. subgraph External & Storage
  95. LS[(localStorage)]
  96. FR[Freighter Wallet Extension]
  97. API[AuthService / Backend]
  98. end
  99. APP -->|initSession| US
  100. BB -->|connect / display| US
  101. MOD -->|read wallet / auth| US
  102. US -->|read / write token| LS
  103. US -->|parse JWT| AUTH
  104. US -->|getAddress / setAllowed| FR
  105. US -->|loginFreighter| API
  106. ```
  107. ### Risks
  108. - **Freighter Extension Locked / Permission Denied**: If a user locks their Freighter wallet or declines permission, `useSession` will catch the error, log a warning, and keep or revert the state cleanly without crashing the UI.
  109. - **Corrupted Token in Storage**: If `localStorage` contains an invalid JWT string, `parseJwt()` returns `null`, prompting `useSession` to clear the bad token and reset state.
  110. # Testing
  111. ### Validation Approach
  112. Verify that the user's wallet connection and JWT authentication remain intact and reactive across page refreshes, and that connection/disconnection lifecycles function properly without regressions in API request authorization.
  113. ### Key Scenarios
  114. 1. **Wallet Connection & Persistence Across Page Reload**:
  115. - Click "Connect Wallet" in `BrandBar.vue` and approve connection.
  116. - Verify that the button updates to the truncated wallet address and `authToken` is saved in `localStorage`.
  117. - Refresh the browser (F5 / full reload).
  118. - Verify that on page load, `BrandBar.vue` immediately displays the truncated wallet address without requiring the user to click "Connect Wallet" again.
  119. 2. **Authenticated API Calls After Reload**:
  120. - With an existing persisted session, refresh the browser and navigate to proposal details or submit a comment/amendment.
  121. - Verify that outgoing requests include the `Authorization: Bearer <token>` header without authentication failure.
  122. 3. **Session Disconnection / Logout**:
  123. - Trigger disconnect (or simulate logout).
  124. - Verify that `localStorage` items are cleared, reactive state resets, and `BrandBar.vue` updates to "Connect Wallet".
  125. - Refresh the page and confirm the unauthenticated state remains.
  126. ### Edge Cases
  127. - **Page Load with No Token**: Initial load with empty `localStorage` renders "Connect Wallet" cleanly with no errors.
  128. - **Freighter Extension Not Installed**: `hasFreighter()` returns `false` and clicking connect displays a descriptive message rather than throwing unhandled rejections.
  129. - **Expired / Malformed Token**: Corrupted token in `localStorage` is safely purged during hydration.
  130. # Delivery Steps
  131. ### ✓ Step 1: Implement reactive wallet session state and persistence in useSession.ts
  132. `useSession.ts` provides a centralized reactive store for wallet address, authentication token, user ID, and connection status, with automatic local persistence and hydration.
  133. - Define reactive module-level state variables (`walletAddress`, `authToken`, `userId`, `isConnecting`, `isInitialized`) to maintain shared session state across all consumer components.
  134. - Implement `initSession()` to restore saved credentials (`authToken` and `walletAddress`) from `localStorage`, validate with `parseJwt()`, and verify Freighter extension availability via `freighter.isConnected()` and `freighter.isAllowed()`.
  135. - Implement `connectWallet()` to orchestrate permission request via `freighter.setAllowed()`, fetch public address via `freighter.getAddress()`, execute `AuthService.loginFreighter`, store `authToken` and `walletAddress` in `localStorage`, and update reactive session state.
  136. - Implement `disconnectWallet()` to clear stored tokens and wallet data from `localStorage` and reset reactive state.
  137. - Expose helper computed properties and functions including `isAuthenticated`, `truncatedAddress`, `hasFreighter`, and connection status flags.
  138. ### ✓ Step 2: Integrate useSession into BrandBar.vue and App.vue
  139. `BrandBar.vue` and `App.vue` consume `useSession.ts`, automatically restoring and displaying the active wallet address upon reload without user intervention.
  140. - Update `App.vue` on-mount lifecycle to invoke `initSession()` from `useSession`, ensuring wallet and auth state are restored immediately during startup.
  141. - Refactor `BrandBar.vue` to bind wallet button text and connection trigger directly to `useSession`'s `isAuthenticated`, `truncatedAddress`, `isConnecting`, and `connectWallet` / `disconnectWallet`.
  142. - Remove redundant direct Freighter API invocations in `BrandBar.vue` and `App.vue` in favor of centralized composable methods.
  143. - Ensure smooth fallback UI handling when Freighter extension is not installed or permissions are revoked.
  144. ### ✓ Step 3: Validate wallet persistence across page refreshes and edge cases
  145. The complete wallet persistence flow is verified across reloads, active voting/proposal actions, and logout scenarios.
  146. - Test connecting a Freighter wallet and refreshing the browser: confirm that wallet address, truncated display text, and JWT bearer token are immediately retained.
  147. - Verify that authenticated API requests dispatched through generated service clients (`CommentService`, `ProposalService`, `AmendmentService`) continue to attach the bearer token after page reload.
  148. - Test edge cases such as corrupted `localStorage` tokens, expired JWTs, Freighter wallet disconnection, and missing browser extensions to ensure graceful fallback without runtime errors.