Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,6 @@ NEXT_PUBLIC_AI_CREDITS_WIDGET_DASHBOARD_ENABLED=false

# Hide the dashboard button while keeping /{locale}/superfluid-campaign accessible.
NEXT_PUBLIC_SUPERFLUID_CAMPAIGN_WIDGET_DASHBOARD_ENABLED=false

# Hide the dashboard button while keeping /{locale}/streaming accessible.
NEXT_PUBLIC_STREAMING_WIDGET_DASHBOARD_ENABLED=false
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ The Superfluid button follows the same pattern with
`NEXT_PUBLIC_SUPERFLUID_CAMPAIGN_WIDGET_DASHBOARD_ENABLED`; its authenticated
`/{locale}/superfluid-campaign` path remains available when the button is hidden.

The Streaming button follows the same pattern with
`NEXT_PUBLIC_STREAMING_WIDGET_DASHBOARD_ENABLED`; its authenticated
`/{locale}/streaming` path remains available when the button is hidden.

## GoodWidget local-tarball workflow

Widget packages that are not yet published to npm (e.g. `@goodwidget/ai-credits-widget`)
Expand Down
1 change: 1 addition & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const nextConfig = {
"@goodwidget/citizen-claim-widget",
"@goodwidget/ai-credits-widget",
"@goodwidget/goodreserve-widget",
"@goodwidget/streaming-widget",
"@goodwidget/core",
"@goodwidget/embed",
"@goodwidget/ui",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@goodwidget/core": "0.1.5",
"@goodwidget/embed": "0.1.4",
"@goodwidget/goodreserve-widget": "0.1.7",
"@goodwidget/streaming-widget": "0.1.1",
"@goodwidget/superfluid-campaign-widget": "0.1.7",
"@goodwidget/ui": "0.1.6",
"@hcaptcha/react-hcaptcha": "^2.0.2",
Expand Down
4 changes: 4 additions & 0 deletions src/analytics/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export enum AnalyticsEventTypes {
AICreditsTabSelected = "AICredits_Tab_Selected",
GoodReserveTabSelected = "GoodReserve_Tab_Selected",
SuperfluidCampaignTabSelected = "SuperfluidCampaign_Tab_Selected",
StreamingTabSelected = "Streaming_Tab_Selected",
}

export enum AvailableMethodsNames {
Expand Down Expand Up @@ -273,3 +274,6 @@ export type AnalyticsEvent =
| {
type: AnalyticsEventTypes.SuperfluidCampaignTabSelected
}
| {
type: AnalyticsEventTypes.StreamingTabSelected
}
2 changes: 2 additions & 0 deletions src/sections/Home/components/WalletSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ type WidgetTabSelectedEventType =
| AnalyticsEventTypes.AICreditsTabSelected
| AnalyticsEventTypes.GoodReserveTabSelected
| AnalyticsEventTypes.SuperfluidCampaignTabSelected
| AnalyticsEventTypes.StreamingTabSelected

const widgetActionEventType: Record<string, WidgetTabSelectedEventType> = {
"goodwidget.ai-credits": AnalyticsEventTypes.AICreditsTabSelected,
"goodwidget.goodreserve": AnalyticsEventTypes.GoodReserveTabSelected,
"goodwidget.superfluid-campaign":
AnalyticsEventTypes.SuperfluidCampaignTabSelected,
"goodwidget.streaming": AnalyticsEventTypes.StreamingTabSelected,
}

export default function WalletSection({
Expand Down
3 changes: 2 additions & 1 deletion src/ui/assets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {
import { FaCaretDown, FaCaretUp } from "react-icons/fa"
import { HiDotsHorizontal } from "react-icons/hi"
import { IoDocumentOutline } from "react-icons/io5"
import { LuSparkles } from "react-icons/lu"
import { LuSparkles, LuWaves } from "react-icons/lu"
import { MdOutlineContactSupport, MdOutlinePrivacyTip } from "react-icons/md"
import { RiArrowDownLine, RiArrowUpLine } from "react-icons/ri"
import { TbCube3dSphere, TbLogout } from "react-icons/tb"
Expand Down Expand Up @@ -121,6 +121,7 @@ export const UIIcons = {
Cash: BsCashStack,
Legal: IoDocumentOutline,
Sparkles: LuSparkles,
Waves: LuWaves,
Logout: TbLogout,
Privacy: MdOutlinePrivacyTip,
Questionmark: BsFillQuestionCircleFill,
Expand Down
14 changes: 14 additions & 0 deletions src/widgets/fixtures/streamingWidgetRegisterMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Vitest mock for `@goodwidget/streaming-widget/register`.
*
* The real module performs a Custom Element side-effect registration which
* requires a browser DOM. This no-op stub satisfies the import during unit
* tests without triggering DOM APIs.
*/
export const goodWidgetMetadata = {
packageName: "@goodwidget/streaming-widget",
packageVersion: "0.1.1",
}

export const register = async (tagName?: string): Promise<string> =>
tagName ?? "gw-streaming"
53 changes: 53 additions & 0 deletions src/widgets/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,57 @@ describe("widget registry", () => {
])
})
})

describe("Streaming widget", () => {
it("is present in the live WIDGETS array with the correct widgetId", () => {
const streaming = WIDGETS.find(
(w) => w.widgetId === "goodwidget.streaming",
)
expect(streaming).toBeDefined()
expect(streaming).toMatchObject({
widgetId: "goodwidget.streaming",
packageName: "@goodwidget/streaming-widget",
packageVersion: "0.1.1",
routeSlug: "streaming",
entry: { tagName: "gw-streaming" },
providerPolicy: {
chainIds: [42220, 8453],
},
})
})

it("passes registry validation (createWidgetRegistry does not throw)", () => {
const streaming = WIDGETS.find(
(w) => w.widgetId === "goodwidget.streaming",
)
expect(streaming).toBeDefined()
if (!streaming) return
expect(() => createWidgetRegistry([streaming])).not.toThrow()
})

it("appears in widgetDashboardActions only when dashboardVisible", () => {
const streaming = WIDGETS.find(
(w) => w.widgetId === "goodwidget.streaming",
)
const action = widgetDashboardActions.find(
(a) => a.widgetId === "goodwidget.streaming",
)
if (streaming?.dashboardVisible === false) {
expect(action).toBeUndefined()
} else {
expect(action).toBeDefined()
expect(action?.routeSlug).toBe("streaming")
expect(action?.label).toBe("Streaming")
}
})

it("uses the shared provider method allowlist", () => {
const streaming = WIDGETS.find(
(w) => w.widgetId === "goodwidget.streaming",
)
expect(streaming?.providerPolicy.requiredMethods).toEqual([
...WIDGET_PROVIDER_METHOD_LIST,
])
})
})
})
35 changes: 34 additions & 1 deletion src/widgets/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,43 @@ const goodReserveWidget = defineWidget({
},
})

/**
* Superfluid streaming widget: money streams, GDA pool memberships, and Super
* Token balances. The package registers its Custom Element as a side effect
* when its register entry is imported.
*/
const streamingWidget = defineWidget({
widgetId: "goodwidget.streaming",
packageName: "@goodwidget/streaming-widget",
packageVersion: "0.1.1",
routeSlug: "streaming",
displayName: "Streaming",
description: "Manage money streams, pools, and Super Token balances",
dashboardVisible: readBooleanEnv(
process.env.NEXT_PUBLIC_STREAMING_WIDGET_DASHBOARD_ENABLED,
false,
),
icon: { kind: "system", name: "Waves" },
integrationMode: "web-component",
entry: {
tagName: "gw-streaming",
load: () => import("@goodwidget/streaming-widget/register"),
},
providerPolicy: {
chainIds: [CELO_CHAIN_ID, BASE_CHAIN_ID],
requiredMethods: WIDGET_PROVIDER_METHOD_LIST,
},
})

export const WIDGETS: readonly RegisteredWidget[] =
process.env.NEXT_PUBLIC_PLAYWRIGHT_TEST_MODE === "true"
? [testFixtureWidget]
: [superfluidCampaignWidget, aiCreditsWidget, goodReserveWidget]
: [
superfluidCampaignWidget,
aiCreditsWidget,
goodReserveWidget,
streamingWidget,
]
export const widgetRegistry = createWidgetRegistry(WIDGETS)

export const getWidgetByRoute = (
Expand Down
5 changes: 5 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export default defineConfig({
__dirname,
"./src/widgets/fixtures/aiCreditsWidgetRegisterMock.ts",
),
// Same for the Streaming widget register module.
"@goodwidget/streaming-widget/register": path.resolve(
__dirname,
"./src/widgets/fixtures/streamingWidgetRegisterMock.ts",
),
},
},
})
78 changes: 78 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,19 @@ __metadata:
languageName: node
linkType: hard

"@goodsdks/streaming-sdk@npm:1.1.0":
version: 1.1.0
resolution: "@goodsdks/streaming-sdk@npm:1.1.0"
dependencies:
"@sfpro/sdk": "npm:^0.1.0"
graphql: "npm:^16.9.0"
graphql-request: "npm:^7.1.2"
peerDependencies:
viem: "*"
checksum: 10c0/ed7236e787a47474308d0950342c67c9cfdc9d68bc7aa01464e7bd0bbc08ee97c6f880a2e7e9355dd09d2824885eef0a7e3868f5b7607c7aa6773b0fc8658538
languageName: node
linkType: hard

"@goodwidget/ai-credits-widget@npm:0.1.12":
version: 0.1.12
resolution: "@goodwidget/ai-credits-widget@npm:0.1.12"
Expand Down Expand Up @@ -1028,6 +1041,22 @@ __metadata:
languageName: node
linkType: hard

"@goodwidget/streaming-widget@npm:0.1.1":
version: 0.1.1
resolution: "@goodwidget/streaming-widget@npm:0.1.1"
dependencies:
"@goodsdks/streaming-sdk": "npm:1.1.0"
"@goodwidget/core": "npm:0.1.5"
"@goodwidget/embed": "npm:0.1.4"
"@goodwidget/ui": "npm:0.1.6"
viem: "npm:^2.0.0"
peerDependencies:
react: ">=18.0.0"
react-dom: ">=18.0.0"
checksum: 10c0/736eed474360e938a07e8a4d27dafabe0baa49e0dcd9323da2370cd73a719a01f1ade4363a7e2d72a635cc85de309baa3e40b00a96b79702e2657e20d70f98bd
languageName: node
linkType: hard

"@goodwidget/superfluid-campaign-widget@npm:0.1.7":
version: 0.1.7
resolution: "@goodwidget/superfluid-campaign-widget@npm:0.1.7"
Expand Down Expand Up @@ -1119,6 +1148,15 @@ __metadata:
languageName: node
linkType: hard

"@graphql-typed-document-node/core@npm:^3.2.0":
version: 3.2.0
resolution: "@graphql-typed-document-node/core@npm:3.2.0"
peerDependencies:
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10c0/94e9d75c1f178bbae8d874f5a9361708a3350c8def7eaeb6920f2c820e82403b7d4f55b3735856d68e145e86c85cbfe2adc444fdc25519cd51f108697e99346c
languageName: node
linkType: hard

"@hcaptcha/react-hcaptcha@npm:^2.0.2":
version: 2.0.2
resolution: "@hcaptcha/react-hcaptcha@npm:2.0.2"
Expand Down Expand Up @@ -3160,6 +3198,27 @@ __metadata:
languageName: node
linkType: hard

"@sfpro/sdk@npm:^0.1.0":
version: 0.1.9
resolution: "@sfpro/sdk@npm:0.1.9"
peerDependencies:
"@wagmi/core": ^2
react: ">=18"
viem: ^2
wagmi: ^2
peerDependenciesMeta:
"@wagmi/core":
optional: true
react:
optional: true
viem:
optional: true
wagmi:
optional: true
checksum: 10c0/b800a428948d18674fb9ca2a1cec013cd56cdfd844f9b859249d7c174d1f4f99a2eafe74f542f4c11122c7c049389f68d5bbdc72278bcbbbb89586379034abe7
languageName: node
linkType: hard

"@signinwithethereum/siwe-parser@npm:^4.2.0":
version: 4.2.0
resolution: "@signinwithethereum/siwe-parser@npm:4.2.0"
Expand Down Expand Up @@ -10050,6 +10109,7 @@ __metadata:
"@goodwidget/core": "npm:0.1.5"
"@goodwidget/embed": "npm:0.1.4"
"@goodwidget/goodreserve-widget": "npm:0.1.7"
"@goodwidget/streaming-widget": "npm:0.1.1"
"@goodwidget/superfluid-campaign-widget": "npm:0.1.7"
"@goodwidget/ui": "npm:0.1.6"
"@hcaptcha/react-hcaptcha": "npm:^2.0.2"
Expand Down Expand Up @@ -10132,6 +10192,24 @@ __metadata:
languageName: node
linkType: hard

"graphql-request@npm:^7.1.2":
version: 7.4.0
resolution: "graphql-request@npm:7.4.0"
dependencies:
"@graphql-typed-document-node/core": "npm:^3.2.0"
peerDependencies:
graphql: 14 - 16
checksum: 10c0/066531d70b9c4656251e51c950ce9bee3df05291e12e2b983608d75ac3a70d700efd6488d273b043235dc125658973db38b21eba59e808863831a5a19a6a7b41
languageName: node
linkType: hard

"graphql@npm:^16.9.0":
version: 16.14.2
resolution: "graphql@npm:16.14.2"
checksum: 10c0/a95a96961eaff55cc9fe9d31fae6f33499ac988b972d07ea5085024cb1333f515b902f376e7393a5489aa82200a8aff3eb96580e4d1b69d702ed19b6eb1ce97a
languageName: node
linkType: hard

"h3@npm:^1.15.10":
version: 1.15.11
resolution: "h3@npm:1.15.11"
Expand Down
Loading