Skip to content

refactor(mobile): 清償技術債(手勢/API重試/元件拆分/ErrorBoundary)+ 修正合圖姓名殘留與 Andro…#78

Merged
Retsomm merged 4 commits into
mainfrom
dev
Jul 22, 2026
Merged

refactor(mobile): 清償技術債(手勢/API重試/元件拆分/ErrorBoundary)+ 修正合圖姓名殘留與 Andro…#78
Retsomm merged 4 commits into
mainfrom
dev

Conversation

@Retsomm

@Retsomm Retsomm commented Jul 21, 2026

Copy link
Copy Markdown
Owner

…id 鍵盤晃動

技術債清償:

  • BirthProfilePickerModal 改用手足結構取代 Pressable 包 ScrollView,修正 iOS 捲動手勢被搶走的問題
  • lib/api.ts:合圖舊格式判斷整併成共用 helper(isCompositeChart/isLegacyPipeComposite), 依 endpoint 調整 timeout、唯讀/idempotent 端點加輕量重試
  • chart/[id].tsx 拆成 lib/useChartDetail.ts(資料 hook)+ components/chart/PersonalChartDetails.tsx (個人圖顯示區塊),723 行降到 443 行
  • chart/[id].tsx、chart/preview.tsx 加上區域性 ErrorBoundary
  • mobile/.gitignore 加入 bugreport-*.zip 規則

bug 修正:

  • CompositeView/TransitView/create.tsx:套用出生資料時姓名殘留舊人物的問題
  • ChartListView:長按選單 Alert 因 message 為 undefined 在 Android 顯示空白
  • profile.tsx:Clerk user.update()/setProfileImage() 加 session 刷新; 加 useFocusEffect(user.reload()) 解決跨裝置改名不同步(含修正一版曾造成無限迴圈的 deps 錯誤)
  • InputModal:Android 上 KeyboardAvoidingView behavior='height' 與系統 resize 重複補償 造成鍵盤跳出時晃動,改為 undefined(已實機確認解決)

Summary by CodeRabbit

  • New Features

    • Added richer personal chart details, including centers, channels, gates, planetary information, incarnation crosses, and variables.
    • Improved chart previews and details with automatic composite/transit results and clearer error recovery.
    • Integration channel information is now generated dynamically for greater accuracy.
  • Bug Fixes

    • Improved profile refresh, avatar updates, and profile-name changes.
    • Fixed modal tap-dismiss gestures and Android keyboard input behavior.
    • Applying a birth profile now consistently updates the chart name.
    • Improved network reliability with timeouts and safe retries.
  • Documentation

    • Updated Human Design reference text and legend colors for consistency.

…id 鍵盤晃動

技術債清償:
- BirthProfilePickerModal 改用手足結構取代 Pressable 包 ScrollView,修正 iOS 捲動手勢被搶走的問題
- lib/api.ts:合圖舊格式判斷整併成共用 helper(isCompositeChart/isLegacyPipeComposite),
  依 endpoint 調整 timeout、唯讀/idempotent 端點加輕量重試
- chart/[id].tsx 拆成 lib/useChartDetail.ts(資料 hook)+ components/chart/PersonalChartDetails.tsx
  (個人圖顯示區塊),723 行降到 443 行
- chart/[id].tsx、chart/preview.tsx 加上區域性 ErrorBoundary
- mobile/.gitignore 加入 bugreport-*.zip 規則

bug 修正:
- CompositeView/TransitView/create.tsx:套用出生資料時姓名殘留舊人物的問題
- ChartListView:長按選單 Alert 因 message 為 undefined 在 Android 顯示空白
- profile.tsx:Clerk user.update()/setProfileImage() 加 session 刷新;
  加 useFocusEffect(user.reload()) 解決跨裝置改名不同步(含修正一版曾造成無限迴圈的 deps 錯誤)
- InputModal:Android 上 KeyboardAvoidingView behavior='height' 與系統 resize 重複補償
  造成鍵盤跳出時晃動,改為 undefined(已實機確認解決)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
self-map Ready Ready Preview, Comment Jul 22, 2026 3:31am

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR centralizes shared Human Design data, improves mobile API timeouts and retries, refactors chart detail loading and rendering, adds localized error boundaries, and updates profile, modal, keyboard, and chart classification behavior.

Changes

Shared Human Design data

Layer / File(s) Summary
Shared data imports and integration rendering
app/api/aura-flow/..., components/humanDesign/..., mobile/shared, mobile/tsconfig.json, mobile/lib/..., mobile/components/...
Human Design data and content imports now use shared modules, with mobile path aliases and a symbolic shared directory. Integration channels are derived from shared channel data.
Shared content corrections
shared/humanDesign/*
Legend colors use palette constants, cross-content formatting is adjusted, and the definition population text changes from approximately 2% to approximately 1%.

Chart classification and API reliability

Layer / File(s) Summary
Classification and request resilience
mobile/lib/api.ts, mobile/components/ChartListView.tsx
Legacy composite detection is centralized, API requests gain configurable timeouts and transient retries, and chart endpoints use endpoint-specific settings and classification helpers.

Chart-detail loading and rendering

Layer / File(s) Summary
Chart detail data flow
mobile/lib/useChartDetail.ts, mobile/app/chart/[id].tsx
Chart loading, composite/transit preview computation, caching, stale-request protection, and report generation are moved into useChartDetail.
Personal detail presentation
mobile/components/chart/PersonalChartDetails.tsx, mobile/app/chart/[id].tsx
Personal chart sections and interactive gates, cross, and variables cards are extracted into PersonalChartDetails.
Localized screen error handling
mobile/app/chart/[id].tsx, mobile/app/chart/preview.tsx
Chart detail and preview content are wrapped in localized ErrorBoundary fallbacks.

Profile and interaction updates

Layer / File(s) Summary
Profile and applied-profile behavior
mobile/app/(tabs)/profile.tsx, mobile/app/(tabs)/create.tsx, mobile/components/CompositeView.tsx, mobile/components/TransitView.tsx
Profile focus reloads use a stable ref, profile mutations refresh Clerk tokens, and applied profile names are updated or cleared consistently.
Modal and keyboard behavior
mobile/components/BirthProfilePickerModal.tsx, mobile/components/InputModal.tsx
The picker uses a sibling backdrop and sheet layout, while input focus and keyboard avoidance are adjusted by platform.

Mobile supporting updates

Layer / File(s) Summary
Tech-debt and local artifact tracking
docs/tech-debt.md, mobile/.gitignore
Completed mobile checklist items are documented, and ADB bugreport ZIP archives are ignored.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChartDetailScreen
  participant useChartDetail
  participant mobileApi
  participant PersonalChartDetails
  ChartDetailScreen->>useChartDetail: load chart and derived results
  useChartDetail->>mobileApi: fetch chart and preview data
  mobileApi-->>useChartDetail: return chart and preview responses
  useChartDetail-->>ChartDetailScreen: expose chart state and result helpers
  ChartDetailScreen->>PersonalChartDetails: render personal chart details
Loading

Possibly related PRs

  • Retsomm/SelfMap#39: Earlier mobile API client work overlaps with the request helper changes.
  • Retsomm/SelfMap#40: Earlier chart-detail fetching changes overlap with the useChartDetail refactor.
  • Retsomm/SelfMap#51: Shared Human Design data migration overlaps with removal of the mobile-local data module.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and aligned with the PR’s main mobile refactor, tech-debt cleanup, and bug fixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
mobile/lib/useChartDetail.ts (1)

72-72: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Async effects lack a cancellation/"stale" guard.

loadChart (called from the effect at Line 94) and the composite/transit auto-rebuild effects (97-120, 130-154) all call setState after an await with no check that the effect is still current. If id changes again (e.g. reload() invoked while a previous fetch is in flight, or the screen receives a new id while mounted) a slower, stale response can overwrite newer chart/compositeFetched/transitFetched state. There's also no cleanup on unmount, so a fetch that resolves after the component unmounts still calls setState.

ESLint (react-hooks/set-state-in-effect) also flags Lines 72 and 94 for calling setState synchronously in the effect body, which is a symptom of the same underlying pattern.

♻️ Suggested pattern
   const loadChart = async () => {
+    // capture id at call time to detect staleness
     setLoading(true)
     setError(null)
     try {
       const token = await getToken()
       if (!token) { setError('未登入,請重新登入'); return }
       const data = await getChart(token, id)
+      if (id !== currentIdRef.current) return // stale response guard
       const c = data.chart
       ...

Flagged in part by static analysis (react-hooks/set-state-in-effect at Lines 72 and 94).

Also applies to: 94-94, 97-120, 130-154

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/lib/useChartDetail.ts` at line 72, Update the effects in
useChartDetail, including loadChart and the composite/transit rebuild effects,
to track cancellation or a current-effect guard and check it after every await
before calling setState. Ensure cleanup invalidates in-flight work on id changes
and unmount, preventing stale chart, compositeFetched, or transitFetched updates
while preserving the existing loading behavior without synchronous setState
calls directly in effect bodies.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mobile/components/BirthProfilePickerModal.tsx`:
- Around line 15-32: Wrap the backdrop Pressable and sheet View in a container
inside the Modal, keeping the ScrollView directly within the sheet to preserve
iOS scrolling. Update the backdrop styling to use StyleSheet.absoluteFillObject
so it covers the full modal behind the sheet, including outside its rounded
corners, while retaining the existing sheet layout and corner radii.

In `@mobile/lib/api.ts`:
- Around line 89-135: Update isTransientError to avoid referencing DOMException
with instanceof, which is unavailable in some React Native/Hermes environments.
Detect abort errors by safely checking whether the unknown error has name ===
'AbortError', while preserving the existing TypeError transient-error fallback
and retry behavior.

In `@mobile/lib/useChartDetail.ts`:
- Around line 156-174: Update getTransitResult to reuse transitFetched before
making an API request, and resolve birthDate, birthTime, birthCity, and timezone
from chart.meta.transitMeta with fallback to the top-level chart fields,
matching the auto-rebuild effect’s webMeta logic. Extract this fallback into a
shared helper and use it from both getTransitResult and the auto-rebuild effect
so transit calculations remain consistent and the fetched result is cached via
setTransitFetched.

---

Nitpick comments:
In `@mobile/lib/useChartDetail.ts`:
- Line 72: Update the effects in useChartDetail, including loadChart and the
composite/transit rebuild effects, to track cancellation or a current-effect
guard and check it after every await before calling setState. Ensure cleanup
invalidates in-flight work on id changes and unmount, preventing stale chart,
compositeFetched, or transitFetched updates while preserving the existing
loading behavior without synchronous setState calls directly in effect bodies.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7c7dd473-9b17-40cd-bf5f-8e1ac4d50d7b

📥 Commits

Reviewing files that changed from the base of the PR and between 0b20e81 and 85221fa.

📒 Files selected for processing (14)
  • docs/tech-debt.md
  • mobile/.gitignore
  • mobile/app/(tabs)/create.tsx
  • mobile/app/(tabs)/profile.tsx
  • mobile/app/chart/[id].tsx
  • mobile/app/chart/preview.tsx
  • mobile/components/BirthProfilePickerModal.tsx
  • mobile/components/ChartListView.tsx
  • mobile/components/CompositeView.tsx
  • mobile/components/InputModal.tsx
  • mobile/components/TransitView.tsx
  • mobile/components/chart/PersonalChartDetails.tsx
  • mobile/lib/api.ts
  • mobile/lib/useChartDetail.ts

Comment thread mobile/components/BirthProfilePickerModal.tsx Outdated
Comment thread mobile/lib/api.ts
Comment thread mobile/lib/useChartDetail.ts
BirthProfilePickerModal 的背景遮罩改用 absoluteFill 鋪滿整個 Modal,避免卡片圓角外露出未變暗的背景;
isTransientError 不再依賴 Hermes 不保證支援的 DOMException;
getTransitResult 改用與自動補算 effect 共用的出生資料 fallback 邏輯並加上結果快取,
避免 web 端存檔格式的流日圖算錯資料或重複打 API;
loadChart 與合圖/流日補算 effect 加上過期請求保護,避免快速切換圖表時寫入過期資料。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
hd-cross-data.ts、hd-summary-data.ts、hd-chart-data.ts 在 web
(原 components/humanDesign/)與 mobile(原 mobile/lib/)兩邊逐字相同
(共約 1650+ 行),搬到 repo root 的 shared/humanDesign/ 單一來源。

web 用既有 @/* alias 匯入;mobile 因為不是 yarn workspaces monorepo,
且 Metro 的 watchFolders 對 projectRoot 與 watchFolders 有巢狀關係的
情境解析有問題(watchman 查得到檔案,Metro file-map 卻找不到),
改用 mobile/shared -> ../shared 的 symlink,讓共用資料落在 Metro
原生認得的 projectRoot 內,不需要額外的 metro.config.js。

驗證:tsc --noEmit(web/mobile)、next build production build、
expo export --platform ios/android 非互動打包成功且資料確實包含在
bundle 內。細節與踩坑記錄見 docs/tech-debt.md。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mobile/lib/useChartDetail.ts`:
- Around line 92-93: Update the chart-switch handling around the id-dependent
effects so compositeFetched, transitFetched, and transitResultFetched are
cleared synchronously as part of the id transition, preventing the next chart
from rendering results cached for the previous chart; avoid relying on a
separate reset effect that can run after the new chart renders.
- Around line 199-207: Update getTransitResult to capture the current chart id
before awaiting previewTransitChart, then compare it with the active chart after
the await. Only call setTransitResultFetched and setTransitFetched when the
chart id is unchanged; still return the fetched result.
- Around line 85-90: Update the liveness-ref setup in useChartDetail so
currentIdRef.current is assigned inside the effect rather than during render,
and reset aliveRef.current to true during effect setup before returning cleanup
that sets it false. Preserve the async guards’ use of currentIdRef and aliveRef
while ensuring Strict Mode setup–cleanup–setup allows chart loading.

In `@shared/humanDesign/hd-chart-data.ts`:
- Around line 382-391: Update LEGEND_ITEMS to use the shared HD_PALETTE color
constants for each center instead of hardcoded hex values. Map every legend
item’s color to the corresponding palette entry already used by CENTERS_GEOM,
preserving the existing legend structure and labels.
- Line 132: Update DetailDrawer’s integration-channel label handling to derive
labels from the shared INTEGRATION_PAIRS metadata in hd-chart-data.ts instead of
maintaining a separate hardcoded list. Preserve the canonical pair names, ensure
all six pairs—including 20-57—are handled consistently, and define one clear
display rule for whether 20-57 appears independently or only as the
compound-view trunk.

In `@shared/humanDesign/hd-cross-data.ts`:
- Around line 47-49: Update the LAC entry’s body text in the cross-data
definition to remove the Markdown bold markers around 「純真」, keeping the
displayed content as plain text consistent with DetailDrawer’s paragraph
rendering.

In `@shared/humanDesign/hd-summary-data.ts`:
- Around line 297-305: Update the Reflector prevalence text in the 'None' entry
of HD_TYPE_CONTENT to match the canonical approximately 1% value used by
HD_TYPE_CONTENT['Reflector'], keeping the surrounding content unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 33e7579d-3cfa-4e05-b98e-ceb82d0271f4

📥 Commits

Reviewing files that changed from the base of the PR and between 85221fa and 17bf9be.

📒 Files selected for processing (31)
  • app/api/aura-flow/live-svg/route.ts
  • components/humanDesign/BodyGraph.tsx
  • components/humanDesign/ChartView.tsx
  • components/humanDesign/DetailDrawer.tsx
  • docs/tech-debt.md
  • mobile/app/chart/[id].tsx
  • mobile/app/chart/preview.tsx
  • mobile/app/learn/[topic].tsx
  • mobile/components/BirthProfilePickerModal.tsx
  • mobile/components/BodyGraph.tsx
  • mobile/components/DetailBottomSheet.tsx
  • mobile/components/TransitView.tsx
  • mobile/components/chart/PersonalChartDetails.tsx
  • mobile/components/chart/TransitAnalysis.tsx
  • mobile/components/learn/CenterList.tsx
  • mobile/components/learn/ChannelList.tsx
  • mobile/components/learn/GateList.tsx
  • mobile/components/learn/SummaryList.tsx
  • mobile/lib/api.ts
  • mobile/lib/chartPdf.ts
  • mobile/lib/hd-chart-data.ts
  • mobile/lib/hd-cross-data.ts
  • mobile/lib/hd-normalizers.ts
  • mobile/lib/hd-sheet-builders.ts
  • mobile/lib/hd-summary-data.ts
  • mobile/lib/useChartDetail.ts
  • mobile/shared
  • mobile/tsconfig.json
  • shared/humanDesign/hd-chart-data.ts
  • shared/humanDesign/hd-cross-data.ts
  • shared/humanDesign/hd-summary-data.ts
💤 Files with no reviewable changes (2)
  • mobile/lib/hd-summary-data.ts
  • mobile/lib/hd-chart-data.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • mobile/components/TransitView.tsx
  • mobile/components/BirthProfilePickerModal.tsx
  • mobile/components/chart/PersonalChartDetails.tsx
  • mobile/app/chart/preview.tsx
  • docs/tech-debt.md
  • mobile/lib/api.ts
  • mobile/app/chart/[id].tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mobile/lib/useChartDetail.ts`:
- Around line 92-93: Update the chart-switch handling around the id-dependent
effects so compositeFetched, transitFetched, and transitResultFetched are
cleared synchronously as part of the id transition, preventing the next chart
from rendering results cached for the previous chart; avoid relying on a
separate reset effect that can run after the new chart renders.
- Around line 199-207: Update getTransitResult to capture the current chart id
before awaiting previewTransitChart, then compare it with the active chart after
the await. Only call setTransitResultFetched and setTransitFetched when the
chart id is unchanged; still return the fetched result.
- Around line 85-90: Update the liveness-ref setup in useChartDetail so
currentIdRef.current is assigned inside the effect rather than during render,
and reset aliveRef.current to true during effect setup before returning cleanup
that sets it false. Preserve the async guards’ use of currentIdRef and aliveRef
while ensuring Strict Mode setup–cleanup–setup allows chart loading.

In `@shared/humanDesign/hd-chart-data.ts`:
- Around line 382-391: Update LEGEND_ITEMS to use the shared HD_PALETTE color
constants for each center instead of hardcoded hex values. Map every legend
item’s color to the corresponding palette entry already used by CENTERS_GEOM,
preserving the existing legend structure and labels.
- Line 132: Update DetailDrawer’s integration-channel label handling to derive
labels from the shared INTEGRATION_PAIRS metadata in hd-chart-data.ts instead of
maintaining a separate hardcoded list. Preserve the canonical pair names, ensure
all six pairs—including 20-57—are handled consistently, and define one clear
display rule for whether 20-57 appears independently or only as the
compound-view trunk.

In `@shared/humanDesign/hd-cross-data.ts`:
- Around line 47-49: Update the LAC entry’s body text in the cross-data
definition to remove the Markdown bold markers around 「純真」, keeping the
displayed content as plain text consistent with DetailDrawer’s paragraph
rendering.

In `@shared/humanDesign/hd-summary-data.ts`:
- Around line 297-305: Update the Reflector prevalence text in the 'None' entry
of HD_TYPE_CONTENT to match the canonical approximately 1% value used by
HD_TYPE_CONTENT['Reflector'], keeping the surrounding content unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 33e7579d-3cfa-4e05-b98e-ceb82d0271f4

📥 Commits

Reviewing files that changed from the base of the PR and between 85221fa and 17bf9be.

📒 Files selected for processing (31)
  • app/api/aura-flow/live-svg/route.ts
  • components/humanDesign/BodyGraph.tsx
  • components/humanDesign/ChartView.tsx
  • components/humanDesign/DetailDrawer.tsx
  • docs/tech-debt.md
  • mobile/app/chart/[id].tsx
  • mobile/app/chart/preview.tsx
  • mobile/app/learn/[topic].tsx
  • mobile/components/BirthProfilePickerModal.tsx
  • mobile/components/BodyGraph.tsx
  • mobile/components/DetailBottomSheet.tsx
  • mobile/components/TransitView.tsx
  • mobile/components/chart/PersonalChartDetails.tsx
  • mobile/components/chart/TransitAnalysis.tsx
  • mobile/components/learn/CenterList.tsx
  • mobile/components/learn/ChannelList.tsx
  • mobile/components/learn/GateList.tsx
  • mobile/components/learn/SummaryList.tsx
  • mobile/lib/api.ts
  • mobile/lib/chartPdf.ts
  • mobile/lib/hd-chart-data.ts
  • mobile/lib/hd-cross-data.ts
  • mobile/lib/hd-normalizers.ts
  • mobile/lib/hd-sheet-builders.ts
  • mobile/lib/hd-summary-data.ts
  • mobile/lib/useChartDetail.ts
  • mobile/shared
  • mobile/tsconfig.json
  • shared/humanDesign/hd-chart-data.ts
  • shared/humanDesign/hd-cross-data.ts
  • shared/humanDesign/hd-summary-data.ts
💤 Files with no reviewable changes (2)
  • mobile/lib/hd-summary-data.ts
  • mobile/lib/hd-chart-data.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • mobile/components/TransitView.tsx
  • mobile/components/BirthProfilePickerModal.tsx
  • mobile/components/chart/PersonalChartDetails.tsx
  • mobile/app/chart/preview.tsx
  • docs/tech-debt.md
  • mobile/lib/api.ts
  • mobile/app/chart/[id].tsx
🛑 Comments failed to post (7)
mobile/lib/useChartDetail.ts (3)

85-90: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file with line numbers
sed -n '1,220p' mobile/lib/useChartDetail.ts | cat -n

# Look for relevant lints or references to the refs in question
rg -n "currentIdRef|aliveRef|setState|Strict Mode|useEffect|useRef" mobile/lib/useChartDetail.ts

Repository: Retsomm/SelfMap

Length of output: 10596


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the React hook documentation from the installed Next.js docs only if present is unnecessary here;
# instead, inspect the local code for the lifecycle pattern in question.
python3 - <<'PY'
from pathlib import Path
p = Path('mobile/lib/useChartDetail.ts')
text = p.read_text()
for needle in ['currentIdRef', 'aliveRef', 'useEffect(() => () => { aliveRef.current = false }, [])']:
    print(f'FOUND {needle}:', needle in text)
PY

Repository: Retsomm/SelfMap

Length of output: 267


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the hook body around the refs and the async guards.
nl -ba mobile/lib/useChartDetail.ts | sed -n '70,140p'

Repository: Retsomm/SelfMap

Length of output: 193


🌐 Web query:

React Strict Mode useEffect cleanup remount useRef current mutation during render official docs

💡 Result:

In React, Strict Mode is a development-only tool designed to help identify subtle bugs and ensure your components are resilient to being mounted and unmounted [1][2][3]. React Strict Mode and useEffect When Strict Mode is enabled in development, React runs an extra setup-plus-cleanup cycle for every useEffect [1][2]. Specifically, the behavior is: setup → cleanup → setup [1][2]. This is a stress-test to verify that your cleanup function correctly stops or undoes whatever the setup function performed (e.g., clearing subscriptions, aborting network requests) [2][4]. The goal is to ensure that a component's behavior remains consistent whether the effect is run once or multiple times [2][4]. If this cycle causes visible issues in your application, it indicates that your cleanup logic is missing or incomplete [1][2]. useRef Current Mutation During Render You should not read or write to the ref.current property during the rendering phase [5][6]. React components should be pure functions of their props and state; reading or writing a ref during render introduces side effects or unpredictable behavior because refs are mutable sources that React does not track for re-renders [5][6][7]. There is one specific exception: you may lazily initialize a ref during the first render if the value has not yet been set [5][6][7]. For example, the pattern if (!ref.current) { ref.current = new Thing; } is safe because it only executes once during initialization and produces a predictable result [5][6]. Outside of this initialization pattern, accessing or modifying ref.current should occur only within event handlers or useEffect/useLayoutEffect hooks, which run during the commit phase [5][6][8].

Citations:


Move the liveness refs into an effect. currentIdRef.current = id writes during render, and aliveRef.current needs to be reset to true in setup; otherwise Strict Mode’s setup→cleanup→setup cycle can leave the guard false and suppress chart loads in development.

🧰 Tools
🪛 ESLint

[error] 87-87: Error: Cannot access refs during render

React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the current property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).

/home/jailuser/git/mobile/lib/useChartDetail.ts:87:3
85 | // 目前有效的圖表 id,供非同步流程在 await 之後判斷自己是否已經過期
86 | const currentIdRef = useRef(id)

87 | currentIdRef.current = id
| ^^^^^^^^^^^^^^^^^^^^ Cannot update ref during render
88 | // unmount 之後任何非同步流程都不可以再 setState
89 | const aliveRef = useRef(true)
90 | useEffect(() => () => { aliveRef.current = false }, [])

(react-hooks/refs)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/lib/useChartDetail.ts` around lines 85 - 90, Update the liveness-ref
setup in useChartDetail so currentIdRef.current is assigned inside the effect
rather than during render, and reset aliveRef.current to true during effect
setup before returning cleanup that sets it false. Preserve the async guards’
use of currentIdRef and aliveRef while ensuring Strict Mode setup–cleanup–setup
allows chart loading.

Source: Linters/SAST tools


92-93: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,220p' mobile/lib/useChartDetail.ts

Repository: Retsomm/SelfMap

Length of output: 8268


🏁 Script executed:

sed -n '220,360p' mobile/lib/useChartDetail.ts && printf '\n---\n' && fd -a 'chart/[id].tsx' mobile

Repository: Retsomm/SelfMap

Length of output: 186


🏁 Script executed:

rg -n "compositeFetched|transitFetched|transitResultFetched|getCompositeResult|getTransitResult|useChartDetail\(" mobile

Repository: Retsomm/SelfMap

Length of output: 2340


🏁 Script executed:

sed -n '80,150p' mobile/app/chart/[id].tsx && printf '\n---\n' && sed -n '360,410p' mobile/app/chart/[id].tsx

Repository: Retsomm/SelfMap

Length of output: 4885


Scope cached chart results by id. compositeFetched / transitFetched are reused across chart switches, so a separate reset effect can let the next chart briefly show the previous chart’s rebuilt composite/transit data. Clear them as part of the id transition or store the source chart id with each cached result.

🧰 Tools
🪛 ESLint

[error] 93-93: Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:

  • Update external systems with the latest state from React.
  • Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/jailuser/git/mobile/lib/useChartDetail.ts:93:21
91 |
92 | // 切換圖表時清除舊的合圖/流日補算結果,避免渲染過期資料

93 | useEffect(() => { setCompositeFetched(null); setTransitFetched(null); setTransitResultFetched(null) }, [id])
| ^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
94 |
95 | const loadChart = async () => {
96 | const requestedId = id

(react-hooks/set-state-in-effect)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/lib/useChartDetail.ts` around lines 92 - 93, Update the chart-switch
handling around the id-dependent effects so compositeFetched, transitFetched,
and transitResultFetched are cleared synchronously as part of the id transition,
preventing the next chart from rendering results cached for the previous chart;
avoid relying on a separate reset effect that can run after the new chart
renders.

Source: Linters/SAST tools


199-207: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the hook and relevant transit helpers.
git ls-files 'mobile/lib/useChartDetail.ts' 'mobile/lib/*' | sed -n '1,120p'

printf '\n--- outline ---\n'
ast-grep outline mobile/lib/useChartDetail.ts --view expanded || true

printf '\n--- relevant text search ---\n'
rg -n "stillCurrent|transitResultFetched|setTransitResultFetched|setTransitFetched|previewTransitChart|resolveTransitBirthInfo|getTransitResult|export" mobile/lib/useChartDetail.ts mobile/lib -g '!**/node_modules/**' || true

Repository: Retsomm/SelfMap

Length of output: 16106


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,280p' mobile/lib/useChartDetail.ts

Repository: Retsomm/SelfMap

Length of output: 8274


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the hook with line numbers and inspect the surrounding state/effects.
nl -ba mobile/lib/useChartDetail.ts | sed -n '1,320p'

Repository: Retsomm/SelfMap

Length of output: 193


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where useChartDetail is consumed and whether the chart detail view remounts on chart changes.
rg -n "useChartDetail\\(" mobile -g '!**/node_modules/**'
printf '\n--- route/detail references ---\n'
rg -n "chart detail|ChartDetail|chartId|key=" mobile -g '!**/node_modules/**'

Repository: Retsomm/SelfMap

Length of output: 8108


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,260p' mobile/app/chart/[id].tsx

printf '\n--- transit result usage ---\n'
rg -n "getTransitResult|transitResultFetched|transitFetched|export|pdf|share" mobile/app/chart/[id].tsx mobile/components mobile/lib -g '!**/node_modules/**'

Repository: Retsomm/SelfMap

Length of output: 29529


Guard the on-demand transit fetch against stale state. Capture the current chart id before await previewTransitChart(...) and skip setTransitResultFetched / setTransitFetched if it changed; otherwise an export started on one chart can cache its result into the next chart’s hook state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/lib/useChartDetail.ts` around lines 199 - 207, Update getTransitResult
to capture the current chart id before awaiting previewTransitChart, then
compare it with the active chart after the await. Only call
setTransitResultFetched and setTransitFetched when the chart id is unchanged;
still return the fetched result.
shared/humanDesign/hd-chart-data.ts (2)

132-132: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== hd-chart-data around integration pairs ==\n'
sed -n '110,170p' shared/humanDesign/hd-chart-data.ts

printf '\n== DetailDrawer integration-related slices ==\n'
rg -n "10-57|20-34|20-57|integration|INTEGRATION" components/humanDesign/DetailDrawer.tsx shared/humanDesign/hd-chart-data.ts -n -A3 -B3

printf '\n== Search repo for integration-channel metadata ==\n'
rg -n "10-57|20-34|20-57|INTEGRATION_PAIRS|integration pairs|integration channel" . -g '!node_modules'

Repository: Retsomm/SelfMap

Length of output: 17593


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [
    Path('shared/humanDesign/hd-chart-data.ts'),
    Path('components/humanDesign/DetailDrawer.tsx'),
]:
    print(f"\n## {p} ##")
    if p.exists():
        text = p.read_text()
        for needle in ['10-57', '20-34', '20-57', 'INTEGRATION_PAIRS', 'integration']:
            if needle in text:
                print(f"contains {needle}")
    else:
        print("missing")
PY

Repository: Retsomm/SelfMap

Length of output: 336


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== DetailDrawer integration block ==\n'
sed -n '148,176p' components/humanDesign/DetailDrawer.tsx

printf '\n== Shared integration constants ==\n'
sed -n '1,110p' lib/humanDesign/constants.ts

printf '\n== Where integration metadata is consumed ==\n'
sed -n '220,275p' components/humanDesign/BodyGraph.tsx
printf '\n--- mobile BodyGraph ---\n'
sed -n '150,190p' mobile/components/BodyGraph.tsx
printf '\n--- live-svg route ---\n'
sed -n '70,105p' app/api/aura-flow/live-svg/route.ts
printf '\n--- chartPdf ---\n'
sed -n '120,150p' mobile/lib/chartPdf.ts

Repository: Retsomm/SelfMap

Length of output: 14674


Make integration-channel labels come from one shared source.
shared/humanDesign/hd-chart-data.ts defines six integration pairs, but components/humanDesign/DetailDrawer.tsx hardcodes a separate five-item list, renames 10-57 and 20-34, and omits 20-57 entirely. Derive the drawer labels from the shared metadata and decide once whether 20-57 should be shown on its own or only as the trunk in the compound view.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shared/humanDesign/hd-chart-data.ts` at line 132, Update DetailDrawer’s
integration-channel label handling to derive labels from the shared
INTEGRATION_PAIRS metadata in hd-chart-data.ts instead of maintaining a separate
hardcoded list. Preserve the canonical pair names, ensure all six
pairs—including 20-57—are handled consistently, and define one clear display
rule for whether 20-57 appears independently or only as the compound-view trunk.

382-391: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reuse HD_PALETTE for legend swatches.

LEGEND_ITEMS hardcodes shades that differ from CENTERS_GEOM/HD_PALETTE (for example, head uses #d9c25e versus #e6c542). The legend will not visually match the rendered centers; reference the shared palette constants instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shared/humanDesign/hd-chart-data.ts` around lines 382 - 391, Update
LEGEND_ITEMS to use the shared HD_PALETTE color constants for each center
instead of hardcoded hex values. Map every legend item’s color to the
corresponding palette entry already used by CENTERS_GEOM, preserving the
existing legend structure and labels.
shared/humanDesign/hd-cross-data.ts (1)

47-49: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove Markdown markers from plain-text content.

DetailDrawer.tsx renders entry.body.split('\n') as plain <p> elements, so **「純真」** appears literally in the Gate 2 LAC content. Store plain text or add Markdown parsing.

Proposed fix
-    LAC: { title: `無知(天真爛漫)`, body: `「無知」聽起來好像不是什麼好事,但在這裡,它的意思其實比較接近**「純真」**。
+    LAC: { title: `無知(天真爛漫)`, body: `「無知」聽起來好像不是什麼好事,但在這裡,它的意思其實比較接近「純真」。
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    LAC: { title: `無知(天真爛漫)`, body: `「無知」聽起來好像不是什麼好事,但在這裡,它的意思其實比較接近「純真」。
你天生不帶太多成見,對生命有一種孩子般的開放態度。你不太會用舊的框架去套新的狀況,這讓你在面對變化的時候反而比很多人更靈活。
不過,你也可能因為這種純真,有時候會被環境帶著走,不確定自己真正想要什麼。你的課題是:在保持開放的同時,也要知道自己的底線在哪裡。因為你帶來的那份清新和無預設立場,是這個世界真的需要的——只是你自己也得先站穩腳步。` },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shared/humanDesign/hd-cross-data.ts` around lines 47 - 49, Update the LAC
entry’s body text in the cross-data definition to remove the Markdown bold
markers around 「純真」, keeping the displayed content as plain text consistent with
DetailDrawer’s paragraph rendering.
shared/humanDesign/hd-summary-data.ts (1)

297-305: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the Reflector prevalence consistent.

HD_TYPE_CONTENT['Reflector'] says approximately 1% (Line 75), while this Reflector entry says 2% on Line 300. Choose one canonical value so the learn screens do not contradict each other.

Proposed fix
-    intro: '九個中心全白,沒有任何一個是有顏色的。佔全人口約 2%。',
+    intro: '九個中心全白,沒有任何一個是有顏色的。佔全人口約 1%。',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  'None': {
    title: '無定義',
    subtitle: '反映者',
    intro: '九個中心全白,沒有任何一個是有顏色的。佔全人口約 1%。',
    paragraphs: [
      '反映者是人類圖五種類型中唯一沒有任何「固定」能量的人,所有中心都是開放的,就像一面超靈敏的鏡子,隨時在反映、感受周遭環境的能量狀態。你可以把反映者想成是某種「空氣品質偵測器」,他們能感知一個環境、一個群體裡的整體狀態,而且往往比其他人更早發現哪裡不對勁。',
      '不過正因為沒有固定的能量錨點,反映者特別需要給自己充裕的時間做決定——至少要等一個月亮循環(約二十八天),才能知道什麼對自己是真正正確的。不是拖延,是本來就需要這個時間來沉澱。',
    ],
    highlights: [{ label: '你的設計', text: '流動如鏡,對環境能量有超強感知力——等待月亮週期才做重大決定' }],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shared/humanDesign/hd-summary-data.ts` around lines 297 - 305, Update the
Reflector prevalence text in the 'None' entry of HD_TYPE_CONTENT to match the
canonical approximately 1% value used by HD_TYPE_CONTENT['Reflector'], keeping
the surrounding content unchanged.

- useChartDetail: 切換圖表時同步清除舊的合圖/流日結果,避免下一張圖表沿用
  上一張的快取;getTransitResult 補上過期請求防呆;修正 ref 相關寫法以符合
  react-compiler 的 render/effect 規則限制
- LEGEND_ITEMS 改用共用 HD_PALETTE,修正跟實際中心顏色不一致的問題
- DetailDrawer 整合通道改為衍生自 HD_CHANNELS/INTEGRATION_PAIRS,補上遺漏的
  20-57 並移除重複維護的硬編碼清單
- 修正 hd-cross-data.ts 殘留的 Markdown 星號與 hd-summary-data.ts 反映者
  盛行率前後矛盾(2% vs 1%)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/humanDesign/DetailDrawer.tsx (1)

23-23: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle cross-content load failures explicitly.

The dynamic import has no rejection handler. If the module load fails, crossContent stays null and the drawer remains on “載入中…” indefinitely. Track the failure, preserving the cancellation guard, and render a clear retry/fallback message.

As per coding guidelines, error handling must be clear and user-friendly in the Human Design calculator context.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/humanDesign/DetailDrawer.tsx` at line 23, Update the dynamic
import in DetailDrawer to handle rejected loads explicitly: preserve the
cancellation guard, record a load-error state when importing hd-cross-data
fails, and render a clear, user-friendly retry or fallback message instead of
leaving the drawer on “載入中…”.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/humanDesign/DetailDrawer.tsx`:
- Around line 149-154: Extract the inline INTEGRATION_PAIRS-to-HD_CHANNELS
lookup from the render branch into a named, documented reusable helper,
preserving the canonical `${from}-${to}` matching and omission of unresolved
channels. Add focused tests for the helper’s canonical from-to mapping, and
document its calculation principle.

---

Outside diff comments:
In `@components/humanDesign/DetailDrawer.tsx`:
- Line 23: Update the dynamic import in DetailDrawer to handle rejected loads
explicitly: preserve the cancellation guard, record a load-error state when
importing hd-cross-data fails, and render a clear, user-friendly retry or
fallback message instead of leaving the drawer on “載入中…”.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 77389a49-2555-43ed-837f-c9e2dfa1ff4e

📥 Commits

Reviewing files that changed from the base of the PR and between 17bf9be and be872f6.

📒 Files selected for processing (5)
  • components/humanDesign/DetailDrawer.tsx
  • mobile/lib/useChartDetail.ts
  • shared/humanDesign/hd-chart-data.ts
  • shared/humanDesign/hd-cross-data.ts
  • shared/humanDesign/hd-summary-data.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • shared/humanDesign/hd-summary-data.ts
  • shared/humanDesign/hd-chart-data.ts
  • mobile/lib/useChartDetail.ts

Comment on lines +149 to +154
// 六條整合通道皆已存在於 HD_CHANNELS,直接依 INTEGRATION_PAIRS 篩選,
// 不重複維護一份名稱/說明;20-57 是複合圖形的主幹(trunk),與其餘四條
// 支線(10-20/10-34/10-57/20-34/34-57)並列顯示,不特別區分。
const integrationChannels = Array.from(INTEGRATION_PAIRS)
.map(pairId => HD_CHANNELS.find(ch => `${ch.from}-${ch.to}` === pairId))
.filter((ch): ch is ChartChannel => !!ch)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract the integration-channel resolution into a reusable helper.

The pair-to-channel lookup is a distinct functionality but is implemented inline in the render branch. Move it to a named, documented helper and test the canonical from-to mapping independently.

As per coding guidelines, each functionality should be implemented as an independent, reusable function with complete comments including calculation principles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/humanDesign/DetailDrawer.tsx` around lines 149 - 154, Extract the
inline INTEGRATION_PAIRS-to-HD_CHANNELS lookup from the render branch into a
named, documented reusable helper, preserving the canonical `${from}-${to}`
matching and omission of unresolved channels. Add focused tests for the helper’s
canonical from-to mapping, and document its calculation principle.

Source: Coding guidelines

@Retsomm
Retsomm merged commit cda37e0 into main Jul 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant