Skip to content

Conversation

@ignaciosantise
Copy link
Collaborator

Summary

  • Added a new patch to the QR code library to allow more customization

Copilot AI review requested due to automatic review settings November 28, 2025 21:19
@vercel
Copy link

vercel bot commented Nov 28, 2025

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

Project Deployment Preview Comments Updated (UTC)
react-native-examples Ready Ready Preview Comment Nov 28, 2025 9:23pm

@claude
Copy link

claude bot commented Nov 28, 2025

Claude finished @ignaciosantise's task —— View job


Code Review Complete

✅ No issues found


Summary:

Reviewed 3 changed files:

  • dapps/pos-app/app.json - Version code bump (12 → 13)
  • dapps/pos-app/components/qr-code.tsx - QR code styling updates (padding change, shape options)
  • dapps/pos-app/patches/react-native-qrcode-skia+0.3.1.patch - Large patch (+853/-91 lines) adding new customization features to QR library

Changes analyzed:

  • Added logoAreaBorderRadius support for rounded logo areas in QR codes
  • Added eyeShape and pupilShape options for QR code eye customization
  • Added "line" shape option for QR code dots
  • Implemented rounded rectangle collision detection for logo area masking
  • Component now uses shape: "line", eyeShape: "rounded", pupilShape: "circle", and increased gap to 1.2

Automated checks:

  • No external domain URLs introduced
  • No cache-control issues detected
  • No GitHub Actions workflow security issues

Memo comparison note (non-blocking observation):
The memo comparison function in qr-code.tsx:89-96 doesn't check logoSize prop changes, but since logoSize is derived from size via calculation, changes to size will trigger re-renders. The logoBorderRadius prop is checked. This appears intentional for performance optimization.

Copy link

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

This PR adds a comprehensive patch to the react-native-qrcode-skia library (v0.3.1) to enable more customization options for QR code rendering. The changes introduce new shape styles, custom eye patterns, and rounded logo areas, then applies these new features to the POS app's QR code component.

Key changes:

  • Adds new QR code customization features: logoAreaBorderRadius, eyeShape, pupilShape, and line shape style
  • Implements rounded rectangle logic for logo areas with corner circle detection
  • Adds custom eye rendering with circle, rounded, and square options for both the eye frame and pupil
  • Implements a "line" shape that renders QR modules as vertical pills
  • Updates the QR code component to use the new "line" shape with custom eye patterns
  • Increments the Android versionCode from 12 to 13

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 10 comments.

File Description
dapps/pos-app/patches/react-native-qrcode-skia+0.3.1.patch Comprehensive patch adding new shape options, rounded logo areas, custom eye patterns, and line rendering mode
dapps/pos-app/components/qr-code.tsx Updates QR configuration to use new line shape with rounded eyes, circular pupils, and reduced padding
dapps/pos-app/app.json Increments Android versionCode to 13 for the release

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

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

Comment on lines +277 to +286
+ const logoAreaX = (center - logoRadius) * cellSize;
+ const logoAreaY = (center - logoRadius) * cellSize;
+ const logoAreaSize = (logoRadius * 2 + 1) * cellSize;
+ const r = Math.min(logoAreaBorderRadius, logoAreaSize / 2);
+ const cellCenterX = j * cellSize + cellSize / 2;
+ const cellCenterY = i * cellSize + cellSize / 2;
+
+ // Center rectangle (excluding corners)
+ if (cellCenterX >= logoAreaX + r && cellCenterX <= logoAreaX + logoAreaSize - r && cellCenterY >= logoAreaY && cellCenterY <= logoAreaY + logoAreaSize) return true;
+ if (cellCenterY >= logoAreaY + r && cellCenterY <= logoAreaY + logoAreaSize - r && cellCenterX >= logoAreaX && cellCenterX <= logoAreaX + logoAreaSize) return true;
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] The variable name logoAreaSize represents a single dimension (width/height) but the rounded rectangle logic uses it as both width and height. This is correct since it's a square, but the calculation (logoRadius * 2 + 1) * cellSize may not be semantically accurate. The + 1 accounts for the center cell, making it an odd number of cells, but this should be verified against the expected logo area dimensions.

Suggested change
+ const logoAreaX = (center - logoRadius) * cellSize;
+ const logoAreaY = (center - logoRadius) * cellSize;
+ const logoAreaSize = (logoRadius * 2 + 1) * cellSize;
+ const r = Math.min(logoAreaBorderRadius, logoAreaSize / 2);
+ const cellCenterX = j * cellSize + cellSize / 2;
+ const cellCenterY = i * cellSize + cellSize / 2;
+
+ // Center rectangle (excluding corners)
+ if (cellCenterX >= logoAreaX + r && cellCenterX <= logoAreaX + logoAreaSize - r && cellCenterY >= logoAreaY && cellCenterY <= logoAreaY + logoAreaSize) return true;
+ if (cellCenterY >= logoAreaY + r && cellCenterY <= logoAreaY + logoAreaSize - r && cellCenterX >= logoAreaX && cellCenterX <= logoAreaX + logoAreaSize) return true;
+ // logoAreaSideLength is the length of one side of the square logo area, covering (logoRadius * 2 + 1) cells centered in the matrix.
+ const logoAreaX = (center - logoRadius) * cellSize;
+ const logoAreaY = (center - logoRadius) * cellSize;
+ const logoAreaSideLength = (logoRadius * 2 + 1) * cellSize;
+ const r = Math.min(logoAreaBorderRadius, logoAreaSideLength / 2);
+ const cellCenterX = j * cellSize + cellSize / 2;
+ const cellCenterY = i * cellSize + cellSize / 2;
+
+ // Center rectangle (excluding corners)
+ if (cellCenterX >= logoAreaX + r && cellCenterX <= logoAreaX + logoAreaSideLength - r && cellCenterY >= logoAreaY && cellCenterY <= logoAreaY + logoAreaSideLength) return true;
+ if (cellCenterY >= logoAreaY + r && cellCenterY <= logoAreaY + logoAreaSideLength - r && cellCenterX >= logoAreaX && cellCenterX <= logoAreaX + logoAreaSideLength) return true;

Copilot uses AI. Check for mistakes.
Comment on lines +421 to +451
+ }
+ return false;
+ };
+ if (shape === 'line') {
+ const processed = Array.from({
+ length: matrix.length
+ }, () => Array(matrix.length).fill(false));
+ for (let j = 0; j < matrix.length; j++) {
+ for (let i = 0; i < matrix.length; i++) {
+ if (matrix[i][j] === 1 && !isLogoArea(i, j) && !processed[i][j]) {
+ if (isDetectionPatternCell(i, j)) {
+ if (!handleEyeCell(i, j)) {
+ const corners = getCorners(i, j, true);
+ const neighbors = getNeighbors(i, j);
+ path += renderElement(corners, neighbors, i, j);
+ }
+ processed[i][j] = true;
+ continue;
+ }
+ let runEnd = i;
+ while (runEnd < matrix.length && matrix[runEnd][j] === 1 && !isLogoArea(runEnd, j) && !isDetectionPatternCell(runEnd, j)) {
+ processed[runEnd][j] = true;
+ runEnd++;
+ }
+ if (runEnd - i === 1) {
+ const corners = getCorners(i, j, false);
+ const effectiveCellSize = cellSize - gap;
+ path += `M${corners.center.x} ${corners.center.y} m-${effectiveCellSize / 2}, 0 a${effectiveCellSize / 2},${effectiveCellSize / 2} 0 1,0 ${effectiveCellSize},0 a${effectiveCellSize / 2},${effectiveCellSize / 2} 0 1,0 -${effectiveCellSize},0`;
+ } else {
+ path += renderVerticalPill(j, i, runEnd - 1);
+ }
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] The 'line' shape implementation processes the QR matrix column by column (outer loop on j), creating vertical pills. However, the non-null assertion operator (!) is used extensively (e.g., matrix[i]![j], processed[i]![j]). While TypeScript may require this due to how the loops are structured, it could indicate a potential runtime issue if the matrix is not properly initialized. Consider adding defensive checks or restructuring to avoid the need for non-null assertions.

Copilot uses AI. Check for mistakes.
+ path += renderEye(origin.row, origin.col);
+ }
+ return true;
}
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] The handleEyeCell function uses a Set to track rendered eyes with a key format ${origin.row},${origin.col}. This ensures each eye is only rendered once. However, if this function is called multiple times for the same eye pattern cells, the deduplication works correctly. Consider if the early return at line 416 could skip legitimate rendering in edge cases where the origin calculation returns null unexpectedly.

Suggested change
}
+ } else {
+ // Defensive: log a warning if origin is unexpectedly null for a detection pattern cell
+ if (typeof console !== "undefined" && console.warn) {
+ console.warn(
+ `[QRCode] Warning: getDetectionPatternOrigin returned null for cell (${i},${j}) in custom eye shape mode.`
+ );
+ }
+ }

Copilot uses AI. Check for mistakes.
Comment on lines +296 to +305
+ if (cellCenterX < logoAreaX + r && cellCenterY < logoAreaY + r) return inCornerCircle(logoAreaX + r, logoAreaY + r);
+
+ // Top-right
+ if (cellCenterX > logoAreaX + logoAreaSize - r && cellCenterY < logoAreaY + r) return inCornerCircle(logoAreaX + logoAreaSize - r, logoAreaY + r);
+
+ // Bottom-right
+ if (cellCenterX > logoAreaX + logoAreaSize - r && cellCenterY > logoAreaY + logoAreaSize - r) return inCornerCircle(logoAreaX + logoAreaSize - r, logoAreaY + logoAreaSize - r);
+
+ // Bottom-left
+ if (cellCenterX < logoAreaX + r && cellCenterY > logoAreaY + logoAreaSize - r) return inCornerCircle(logoAreaX + r, logoAreaY + logoAreaSize - r);
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The inCornerCircle function checks if a point is within a circular corner region. However, the logic for the corner checks at lines 293-302 uses > and < comparisons which might miss cells exactly on the boundary. Consider if cells exactly at logoAreaX + r should be included or excluded, and ensure the logic is consistent with the center rectangle checks above.

Suggested change
+ if (cellCenterX < logoAreaX + r && cellCenterY < logoAreaY + r) return inCornerCircle(logoAreaX + r, logoAreaY + r);
+
+ // Top-right
+ if (cellCenterX > logoAreaX + logoAreaSize - r && cellCenterY < logoAreaY + r) return inCornerCircle(logoAreaX + logoAreaSize - r, logoAreaY + r);
+
+ // Bottom-right
+ if (cellCenterX > logoAreaX + logoAreaSize - r && cellCenterY > logoAreaY + logoAreaSize - r) return inCornerCircle(logoAreaX + logoAreaSize - r, logoAreaY + logoAreaSize - r);
+
+ // Bottom-left
+ if (cellCenterX < logoAreaX + r && cellCenterY > logoAreaY + logoAreaSize - r) return inCornerCircle(logoAreaX + r, logoAreaY + logoAreaSize - r);
+ if (cellCenterX <= logoAreaX + r && cellCenterY <= logoAreaY + r) return inCornerCircle(logoAreaX + r, logoAreaY + r);
+
+ // Top-right
+ if (cellCenterX >= logoAreaX + logoAreaSize - r && cellCenterY <= logoAreaY + r) return inCornerCircle(logoAreaX + logoAreaSize - r, logoAreaY + r);
+
+ // Bottom-right
+ if (cellCenterX >= logoAreaX + logoAreaSize - r && cellCenterY >= logoAreaY + logoAreaSize - r) return inCornerCircle(logoAreaX + logoAreaSize - r, logoAreaY + logoAreaSize - r);
+
+ // Bottom-left
+ if (cellCenterX <= logoAreaX + r && cellCenterY >= logoAreaY + logoAreaSize - r) return inCornerCircle(logoAreaX + r, logoAreaY + logoAreaSize - r);

Copilot uses AI. Check for mistakes.
Comment on lines +358 to +359
+ const currentEyeShape = eyeShape ?? 'square';
+ const currentPupilShape = pupilShape ?? 'square';
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The default values for currentEyeShape and currentPupilShape use the nullish coalescing operator (??) with 'square' as the default, but the type definitions suggest these should be optional. When eyeShape or pupilShape are undefined, the eyes will render as squares. However, the original behavior (when useCustomEyeShape is false) is not clear. Consider documenting this behavior change or ensuring backward compatibility.

Suggested change
+ const currentEyeShape = eyeShape ?? 'square';
+ const currentPupilShape = pupilShape ?? 'square';
+ const currentEyeShape = eyeShape ?? eyePatternShape;
+ const currentPupilShape = pupilShape ?? eyePatternShape;

Copilot uses AI. Check for mistakes.
Comment on lines +365 to +374
+ const centerX = outerX + outerSize / 2;
+ const centerY = outerY + outerSize / 2;
+ // Outer circle
+ eyePath += `M${centerX - outerRadius} ${centerY} a${outerRadius},${outerRadius} 0 1,0 ${outerSize},0 a${outerRadius},${outerRadius} 0 1,0 -${outerSize},0 `;
+ // Inner cutout circle (reverse winding for cutout)
+ eyePath += `M${centerX - cutoutRadius} ${centerY} a${cutoutRadius},${cutoutRadius} 0 1,1 ${cutoutSize},0 a${cutoutRadius},${cutoutRadius} 0 1,1 -${cutoutSize},0 `;
+ } else if (currentEyeShape === 'rounded') {
+ const outerR = outerSize * 0.35;
+ const cutoutR = cutoutSize * 0.3;
+ // Outer rounded rect
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] In the renderEye function, the reverse winding for cutout paths uses opposite sweep flags (0 1,1 vs 0 1,0) to create holes. Ensure this is tested across different rendering engines, as SVG path winding rules can be implementation-specific. The even-odd fill rule should handle this correctly, but verify the QR codes render properly on both iOS and Android.

Copilot uses AI. Check for mistakes.
+ const yEnd = (endRow + 1) * cellSize - padding;
+ const width = cellSize - gap;
+ const radius = width / 2;
+ return `M${x} ${yStart + radius} A${radius} ${radius} 0 0 1 ${x + width} ${yStart + radius} L${x + width} ${yEnd - radius} A${radius} ${radius} 0 0 1 ${x} ${yEnd - radius} Z`;
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The renderVerticalPill function creates pill-shaped modules for the 'line' shape style. The arc commands use flags 0 0 1 which means: rx-axis-rotation=0, large-arc-flag=0, sweep-flag=1. However, for a semicircle (180 degrees), the large-arc-flag should typically be 0. Verify this creates the intended pill shape visually, especially when the height spans multiple cells.

Suggested change
+ return `M${x} ${yStart + radius} A${radius} ${radius} 0 0 1 ${x + width} ${yStart + radius} L${x + width} ${yEnd - radius} A${radius} ${radius} 0 0 1 ${x} ${yEnd - radius} Z`;
+ return `M${x} ${yStart + radius} A${radius} ${radius} 0 0 0 ${x + width} ${yStart + radius} L${x + width} ${yEnd - radius} A${radius} ${radius} 0 0 0 ${x} ${yEnd - radius} Z`;

Copilot uses AI. Check for mistakes.
@ignaciosantise ignaciosantise merged commit 44a9e99 into main Nov 28, 2025
13 checks passed
@ignaciosantise ignaciosantise deleted the chore/qr-changes branch November 28, 2025 21:26
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