Skip to content

feat(pos-app): redesign settings view with bottom sheets#389

Merged
ignaciosantise merged 11 commits intomainfrom
feat/settings-view-redesign
Feb 24, 2026
Merged

feat(pos-app): redesign settings view with bottom sheets#389
ignaciosantise merged 11 commits intomainfrom
feat/settings-view-redesign

Conversation

@ignaciosantise
Copy link
Copy Markdown
Collaborator

@ignaciosantise ignaciosantise commented Feb 23, 2026

Summary

Redesigned the POS app settings screen with a modern bottom sheet modal pattern, replacing the inline dropdown component. Added support for three theme modes (System, Light, Dark) with new theme selection UI.

Key changes:

  • Replace Dropdown component with SettingsItem + SettingsBottomSheet + RadioList pattern
  • Add "System" theme mode that respects device OS preference
  • Move Merchant ID and Partner API Key inputs into dedicated bottom sheets
  • New components: RadioList, SettingsBottomSheet, SettingsItem
  • New theme icons (sun, moon, device-mobile, caret-up-down)
  • Increment Android versionCode to 17
  • Bug fixes: Partner API Key input now captures first character on edit

Test Plan

  • Settings screen renders without errors
  • Theme selector (System/Light/Dark) works correctly
  • Wallet theme selector displays all variants
  • Currency selector displays all currencies
  • Merchant ID input/save flow works
  • Partner API Key input/save flow works (first character captured)
  • Bottom sheets open/close properly
  • Theme changes apply immediately to the app

🤖 Generated with Claude Code

- Replace inline dropdown component with bottom sheet modals and radio list UI
- Add new "System" theme mode that respects device OS preference
- Introduce SettingsItem, SettingsBottomSheet, and RadioList components
- Refactor theme handling: add ThemeMode type with "light", "dark", "system" options
- Move Merchant ID and Partner API Key inputs into dedicated bottom sheets
- Add new theme icons (sun, moon, device-mobile) and caret-up-down icon
- Remove Dropdown component (replaced by bottom sheet pattern)
- Increment Android versionCode from 16 to 17
- Fix Partner API Key input to capture first character on edit

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 23, 2026 19:50
@vercel
Copy link
Copy Markdown

vercel bot commented Feb 23, 2026

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

Project Deployment Actions Updated (UTC)
react-native-examples Ready Ready Preview, Comment Feb 24, 2026 4:16pm

Request Review

@claude
Copy link
Copy Markdown

claude bot commented Feb 23, 2026

Claude finished @ignaciosantise's task in 3s —— View job


Code Review

  • Gather context and read changed files
  • Run subagent checks (license, breaking changes, data classification)
  • Analyze code quality, bugs, performance, security
  • Post final review

License check: ✅ No issues — two new transitive MIT dev dependencies (@fastify/busboy, undici) via @vercel/node.

Breaking changes: ✅ No breaking changes — dropdown.tsx was only consumed by settings.tsx within the same app; all other changes are additive.

Found 4 issue(s)

Issue 1: Partner API key logged in plaintext on every keystroke

ID: dcl-settings-partner-api-key-logged-plaintext-a3f2
File: dapps/pos-app/app/settings.tsx:172
Severity: CRITICAL
Category: security / data_classification

Context:

  • Pattern: console.log("handlePartnerKeyChange", value) fires on every keystroke of the Partner API key input.
  • Risk: On Android, device logs are accessible via adb logcat; on iOS via Xcode/idevicesyslog. Crash-reporting SDKs (Sentry, Crashlytics) that scrape logcat also capture this. Every character of the key is reconstructed in plaintext.
  • Impact: Full credential leak of the Partner API key to anyone with USB debug access or crash-reporting dashboard access.
  • Trigger: Every keystroke in the Partner API Key bottom sheet.

Recommendation: Remove the console.log entirely.

 const handlePartnerKeyChange = (value: string) => {
-  console.log("handlePartnerKeyChange", value);
   if (!isEditingPartnerKey) {

Fix this →


Issue 2: System theme is non-reactive — OS changes not detected at runtime

ID: use-color-scheme-system-non-reactive-b7e1
File: dapps/pos-app/hooks/use-color-scheme.ts:6–10 / dapps/pos-app/hooks/use-theme-color.ts:7–9
Severity: HIGH
Category: code_quality / bug

Context:

  • Pattern: Both hooks call Appearance.getColorScheme() synchronously when "system" is active. This is a one-time snapshot — it does not re-render when the user switches the OS between light/dark mode while the app is running.
  • Risk: The "System" option (new in this PR) silently does nothing after initial mount.
  • Impact: User sets "System", switches OS to dark mode — app stays on whatever scheme was active at launch. The comments in both files acknowledge this.
  • Trigger: User changes OS appearance while app is foregrounded.

Recommendation: Use React Native's useColorScheme hook (already ships with RN / Expo) which subscribes to Appearance changes:

import { useColorScheme as useRNColorScheme } from 'react-native';

export const useColorScheme = () => {
  const themeMode = useSettingsStore((state) => state.themeMode);
  const systemScheme = useRNColorScheme(); // reactive
  if (themeMode === 'system') {
    return systemScheme || 'light';
  }
  return themeMode || 'light';
};

Apply the same pattern in use-theme-color.ts's resolveTheme.

Fix this →


Issue 3: Partner API Key SettingsItem always shows masked value even when no key is set

ID: settings-partner-key-display-misleading-c4d9
File: dapps/pos-app/app/settings.tsx:274–278
Severity: MEDIUM
Category: code_quality

Context:

  • Pattern: value="**********" is hardcoded — the storedPartnerApiKey from useMerchantFlow is available but not used here.
  • Risk: When no key is configured, the UI shows ten asterisks, implying a key is present.
  • Impact: Users can't distinguish between "key is set" and "no key set", which is misleading for first-time setup.

Recommendation:

<SettingsItem
 
  value={storedPartnerApiKey ? "**********" : undefined}
  onPress={() => setActiveSheet("partnerApiKey")}
/>

storedPartnerApiKey is already destructured from useMerchantFlow on line 100.

Fix this →


Issue 4: Async save handlers called without await — errors silently unhandled

ID: settings-async-handler-no-await-f2a3
File: dapps/pos-app/app/settings.tsx:162, 167
Severity: MEDIUM
Category: code_quality

Context:

  • Pattern: handleMerchantIdConfirm() and handlePartnerApiKeyConfirm() are both async, but called without await in handleMerchantIdSave and handlePartnerApiKeySave.
  • Risk: If the async chain throws before the internal try/catch in completeSave catches it (e.g., in isPinSet()), the rejection is unhandled. Also hides lint warnings for unhandled promise rejections.
  • Impact: Silent failures in the PIN initiation path are undetectable to the user.

Recommendation:

const handleMerchantIdSave = async () => {
  await handleMerchantIdConfirm();
  closeSheet();
};

const handlePartnerApiKeySave = async () => {
  await handlePartnerApiKeyConfirm();
  closeSheet();
};

Fix this →

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Redesigns the POS app Settings screen to use a bottom-sheet based selection/editing flow (radio lists + sheets) and adds a new “System” theme mode option, while removing the legacy inline dropdown UI.

Changes:

  • Replaces the old Dropdown UI with SettingsItem + SettingsBottomSheet + RadioList patterns across settings selections.
  • Introduces ThemeMode = "light" | "dark" | "system" and updates theme resolution + StatusBar behavior for “system”.
  • Moves Merchant ID / Partner API Key editing into dedicated bottom sheets and bumps Android versionCode to 17.

Reviewed changes

Copilot reviewed 11 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
dapps/pos-app/store/useSettingsStore.ts Adds ThemeMode (including system), updates persisted store version.
dapps/pos-app/package-lock.json Lockfile updates (adds nested dev dependencies).
dapps/pos-app/hooks/use-theme-color.ts Resolves system mode to an actual light/dark scheme for color selection.
dapps/pos-app/hooks/use-merchant-flow.ts Exposes storedPartnerApiKey to consumers (used by settings UI).
dapps/pos-app/hooks/use-color-scheme.ts Adds system mode behavior for navigation theme selection.
dapps/pos-app/components/settings-item.tsx New tappable row component used to open sheets / trigger actions.
dapps/pos-app/components/settings-bottom-sheet.tsx New bottom sheet wrapper built on FramedModal.
dapps/pos-app/components/radio-list.tsx New radio list UI for selecting among options with optional icons.
dapps/pos-app/components/dropdown.tsx Removes legacy dropdown component.
dapps/pos-app/assets/images/sun.png New theme icon asset.
dapps/pos-app/assets/images/moon.png New theme icon asset.
dapps/pos-app/assets/images/device-mobile.png New theme icon asset for “system”.
dapps/pos-app/assets/images/caret-up-down.png New caret icon asset for settings rows.
dapps/pos-app/app/settings.tsx Reworks settings screen to use bottom sheets + radio lists + sheet-based inputs.
dapps/pos-app/app/payment-success.tsx Adjusts StatusBar style handling for themeMode === "system".
dapps/pos-app/app.json Bumps Android versionCode from 16 → 17.
Files not reviewed (1)
  • dapps/pos-app/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…eedback

Add fade + slide-up animations to web modals using react-native-reanimated.
Update tests for "system" default theme mode and persist version 12.
Address PR review feedback: async handlers, partner API key rename merge.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Portal mobile web modal to document.body so backdrop covers full viewport including header
- Skip KeyboardAvoidingView on web to prevent empty gap below modal when keyboard opens
- Fix exhaustive-deps warnings in useEffect hooks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and visualViewport

- Use position: fixed for mobile web modal to prevent scroll displacement
- Track visualViewport height to constrain overlay above keyboard
- Fixes gap below modal when keyboard opens and view not restoring on close

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… formatting

Stop loading the actual partner API key into component state. Instead use
the isPartnerApiKeySet flag from the store and display a masked placeholder
in the UI. This keeps the sensitive value in secure storage only. Also
applies prettier formatting fixes across several files and moves ThemeMode
type to utils/types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ignaciosantise
Copy link
Copy Markdown
Collaborator Author

@claude review

@claude
Copy link
Copy Markdown

claude bot commented Feb 24, 2026

Claude finished @ignaciosantise's task in 3m 46s —— View job


Code Review

  • Gather context
  • Read changed files
  • Verify previously flagged issues
  • Analyze for new issues
  • Post final review

🔄 Incremental Review — checking state of previous issues and new changes

Previously flagged issues:

  • dcl-settings-partner-api-key-logged-plaintext-a3f2 (CRITICAL): console.log removed — resolved
  • settings-async-handler-no-await-f2a3 (MEDIUM): Both save handlers are now async/await — resolved
  • ⚠️ use-color-scheme-system-non-reactive-b7e1 (HIGH): Still present, acknowledged with comment in both use-color-scheme.ts:7-9 and use-theme-color.ts:7-8. Marked intentional/out of scope — accepted
  • ⚠️ settings-partner-key-display-misleading-c4d9 (MEDIUM): value="**********" still hardcoded at settings.tsx:267. Author confirmed app always has a default key — accepted

Found 2 issue(s)

Issue 1: Bottom sheet content offset 300px off-screen on native

ID: settings-bottom-sheet-native-translatey-f1c3
File: dapps/pos-app/components/settings-bottom-sheet.tsx:42–61
Severity: HIGH
Category: code_quality / bug

Context:

  • Pattern: translateY is initialized to useSharedValue(300) and the animation useEffect returns early with if (Platform.OS !== "web") return, so translateY.value is permanently 300 on iOS/Android.
  • Risk: sheetAnimatedStyle ({ transform: [{ translateY: 300 }] }) is applied to sheetContent on all platforms, including native. With justifyContent: "flex-end" positioning the sheet at the bottom, a 300px downward offset pushes it partially or fully off-screen.
  • Impact: All bottom sheets (Theme, Wallet theme, Currency, Merchant ID, Partner API Key) are broken on native — content is not visible.
  • Trigger: Any SettingsBottomSheet opened on iOS or Android.

Recommendation:

// Option A: skip the animated style on native (Modal handles its own animation)
<Animated.View
  style={[
    styles.sheet,
    { backgroundColor: Theme["bg-primary"] },
    Platform.OS === "web" ? sheetAnimatedStyle : undefined,
  ]}
>

// Option B: initialize translateY to 0 on native
const translateY = useSharedValue(Platform.OS === "web" ? 300 : 0);

Fix this →


Issue 2: Stale partnerApiKeyInput silently enables Save after discarding

ID: settings-partner-key-stale-input-e8b2
File: dapps/pos-app/app/settings.tsx:142–145, dapps/pos-app/hooks/use-merchant-flow.ts:264–265
Severity: MEDIUM
Category: code_quality / bug

Context:

  • Pattern: closeSheet() resets isEditingPartnerKey to false but does NOT clear partnerApiKeyInput in useMerchantFlow. The Save button's disabled state (isPartnerApiKeyConfirmDisabled) is driven by partnerApiKeyInput.trim().length === 0, not by the visual editing flag.
  • Risk: User types in the Partner API Key field → closes the sheet via ✕ → reopens it. The input visually shows "********" (masked, since isEditingPartnerKey is false), but partnerApiKeyInput still holds the typed value, so the Save button is enabled. Tapping Save persists the old unseen input.
  • Impact: Unintended API key saved without user awareness on subsequent sheet open.
  • Trigger: User types a value in the Partner API Key sheet, closes it without saving, then reopens and taps Save.

Recommendation: Reset partnerApiKeyInput on sheet close. The simplest fix is to expose a reset from useMerchantFlow:

// In useMerchantFlow — expose a reset function
const resetPartnerApiKeyInput = useCallback(() => {
  setState(prev => ({ ...prev, partnerApiKeyInput: "" }));
}, []);

// In settings.tsx closeSheet:
const closeSheet = () => {
  setActiveSheet(null);
  setIsEditingPartnerKey(false);
  resetPartnerApiKeyInput();
};

Fix this →

@ignaciosantise ignaciosantise merged commit 98af903 into main Feb 24, 2026
8 checks passed
@ignaciosantise ignaciosantise deleted the feat/settings-view-redesign branch February 24, 2026 17:17
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.

2 participants