feat(invite): drop themeKey prop from AddToWalletButton, share icon assets
This commit is contained in:
@@ -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})
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ export function InviteFriendsDialogInner({
|
||||
|
||||
{isHandleValid && handle ? (
|
||||
<View style={[a.w_full, a.align_center, {marginTop: 24}]}>
|
||||
<AddToWalletButton themeKey={themeKey} handle={handle} />
|
||||
<AddToWalletButton handle={handle} />
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -85,9 +85,7 @@ afterEach(async () => {
|
||||
// Test 1: IS_WEB gate
|
||||
test('renders null on web', async () => {
|
||||
mockIsWeb = true
|
||||
const {toJSON} = render(
|
||||
<AddToWalletButton themeKey="dusk" handle="alice.bsky.social" />,
|
||||
)
|
||||
const {toJSON} = render(<AddToWalletButton handle="alice.bsky.social" />)
|
||||
// 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(
|
||||
<AddToWalletButton themeKey="dusk" handle="alice.bsky.social" />,
|
||||
)
|
||||
const {toJSON} = render(<AddToWalletButton handle="alice.bsky.social" />)
|
||||
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(
|
||||
<AddToWalletButton themeKey="dusk" handle="handle.invalid" />,
|
||||
)
|
||||
const {toJSON} = render(<AddToWalletButton handle="handle.invalid" />)
|
||||
// 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(<AddToWalletButton themeKey="dusk" handle="" />)
|
||||
const {toJSON} = render(<AddToWalletButton handle="" />)
|
||||
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(
|
||||
<AddToWalletButton themeKey="dusk" handle="alice.bsky.social" />,
|
||||
<AddToWalletButton handle="alice.bsky.social" />,
|
||||
)
|
||||
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(
|
||||
<AddToWalletButton themeKey="day" handle="bob.bsky.social" />,
|
||||
)
|
||||
const {toJSON} = render(<AddToWalletButton handle="bob.bsky.social" />)
|
||||
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(
|
||||
<AddToWalletButton themeKey="day" handle="alice.bsky.social" />,
|
||||
<AddToWalletButton handle="alice.bsky.social" />,
|
||||
)
|
||||
await waitFor(() =>
|
||||
UNSAFE_getByType('RNWalletView' as unknown as ComponentType),
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user