Feat/add qr code support for sharing and joining groups - #500
Closed
theo-engels wants to merge 2 commits into
Closed
Feat/add qr code support for sharing and joining groups#500theo-engels wants to merge 2 commits into
theo-engels wants to merge 2 commits into
Conversation
- Add internationalization (i18n) to QrCodeScanner component using next-intl - Extract hardcoded UI strings: "Start Camera", "Stop Scanning", camera error messages - Follow existing translation conventions used in other components - Fix ShareQrCodeDialog to support multiple instances on same page - Replace hardcoded id="qr-code-svg" with per-instance unique id via useId() - Update handleDownload to reference dynamic id instead of document.getElementById() - Ensures no DOM id conflicts when multiple dialogs are rendered
theo-engels
marked this pull request as ready for review
January 31, 2026 16:04
BastiOfBerlin
pushed a commit
to BastiOfBerlin/spliit
that referenced
this pull request
Jun 11, 2026
Port several improvements from upstream spliit-app/spliit: - Exchange rates: migrate from api.frankfurter.app to api.frankfurter.dev/v1, fixing broken foreign-currency conversion (upstream spliit-app#515 / bugs spliit-app#513, spliit-app#514). - Selectors: wrap category and currency lists in cmdk's CommandList so keyboard (arrow-key) navigation works again (upstream spliit-app#491). - Group sharing: render a scannable QR code of the invite link in the share popover via qrcode.react (upstream spliit-app#500). - Currencies: add COP, VND, MKD, and MYR to the supported list and regenerate the per-locale currency data (upstream spliit-app#486, spliit-app#507, spliit-app#516, spliit-app#521). - React Compiler: enable Next.js 16's stable reactCompiler for automatic memoization, adding the babel-plugin-react-compiler dev dependency (upstream spliit-app#503). - Docker build: parse package.json name/version with POSIX tools instead of `node -p`, so the image can be built on hosts without Node.js (upstream spliit-app#219). - Docs: document the Docker Compose workflow and stack in the README (upstream spliit-app#505).
BastiOfBerlin
pushed a commit
to BastiOfBerlin/spliit
that referenced
this pull request
Jun 11, 2026
Port several improvements from upstream spliit-app/spliit: - Exchange rates: migrate from api.frankfurter.app to api.frankfurter.dev/v1, fixing broken foreign-currency conversion (upstream spliit-app#515 / bugs spliit-app#513, spliit-app#514). - Selectors: wrap category and currency lists in cmdk's CommandList so keyboard (arrow-key) navigation works again (upstream spliit-app#491). - Group sharing: render a scannable QR code of the invite link in the share popover via qrcode.react (upstream spliit-app#500). - Currencies: add COP, VND, MKD, and MYR to the supported list and regenerate the per-locale currency data (upstream spliit-app#486, spliit-app#507, spliit-app#516, spliit-app#521). - React Compiler: enable Next.js 16's stable reactCompiler for automatic memoization, adding the babel-plugin-react-compiler dev dependency (upstream spliit-app#503). - Docker build: parse package.json name/version with POSIX tools instead of `node -p`, so the image can be built on hosts without Node.js (upstream spliit-app#219). - Docs: document the Docker Compose workflow and stack in the README (upstream spliit-app#505).
Ecklebe
pushed a commit
to Ecklebe/spliit
that referenced
this pull request
Jul 19, 2026
Ecklebe
pushed a commit
to Ecklebe/spliit
that referenced
this pull request
Jul 19, 2026
Contributor
|
@theo-engels thanks for this! using it now |
24 tasks
BastiOfBerlin
pushed a commit
to BastiOfBerlin/spliit
that referenced
this pull request
Aug 13, 2026
Layered on top of spliit-app#500, from a fork that had arrived at a QR code independently. - Parse the pasted or scanned URL with the URL API instead of building a RegExp from window.location.origin, which interpolates it unescaped. Resolving against the current origin keeps spliit-app#500's support for relative /groups/<id> links from a scanned code, while an absolute link retains its own origin and so still fails the same-origin check — the previous relative-URL fallback matched any host. - Let the scanner be retried after a permission denial. The start button rendered only while hasPermission was null, so a dismissed browser prompt left the error message as the only remaining UI until the popover was closed and reopened. - Use marginSize instead of includeMargin, deprecated in qrcode.react 4, and give the QR an explicit title and colours so it stays legible when the dialog is rendered against a dark theme. - Add titles to the copy and share buttons, which had none.
Merged
BastiOfBerlin
added a commit
that referenced
this pull request
Aug 13, 2026
# feat: QR codes for sharing and joining groups Picks up **#500** by @theo-engels, rebased onto current `main` and with a few fixes layered on top. Their commit is unchanged and still under their name — everything I added is in a separate commit at the end. Closes #500. ## What it does Two things, both from the original PR: - **Share a group as a QR code.** A `QrCode` button in the share popover opens a dialog with the group's URL as a QR, the Spliit logo embedded in the middle, and a button to download it as a PNG. - **Join a group by scanning one.** On narrow viewports, "Add by URL" gains a second mode that opens the camera and accepts a scanned group link. ## Why it needed a rebase The PR conflicted, but only on `package-lock.json` — its lockfile commit (`3fabe84`) was cut from `d3b151e` back in January and no longer applies. I regenerated the lockfile against current `main` on npm 11 rather than replaying that diff. It produces the same 17-line/11-line shape, and after #556 I'd rather not replay a six-month-old lockfile diff onto `main` again. `package.json` and the source files rebased cleanly. ## Commits | | | |---|---| | `ee4f467` | @theo-engels' original commit, cherry-picked with `-x`, byte-identical | | `a849a7f` | regenerate `package-lock.json` for the two new dependencies | | `3195ad4` | apply the repo's Prettier config to the three touched files | | `622e0e0` | the fixes below | **Please merge this with a merge commit or a rebase rather than a squash** — squashing collapses their commit and mine into one authorship. ## What I changed on top ### The relative-URL fallback accepted any host This is the one worth a close look. #500 extends the "add by URL" parser so a scanned code can carry a relative link: ```js const [, groupId] = urlToProcess.match(new RegExp(`${window.location.origin}/groups/([^/]+)`)) ?? urlToProcess.match(/\/groups\/([^/?]+)/) ?? // Also match relative URLs from QR [] ``` The second pattern is unanchored and has no origin check, so it also matches absolute URLs on **any** host — `https://evil.example/groups/abc` scans as a valid group link. The first pattern has a separate problem that predates this PR: it interpolates `window.location.origin` into a `RegExp` unescaped. Both go away with the URL API: ```js const parsed = new URL(urlToProcess, window.location.origin) if (parsed.origin === window.location.origin) { groupId = parsed.pathname.match(/^\/groups\/([^/]+)/)?.[1] } ``` Resolving against the current origin is what keeps the relative-link case working — a relative path inherits our origin and passes, while an absolute URL keeps its own and fails the check. `pathname` also drops the query string, so the `?ref=share` on shared links no longer needs its own character class. ### The scanner couldn't be retried after a denied permission The start button rendered only while `hasPermission === null`: ```jsx {!isScanning && hasPermission === null && ( <Button …> )} ``` So dismissing the browser's camera prompt — easy to do by accident — flipped it to `false` and left the error message as the only remaining UI. The only way back was to close and reopen the popover. It now renders whenever the scanner isn't running, and `startScanning` resets the flag so a stale error doesn't sit underneath a fresh attempt. ### Smaller things - `includeMargin` is deprecated in qrcode.react 4; switched to `marginSize`. - Gave the QR explicit `bgColor`/`fgColor` and a `title`. It renders over a themed surface, so it shouldn't inherit one, and the title gives screen readers something to announce. - Added `title` attributes to the copy and share buttons in the share popover, which had none. New `en-US` strings only, so Weblate picks them up normally. ## New dependencies | | | | |---|---|---| | `qrcode.react` | ISC | 45 kB unminified, rendering only | | `html5-qrcode` | Apache-2.0 | 375 kB minified, unpacked 3.4 MB | `html5-qrcode` is not small, and it's worth deciding deliberately. It's imported by `qr-code-scanner.tsx`, a client component reachable from the groups list, so Next will split it into that route's chunk rather than the shared bundle — but anyone landing on the groups page pays for it once the chunk loads. If that's not a trade you want, a `next/dynamic` import behind the scan-mode toggle would defer it to the moment someone actually taps "Scan QR", and I'm happy to add that here. ## Verification `npm ci --ignore-scripts`, `npx prisma generate`, `npm run check-types`, `npm run lint`, `npm run check-formatting` — all clean against current `main`. Lint reports 16 warnings, all of them pre-existing. Not yet exercised on a physical device: the camera path needs a real phone, and the PNG download path needs a browser check on Safari in particular (`canvas.toDataURL` on an SVG-sourced image is the fiddly part). Worth someone confirming both before this goes out. --------- Co-authored-by: theo-engels <theoengels@proton.me> Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add QR code functionality to improve group sharing and joining experience:
Add camera-based QR code scanner for joining groups
Add new dependencies:
Add translation keys for QR code UI elements
Generating a QR code can be done on desktop and mobile clients, using the same workflow as for sharing a URL, but with an additional QR code icon. Selecting this will generate the QR Code.
Generate QR Code icon

Show QR Code

For a new group member that is all that would be needed to take the user to the site and access the group. For existing Spliit users, there is also an option to aquire the URL via the QR Code and add it to their list. To go this go to the top level groups section , and select the current Add by URL button. If on a mobile device it will now offer the user to add by URL as per the current option, or via QR code, at which point it would prompt the user to
Aquire Group via QR Code
