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) {
|
func (srv *Server) buildPassAssets(theme, handle string, avatarBytes []byte) ([]PassAsset, error) {
|
||||||
icon1, err := readFS(srv.cfg.InvitePass.StripFS, "passes/"+theme+"/icon.png")
|
_ = theme
|
||||||
if err != nil {
|
// Icons are shared across themes (matches the Pass Designer layout - the
|
||||||
return nil, fmt.Errorf("icon: %w", err)
|
// 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")
|
logo1, err := readFS(srv.cfg.InvitePass.StripFS, "passes/logo.png")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("logo: %w", err)
|
return nil, fmt.Errorf("logo: %w", err)
|
||||||
@@ -247,9 +250,11 @@ func (srv *Server) buildPassAssets(theme, handle string, avatarBytes []byte) ([]
|
|||||||
_ = avatarBytes
|
_ = avatarBytes
|
||||||
|
|
||||||
assets := []PassAsset{
|
assets := []PassAsset{
|
||||||
{Name: "icon.png", Data: icon1},
|
|
||||||
{Name: "logo.png", Data: logo1},
|
{Name: "logo.png", Data: logo1},
|
||||||
}
|
}
|
||||||
|
if len(icon1) > 0 {
|
||||||
|
assets = append(assets, PassAsset{Name: "icon.png", Data: icon1})
|
||||||
|
}
|
||||||
if len(icon2) > 0 {
|
if len(icon2) > 0 {
|
||||||
assets = append(assets, PassAsset{Name: "icon@2x.png", Data: icon2})
|
assets = append(assets, PassAsset{Name: "icon@2x.png", Data: icon2})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ export function InviteFriendsDialogInner({
|
|||||||
|
|
||||||
{isHandleValid && handle ? (
|
{isHandleValid && handle ? (
|
||||||
<View style={[a.w_full, a.align_center, {marginTop: 24}]}>
|
<View style={[a.w_full, a.align_center, {marginTop: 24}]}>
|
||||||
<AddToWalletButton themeKey={themeKey} handle={handle} />
|
<AddToWalletButton handle={handle} />
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|||||||
@@ -85,9 +85,7 @@ afterEach(async () => {
|
|||||||
// Test 1: IS_WEB gate
|
// Test 1: IS_WEB gate
|
||||||
test('renders null on web', async () => {
|
test('renders null on web', async () => {
|
||||||
mockIsWeb = true
|
mockIsWeb = true
|
||||||
const {toJSON} = render(
|
const {toJSON} = render(<AddToWalletButton handle="alice.bsky.social" />)
|
||||||
<AddToWalletButton themeKey="dusk" handle="alice.bsky.social" />,
|
|
||||||
)
|
|
||||||
// Flush effects - with IS_WEB=true the canAddPasses effect exits early,
|
// Flush effects - with IS_WEB=true the canAddPasses effect exits early,
|
||||||
// so the flush is just a guard.
|
// so the flush is just a guard.
|
||||||
await act(async () => {})
|
await act(async () => {})
|
||||||
@@ -97,9 +95,7 @@ test('renders null on web', async () => {
|
|||||||
// Test 2: canAddPasses gate
|
// Test 2: canAddPasses gate
|
||||||
test('renders null when canAddPasses() resolves false', async () => {
|
test('renders null when canAddPasses() resolves false', async () => {
|
||||||
RNWallet.canAddPasses.mockResolvedValue(false)
|
RNWallet.canAddPasses.mockResolvedValue(false)
|
||||||
const {toJSON} = render(
|
const {toJSON} = render(<AddToWalletButton handle="alice.bsky.social" />)
|
||||||
<AddToWalletButton themeKey="dusk" handle="alice.bsky.social" />,
|
|
||||||
)
|
|
||||||
await waitFor(() => expect(RNWallet.canAddPasses).toHaveBeenCalled())
|
await waitFor(() => expect(RNWallet.canAddPasses).toHaveBeenCalled())
|
||||||
// Flush promise resolution so setCanAdd(false) runs inside act
|
// Flush promise resolution so setCanAdd(false) runs inside act
|
||||||
await act(async () => {})
|
await act(async () => {})
|
||||||
@@ -108,9 +104,7 @@ test('renders null when canAddPasses() resolves false', async () => {
|
|||||||
|
|
||||||
// Test 3: handle sentinel gate
|
// Test 3: handle sentinel gate
|
||||||
test('renders null when handle is invalid', async () => {
|
test('renders null when handle is invalid', async () => {
|
||||||
const {toJSON} = render(
|
const {toJSON} = render(<AddToWalletButton handle="handle.invalid" />)
|
||||||
<AddToWalletButton themeKey="dusk" handle="handle.invalid" />,
|
|
||||||
)
|
|
||||||
// handle is invalid so canAddPasses is not called; flush anyway for safety
|
// handle is invalid so canAddPasses is not called; flush anyway for safety
|
||||||
await act(async () => {})
|
await act(async () => {})
|
||||||
expect(toJSON()).toBeNull()
|
expect(toJSON()).toBeNull()
|
||||||
@@ -118,7 +112,7 @@ test('renders null when handle is invalid', async () => {
|
|||||||
|
|
||||||
// Test 4: empty handle gate
|
// Test 4: empty handle gate
|
||||||
test('renders null when handle is empty', async () => {
|
test('renders null when handle is empty', async () => {
|
||||||
const {toJSON} = render(<AddToWalletButton themeKey="dusk" handle="" />)
|
const {toJSON} = render(<AddToWalletButton handle="" />)
|
||||||
await act(async () => {})
|
await act(async () => {})
|
||||||
expect(toJSON()).toBeNull()
|
expect(toJSON()).toBeNull()
|
||||||
})
|
})
|
||||||
@@ -133,7 +127,7 @@ test('iOS: mints token then calls addPass with the returned URL', async () => {
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
const {UNSAFE_getByType} = render(
|
const {UNSAFE_getByType} = render(
|
||||||
<AddToWalletButton themeKey="dusk" handle="alice.bsky.social" />,
|
<AddToWalletButton handle="alice.bsky.social" />,
|
||||||
)
|
)
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
UNSAFE_getByType('RNWalletView' as unknown as ComponentType),
|
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 6: Android is gated out for iOS-only launch (Google Wallet backend not provisioned)
|
||||||
test('Android: renders null until Google Wallet backend is wired', () => {
|
test('Android: renders null until Google Wallet backend is wired', () => {
|
||||||
;(Platform as {OS: string}).OS = 'android'
|
;(Platform as {OS: string}).OS = 'android'
|
||||||
const {toJSON} = render(
|
const {toJSON} = render(<AddToWalletButton handle="bob.bsky.social" />)
|
||||||
<AddToWalletButton themeKey="day" handle="bob.bsky.social" />,
|
|
||||||
)
|
|
||||||
expect(toJSON()).toBeNull()
|
expect(toJSON()).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -178,7 +170,7 @@ test('addPass throws – error toast and logger.error are called', async () => {
|
|||||||
const {logger} = require('#/logger')
|
const {logger} = require('#/logger')
|
||||||
const loggerSpy = jest.spyOn(logger, 'error')
|
const loggerSpy = jest.spyOn(logger, 'error')
|
||||||
const {UNSAFE_getByType} = render(
|
const {UNSAFE_getByType} = render(
|
||||||
<AddToWalletButton themeKey="day" handle="alice.bsky.social" />,
|
<AddToWalletButton handle="alice.bsky.social" />,
|
||||||
)
|
)
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
UNSAFE_getByType('RNWalletView' as unknown as ComponentType),
|
UNSAFE_getByType('RNWalletView' as unknown as ComponentType),
|
||||||
|
|||||||
@@ -8,17 +8,10 @@ import {useAgent} from '#/state/session'
|
|||||||
import * as Toast from '#/components/Toast'
|
import * as Toast from '#/components/Toast'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
import {IS_WEB} from '#/env'
|
import {IS_WEB} from '#/env'
|
||||||
import {type InviteThemeKey} from '../themes'
|
|
||||||
|
|
||||||
const BSKY_WEB = 'https://bsky.app'
|
const BSKY_WEB = 'https://bsky.app'
|
||||||
|
|
||||||
export function AddToWalletButton({
|
export function AddToWalletButton({handle}: {handle: string}) {
|
||||||
themeKey,
|
|
||||||
handle,
|
|
||||||
}: {
|
|
||||||
themeKey: InviteThemeKey
|
|
||||||
handle: string
|
|
||||||
}) {
|
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
@@ -64,16 +57,15 @@ export function AddToWalletButton({
|
|||||||
Authorization: `Bearer ${accessJwt}`,
|
Authorization: `Bearer ${accessJwt}`,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({theme: themeKey}),
|
body: JSON.stringify({}),
|
||||||
})
|
})
|
||||||
if (!res.ok) throw new Error(`pass.url ${res.status}`)
|
if (!res.ok) throw new Error(`pass.url ${res.status}`)
|
||||||
const {url} = (await res.json()) as {url: string}
|
const {url} = (await res.json()) as {url: string}
|
||||||
added = await RNWallet.addPass(url)
|
added = await RNWallet.addPass(url)
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch(
|
const res = await fetch(`${BSKY_WEB}/invite/wallet/jwt`, {
|
||||||
`${BSKY_WEB}/invite/wallet/jwt?theme=${themeKey}`,
|
headers: {Authorization: `Bearer ${accessJwt}`},
|
||||||
{headers: {Authorization: `Bearer ${accessJwt}`}},
|
})
|
||||||
)
|
|
||||||
if (!res.ok) throw new Error(`wallet/jwt ${res.status}`)
|
if (!res.ok) throw new Error(`wallet/jwt ${res.status}`)
|
||||||
const {jwt} = (await res.json()) as {jwt: string}
|
const {jwt} = (await res.json()) as {jwt: string}
|
||||||
added = await RNWallet.addPassWithSignedJwt(jwt)
|
added = await RNWallet.addPassWithSignedJwt(jwt)
|
||||||
|
|||||||
Reference in New Issue
Block a user