diff --git a/bskyweb/cmd/bskyweb/invitepass.go b/bskyweb/cmd/bskyweb/invitepass.go index 51dd7943dd..7b17d2de66 100644 --- a/bskyweb/cmd/bskyweb/invitepass.go +++ b/bskyweb/cmd/bskyweb/invitepass.go @@ -220,12 +220,15 @@ func (srv *Server) WebInviteWalletHero(c echo.Context) error { } func (srv *Server) buildPassAssets(theme, handle string, avatarBytes []byte) ([]PassAsset, error) { - icon1, err := readFS(srv.cfg.InvitePass.StripFS, "passes/"+theme+"/icon.png") - if err != nil { - return nil, fmt.Errorf("icon: %w", err) + _ = theme + // Icons are shared across themes (matches the Pass Designer layout - the + // pass.json hardcodes one backgroundColor + one background image). + icon1, _ := readFS(srv.cfg.InvitePass.StripFS, "passes/icon.png") + icon2, _ := readFS(srv.cfg.InvitePass.StripFS, "passes/icon@2x.png") + icon3, _ := readFS(srv.cfg.InvitePass.StripFS, "passes/icon@3x.png") + if len(icon1) == 0 && len(icon2) == 0 && len(icon3) == 0 { + return nil, fmt.Errorf("icon: no icon asset found at any density") } - icon2, _ := readFS(srv.cfg.InvitePass.StripFS, "passes/"+theme+"/icon@2x.png") - icon3, _ := readFS(srv.cfg.InvitePass.StripFS, "passes/"+theme+"/icon@3x.png") logo1, err := readFS(srv.cfg.InvitePass.StripFS, "passes/logo.png") if err != nil { return nil, fmt.Errorf("logo: %w", err) @@ -247,9 +250,11 @@ func (srv *Server) buildPassAssets(theme, handle string, avatarBytes []byte) ([] _ = avatarBytes assets := []PassAsset{ - {Name: "icon.png", Data: icon1}, {Name: "logo.png", Data: logo1}, } + if len(icon1) > 0 { + assets = append(assets, PassAsset{Name: "icon.png", Data: icon1}) + } if len(icon2) > 0 { assets = append(assets, PassAsset{Name: "icon@2x.png", Data: icon2}) } diff --git a/src/features/inviteFriends/InviteFriendsDialogInner.tsx b/src/features/inviteFriends/InviteFriendsDialogInner.tsx index a1c2f0c867..65348c4e0a 100644 --- a/src/features/inviteFriends/InviteFriendsDialogInner.tsx +++ b/src/features/inviteFriends/InviteFriendsDialogInner.tsx @@ -203,7 +203,7 @@ export function InviteFriendsDialogInner({ {isHandleValid && handle ? ( - + ) : null} diff --git a/src/features/inviteFriends/components/AddToWalletButton.test.tsx b/src/features/inviteFriends/components/AddToWalletButton.test.tsx index 7aa2608c2f..02e0745e17 100644 --- a/src/features/inviteFriends/components/AddToWalletButton.test.tsx +++ b/src/features/inviteFriends/components/AddToWalletButton.test.tsx @@ -85,9 +85,7 @@ afterEach(async () => { // Test 1: IS_WEB gate test('renders null on web', async () => { mockIsWeb = true - const {toJSON} = render( - , - ) + const {toJSON} = render() // Flush effects - with IS_WEB=true the canAddPasses effect exits early, // so the flush is just a guard. await act(async () => {}) @@ -97,9 +95,7 @@ test('renders null on web', async () => { // Test 2: canAddPasses gate test('renders null when canAddPasses() resolves false', async () => { RNWallet.canAddPasses.mockResolvedValue(false) - const {toJSON} = render( - , - ) + const {toJSON} = render() await waitFor(() => expect(RNWallet.canAddPasses).toHaveBeenCalled()) // Flush promise resolution so setCanAdd(false) runs inside act await act(async () => {}) @@ -108,9 +104,7 @@ test('renders null when canAddPasses() resolves false', async () => { // Test 3: handle sentinel gate test('renders null when handle is invalid', async () => { - const {toJSON} = render( - , - ) + const {toJSON} = render() // handle is invalid so canAddPasses is not called; flush anyway for safety await act(async () => {}) expect(toJSON()).toBeNull() @@ -118,7 +112,7 @@ test('renders null when handle is invalid', async () => { // Test 4: empty handle gate test('renders null when handle is empty', async () => { - const {toJSON} = render() + const {toJSON} = render() await act(async () => {}) expect(toJSON()).toBeNull() }) @@ -133,7 +127,7 @@ test('iOS: mints token then calls addPass with the returned URL', async () => { }), }) const {UNSAFE_getByType} = render( - , + , ) await waitFor(() => UNSAFE_getByType('RNWalletView' as unknown as ComponentType), @@ -159,9 +153,7 @@ test('iOS: mints token then calls addPass with the returned URL', async () => { // Test 6: Android is gated out for iOS-only launch (Google Wallet backend not provisioned) test('Android: renders null until Google Wallet backend is wired', () => { ;(Platform as {OS: string}).OS = 'android' - const {toJSON} = render( - , - ) + const {toJSON} = render() expect(toJSON()).toBeNull() }) @@ -178,7 +170,7 @@ test('addPass throws – error toast and logger.error are called', async () => { const {logger} = require('#/logger') const loggerSpy = jest.spyOn(logger, 'error') const {UNSAFE_getByType} = render( - , + , ) await waitFor(() => UNSAFE_getByType('RNWalletView' as unknown as ComponentType), diff --git a/src/features/inviteFriends/components/AddToWalletButton.tsx b/src/features/inviteFriends/components/AddToWalletButton.tsx index f1eca8a186..480d0f0890 100644 --- a/src/features/inviteFriends/components/AddToWalletButton.tsx +++ b/src/features/inviteFriends/components/AddToWalletButton.tsx @@ -8,17 +8,10 @@ import {useAgent} from '#/state/session' import * as Toast from '#/components/Toast' import {useAnalytics} from '#/analytics' import {IS_WEB} from '#/env' -import {type InviteThemeKey} from '../themes' const BSKY_WEB = 'https://bsky.app' -export function AddToWalletButton({ - themeKey, - handle, -}: { - themeKey: InviteThemeKey - handle: string -}) { +export function AddToWalletButton({handle}: {handle: string}) { const {t: l} = useLingui() const ax = useAnalytics() const agent = useAgent() @@ -64,16 +57,15 @@ export function AddToWalletButton({ Authorization: `Bearer ${accessJwt}`, 'Content-Type': 'application/json', }, - body: JSON.stringify({theme: themeKey}), + body: JSON.stringify({}), }) if (!res.ok) throw new Error(`pass.url ${res.status}`) const {url} = (await res.json()) as {url: string} added = await RNWallet.addPass(url) } else { - const res = await fetch( - `${BSKY_WEB}/invite/wallet/jwt?theme=${themeKey}`, - {headers: {Authorization: `Bearer ${accessJwt}`}}, - ) + const res = await fetch(`${BSKY_WEB}/invite/wallet/jwt`, { + headers: {Authorization: `Bearer ${accessJwt}`}, + }) if (!res.ok) throw new Error(`wallet/jwt ${res.status}`) const {jwt} = (await res.json()) as {jwt: string} added = await RNWallet.addPassWithSignedJwt(jwt)