Skip to content

Commit c2e7ce9

Browse files
committed
Merge branch 'main' into release
2 parents 1451078 + 998623e commit c2e7ce9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+801
-656
lines changed

api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"@paralleldrive/cuid2": "^2.3.1",
2121
"@shared/config-storage-service": "workspace:^",
2222
"@shared/probabilistic-revenue-share": "workspace:^",
23+
"@shared/default-data": "workspace:^",
2324
"@shared/utils": "workspace:^",
2425
"hono": "^4.11.3",
2526
"http-message-signatures": "^1.0.4",

api/src/routes/get-profile.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import { zValidator } from '@hono/zod-validator'
44
import {
55
ConfigStorageService,
66
ConfigStorageServiceError,
7+
isConfigStorageNotFoundError,
78
} from '@shared/config-storage-service'
9+
import { getDefaultProfile } from '@shared/default-data'
810
import { AWS_PREFIX } from '@shared/defines'
911
import {
1012
numberToBannerFontSize,
@@ -14,6 +16,7 @@ import {
1416
} from '@shared/types'
1517
import type {
1618
BaseToolProfile,
19+
Configuration,
1720
ConfigVersions,
1821
ElementConfigType,
1922
Tool,
@@ -44,26 +47,36 @@ app.get(
4447
const storage = new ConfigStorageService({ ...env, AWS_PREFIX })
4548

4649
try {
47-
const fullConfig = await storage.getJson<ConfigVersions>(walletAddress)
48-
const legacyProfile = fullConfig[profileId]
49-
const profile = convertToProfile(legacyProfile, tool)
50-
return json<ToolProfile<typeof tool>>(profile)
50+
let profile: ToolProfile<typeof tool> | null = null
51+
try {
52+
const config = await storage.getJson<Configuration>(walletAddress)
53+
profile = config[tool]?.[profileId] ?? null
54+
} catch (e) {
55+
if (!isConfigStorageNotFoundError(e)) {
56+
throw e
57+
}
58+
// TODO: to be removed after the completion of versioned config migration
59+
const legacy = await storage.getJson<ConfigVersions>(
60+
walletAddress,
61+
true,
62+
)
63+
profile = convertToProfile(legacy[profileId], tool) ?? null
64+
}
65+
66+
if (!profile)
67+
throw new ConfigStorageServiceError(
68+
'not-found',
69+
404,
70+
`No profile found for tool profile ${profileId}`,
71+
)
72+
73+
return json(profile)
5174
} catch (error) {
5275
if (error instanceof HTTPException) throw error
53-
if (error instanceof ConfigStorageServiceError) {
54-
if (error.code === 'not-found') {
55-
const msg = 'No saved profile found for given wallet address'
56-
throw createHTTPException(404, msg, {
57-
message: 'Not found',
58-
code: '404',
59-
cause: {
60-
statusCode: error.statusCode,
61-
code: error.code,
62-
message: error.message,
63-
},
64-
})
65-
}
76+
if (isConfigStorageNotFoundError(error)) {
77+
return json(getDefaultProfile(tool), 404)
6678
}
79+
6780
throw createHTTPException(500, 'Config fetch error: ', error)
6881
}
6982
},
@@ -73,7 +86,10 @@ app.get(
7386
function convertToProfile<T extends Tool>(
7487
config: ElementConfigType,
7588
tool: T,
76-
): ToolProfile<T> {
89+
): ToolProfile<T> | undefined {
90+
// means there is no profile for the given tool/profileId
91+
if (!config) return
92+
7793
if (tool === 'offerwall') {
7894
return config.offerwall as ToolProfile<T>
7995
}

cdn/src/utils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export async function fetchProfile<T extends Tool>(
4848
url.searchParams.set('id', params.profileId)
4949

5050
const res = await fetch(url)
51-
if (!res.ok) {
51+
if (!res.ok && res.status !== 404) {
5252
throw new Error(
5353
`Failed to fetch config: HTTP ${res.status} ${res.statusText}`,
5454
)

components/src/offerwall/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ export class OfferwallModal extends LitElement {
130130
#dialogRef: Ref<HTMLDialogElement> = createRef()
131131
#openDialog() {
132132
if (this.#controller.isPreviewMode) {
133-
this.#dialogRef.value!.show()
133+
const dialog = this.#dialogRef.value!
134+
dialog.inert = true
135+
dialog.show()
136+
dialog.inert = false
134137
return
135138
}
136139
this.#dialogRef.value!.showModal()
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

frontend/app/components/banner/BannerBuilder.tsx

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ import {
2121
import { BannerAnimationSelector } from '~/components/banner/BannerAnimationSelector'
2222
import { BannerPositionSelector } from '~/components/banner/BannerPositionSelector'
2323
import { BannerThumbnailSelector } from '~/components/banner/BannerThumbnailSelector'
24+
import { useBuilderSectionHandlers } from '~/hooks/useBuilderSectionHandlers'
2425
import { useBannerProfile } from '~/stores/banner-store'
25-
import { useUIActions, useUIState } from '~/stores/uiStore'
26+
import type { BuilderSection } from '~/stores/uiStore'
2627

2728
interface Props {
28-
onRefresh: (section: 'content' | 'appearance') => void
29+
onRefresh: (section: BuilderSection) => void
2930
}
3031

3132
const config = {
@@ -56,25 +57,19 @@ export function BannerBuilder({ onRefresh }: Props) {
5657
}
5758

5859
function ContentBuilder({ onRefresh }: Props) {
59-
const uiState = useUIState()
60-
const uiActions = useUIActions()
60+
const { isComplete, isOpen, onClick, onToggle, onDone } =
61+
useBuilderSectionHandlers('content')
6162
const [snap, profile] = useBannerProfile({ sync: true })
6263

6364
return (
6465
<BuilderAccordion
6566
title="Content"
66-
isComplete={uiState.contentComplete}
67-
onToggle={(isOpen) => {
68-
uiActions.setActiveSection(isOpen ? 'content' : null)
69-
if (isOpen) {
70-
uiActions.setContentComplete(true)
71-
}
72-
}}
67+
isComplete={isComplete}
68+
isOpen={isOpen}
69+
onClick={onClick}
70+
onToggle={onToggle}
7371
onRefresh={() => onRefresh('content')}
74-
onDone={() => {
75-
uiActions.setContentComplete(true)
76-
}}
77-
initialIsOpen={uiState.activeSection === 'content'}
72+
onDone={onDone}
7873
>
7974
<TitleInput
8075
value={snap.title.text}
@@ -107,8 +102,8 @@ function ContentBuilder({ onRefresh }: Props) {
107102
}
108103

109104
function AppearanceBuilder({ onRefresh }: Props) {
110-
const uiState = useUIState()
111-
const uiActions = useUIActions()
105+
const { isComplete, isOpen, onClick, onToggle, onDone } =
106+
useBuilderSectionHandlers('appearance')
112107
const [snap, profile] = useBannerProfile()
113108

114109
const defaultFontIndex = FONT_FAMILY_OPTIONS.findIndex(
@@ -118,18 +113,12 @@ function AppearanceBuilder({ onRefresh }: Props) {
118113
return (
119114
<BuilderAccordion
120115
title="Appearance"
121-
isComplete={uiState.appearanceComplete}
122-
onToggle={(isOpen: boolean) => {
123-
uiActions.setActiveSection(isOpen ? 'appearance' : null)
124-
if (isOpen) {
125-
uiActions.setAppearanceComplete(true)
126-
}
127-
}}
116+
isComplete={isComplete}
117+
isOpen={isOpen}
118+
onClick={onClick}
119+
onToggle={onToggle}
128120
onRefresh={() => onRefresh('appearance')}
129-
onDone={() => {
130-
uiActions.setAppearanceComplete(true)
131-
}}
132-
initialIsOpen={uiState.activeSection === 'appearance'}
121+
onDone={onDone}
133122
>
134123
<InputFieldset label="Text" icon={<SVGText className="w-5 h-5" />}>
135124
<ToolsDropdown

frontend/app/components/offerwall/HowItWorks.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import step3 from '~/assets/images/offerwall/illustration_offerwall_step3.svg'
77
const StepArrow = () => (
88
// adapts length to the container
99
<div
10-
className="flex h-[56px] w-[56px] shrink-0 rotate-90 items-center self-center xl:h-auto xl:w-[187px] xl:rotate-0"
10+
className="flex h-[56px] w-[56px] shrink-0 rotate-90 items-center self-center xl:h-auto xl:w-[180px] xl:rotate-0"
1111
aria-hidden="true"
1212
>
1313
<div className="size-[10px] shrink-0 rounded-full bg-[#9CD6CB]" />
@@ -41,7 +41,7 @@ const Step: React.FC<StepProps> = ({
4141
export default function HowItWorks() {
4242
return (
4343
<section
44-
className="flex max-w-[1280px] flex-col gap-2xl rounded-md px-md"
44+
className="flex flex-col gap-2xl rounded-md px-md"
4545
aria-labelledby="how-it-works-heading"
4646
>
4747
<header className="flex flex-col items-center gap-md xl:items-start">
@@ -53,10 +53,8 @@ export default function HowItWorks() {
5353
</Heading4>
5454
<BodyStandard className="text-center xl:text-left">
5555
If you have a Google Ad Manager account you can add Web Monetization
56-
to your Offerwall.
57-
<br />
58-
Customize the user choice screen here, then copy the generated script
59-
and add it to your website.
56+
to your Offerwall. Customize the user choice screen here, then copy
57+
the generated script and add it to your website.
6058
</BodyStandard>
6159
</header>
6260

@@ -76,7 +74,7 @@ export default function HowItWorks() {
7674
/>
7775
<Step
7876
image={step3}
79-
description="Start receiving support as users choose to use WM"
77+
description="Start receiving support as users choose to use Web Monetization"
8078
/>
8179
</ol>
8280
</section>

frontend/app/components/offerwall/OfferwallBuilder.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { useEffect } from 'react'
2+
import { subscribe } from 'valtio'
13
import {
24
Divider,
35
ToolsDropdown,
@@ -14,7 +16,7 @@ import {
1416
import { SVGColorPicker, SVGRoundedCorner, SVGText } from '~/assets/svg'
1517
import { useOfferwallProfile } from '~/stores/offerwall-store'
1618
import { toolActions } from '~/stores/toolStore'
17-
import { useUIActions, useUIState } from '~/stores/uiStore'
19+
import { useUIState } from '~/stores/uiStore'
1820

1921
interface Props {
2022
onRefresh: () => void
@@ -30,29 +32,25 @@ export function OfferwallBuilder({ onRefresh }: Props) {
3032

3133
function AppearanceBuilder({ onRefresh }: Props) {
3234
const uiState = useUIState()
33-
const uiActions = useUIActions()
3435
const [snap, profile] = useOfferwallProfile()
35-
3636
const defaultFontIndex = FONT_FAMILY_OPTIONS.findIndex(
3737
(option) => option === snap.font.name,
3838
)
3939

40+
useEffect(() => {
41+
const unsubscribe = subscribe(profile, () => {
42+
toolActions.setBuildCompleteStep('filled')
43+
})
44+
45+
return unsubscribe
46+
}, [])
47+
4048
return (
4149
<BuilderAccordion
4250
title="Appearance"
4351
isComplete={uiState.appearanceComplete}
44-
onToggle={(isOpen: boolean) => {
45-
if (!isOpen) {
46-
uiActions.setAppearanceComplete(true)
47-
toolActions.setBuildCompleteStep('filled')
48-
}
49-
}}
5052
onRefresh={onRefresh}
51-
onDone={() => {
52-
uiActions.setAppearanceComplete(true)
53-
toolActions.setBuildCompleteStep('filled')
54-
}}
55-
initialIsOpen
53+
isOpen
5654
>
5755
<InputFieldset label="Text" icon={<SVGText className="w-5 h-5" />}>
5856
<ToolsDropdown

frontend/app/components/offerwall/OfferwallPreview.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export default function OfferwallPreview() {
1818
const snap = useSnapshot(toolState)
1919

2020
const setCssVars = (elem: OfferwallModal, profile: OfferwallProfile) => {
21-
applyFontFamily(elem, profile.font.name, TOOL_OFFERWALL, snap.cdnUrl)
21+
const fontBaseUrl = new URL('/assets/fonts/', snap.cdnUrl).href
22+
applyFontFamily(elem, profile.font.name, TOOL_OFFERWALL, fontBaseUrl)
2223

2324
elem.style.setProperty(
2425
'--wm-border-radius',

0 commit comments

Comments
 (0)