Skip to content
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e5d0672
Initial commit for React Router v7 update
kjmitchelljr Oct 16, 2025
d8fb2f7
Merge branch 'main' into 129-refactor-frontend-update-remix-to-react-…
kjmitchelljr Oct 16, 2025
70344a4
Fix typecheck
kjmitchelljr Oct 16, 2025
713a362
Fix formatting issues
kjmitchelljr Oct 16, 2025
f730aaf
Fix issue w/ build process
kjmitchelljr Oct 20, 2025
1b9ed5d
Commit pnpm-lock
kjmitchelljr Oct 20, 2025
c272381
Merge branch 'main' into 129-refactor-frontend-update-remix-to-react-…
kjmitchelljr Oct 20, 2025
2b80677
Fix merge errors
kjmitchelljr Oct 20, 2025
7f34159
Add build directory as output
kjmitchelljr Oct 20, 2025
903da2c
Revert to custom build import
kjmitchelljr Oct 20, 2025
dc17ca9
Want to see virtual react router issue
kjmitchelljr Oct 20, 2025
5a37c1c
Test for dev and production env
kjmitchelljr Oct 20, 2025
ac1d262
Add ts directive
kjmitchelljr Oct 20, 2025
37e36b0
Remove ts directive
kjmitchelljr Oct 20, 2025
457d75a
Revert uiStore changes
kjmitchelljr Oct 20, 2025
df4b088
Getting error for useRef = reintroduce change in uiStore
kjmitchelljr Oct 20, 2025
0933a47
Fixes the tool routes
kjmitchelljr Oct 21, 2025
270b536
With React Router this the redirect is no longer required in dev
kjmitchelljr Oct 21, 2025
cd60b39
Testing to see if it works on cloudflare runner
kjmitchelljr Oct 21, 2025
1225dd1
Remove crypto alias to resolve CommonJS issue
kjmitchelljr Oct 22, 2025
35dc016
Merge branch 'main' into 129-refactor-frontend-update-remix-to-react-…
kjmitchelljr Oct 22, 2025
b225ac2
No longer seeing the error
kjmitchelljr Oct 22, 2025
1f499b3
favicon.ico not needed
kjmitchelljr Oct 22, 2025
994f9e7
Combine into one import
kjmitchelljr Oct 22, 2025
2c4c83d
Update vite-tsconfig-paths package
kjmitchelljr Oct 23, 2025
d0bd5ef
Fix potential circular dependency on build
kjmitchelljr Oct 23, 2025
872cbbf
Will add in separate PR
kjmitchelljr Oct 23, 2025
115bee1
Add back crypto alias
kjmitchelljr Oct 23, 2025
ce47a85
Merge main into react-router-v7 and resolve conflicts
kjmitchelljr Oct 24, 2025
2bb4a3c
Fix payment-confirmation and wrangler file typechecks
kjmitchelljr Oct 24, 2025
697a92e
Update response.json() references with data()
kjmitchelljr Oct 24, 2025
a8b33f1
fix typecheck
kjmitchelljr Oct 24, 2025
8a9bce5
Re add persistState
kjmitchelljr Oct 24, 2025
69c6d07
Wrap previous json args in data
kjmitchelljr Oct 24, 2025
5d2eef0
fixes the crypto issue on local dev
kjmitchelljr Oct 24, 2025
1847fa6
PR changes and persist state check
kjmitchelljr Oct 29, 2025
e98e395
Refactor Vite config to use concise syntax
sidvishnoi Oct 30, 2025
32adaa8
Move type generation to package.json
kjmitchelljr Oct 30, 2025
58a85a0
Merge branch 'main' into 129-refactor-frontend-update-remix-to-react-…
DarianM Oct 31, 2025
06429fd
exclude .react-router type from liting; exclude .wrangler dirs from l…
DarianM Oct 31, 2025
081ebf1
react linting only to frontend and components
DarianM Oct 31, 2025
8eea4ff
Merge branch 'main' into 129-refactor-frontend-update-remix-to-react-…
DarianM Oct 31, 2025
2451ac8
correctly persist wrangler dir path
DarianM Oct 31, 2025
46f0fc5
Update routes and change to ts file
kjmitchelljr Nov 3, 2025
2232c58
Update vite-tsconfig-paths
kjmitchelljr Nov 3, 2025
c9314a0
Update to pnpm-lock
kjmitchelljr Nov 10, 2025
2ad4aef
Merge branch 'main' into 129-refactor-frontend-update-remix-to-react-…
kjmitchelljr Nov 10, 2025
022356d
Redo lock file after merge with main
kjmitchelljr Nov 10, 2025
2f6a61d
Wrap return in data and package updates
kjmitchelljr Nov 12, 2025
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ build
.cache

# macOS
.DS_Store
.DS_Store

# React Router
.react-router/
10 changes: 9 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default [
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{
files: ['frontend/**/*.{ts,tsx}', 'components/**/*.{ts,tsx}'],
...pluginReact.configs.flat.recommended,
settings: {
react: {
Expand All @@ -40,6 +41,13 @@ export default [
}
},
{
ignores: ['**/node_modules/', '**/dist/', '**/build/', '**/public/init.js']
ignores: [
'**/node_modules/',
'**/dist/',
'**/build/',
'**/public/init.js',
'**/.react-router/',
'**/.wrangler/'
]
}
]
2 changes: 1 addition & 1 deletion frontend/app/components/ButtonOrLink.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Link, type LinkProps } from '@remix-run/react'
import { Link, type LinkProps } from 'react-router'
import { forwardRef, type ComponentProps } from 'react'

type AnchorOrLinkProps = ComponentProps<'a'> & Partial<LinkProps>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import { useSnapshot } from 'valtio'
import { cx } from 'class-variance-authority'
import { toolState } from '~/stores/toolStore'
import { ToolsSecondaryButton } from '@/components'
import { ToolsSecondaryButton } from '@/components/ToolsSecondaryButton'
import { SLIDE_ANIMATION } from '@shared/types'

const DOT_PATTERN_SVG = `<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" r="2" fill="white" fill-opacity="0.5" /></svg>`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { GhostButton } from '@/components'
import { GhostButton } from '@/components/GhostButton'
import { Heading1, Heading2SemiBold } from '@/typography'
import { SVGArrowLeft } from '@/assets'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useId } from 'react'
import { Link } from '@remix-run/react'
import { Link } from 'react-router'
import { PillTag } from '@/components'
import arrowOutwardIcon from '~/assets/images/landing/arrow-outward.svg'

Expand Down
4 changes: 2 additions & 2 deletions frontend/app/entry.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* For more information, see https://remix.run/file-conventions/entry.client
*/

import { RemixBrowser } from '@remix-run/react'
import { HydratedRouter } from 'react-router/dom'
import { startTransition, StrictMode } from 'react'
import { hydrateRoot } from 'react-dom/client'
import { scan } from 'react-scan'
Expand All @@ -22,7 +22,7 @@ startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
<HydratedRouter />
</StrictMode>
)
})
19 changes: 9 additions & 10 deletions frontend/app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,31 @@
* For more information, see https://remix.run/file-conventions/entry.server
*/

import type { AppLoadContext, EntryContext } from '@remix-run/cloudflare'
import { RemixServer } from '@remix-run/react'
import {
ServerRouter,
type AppLoadContext,
type EntryContext
} from 'react-router'
import { isbot } from 'isbot'
import { renderToReadableStream } from 'react-dom/server'

const ABORT_DELAY = 5000
export const streamTimeout = 5000

export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
reactRouterContext: EntryContext,
// This is ignored so we can keep it in the template for visibility. Feel
// free to delete this parameter in your app if you're not using it!
// eslint-disable-next-line @typescript-eslint/no-unused-vars
loadContext: AppLoadContext
) {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), ABORT_DELAY)
const timeoutId = setTimeout(() => controller.abort(), streamTimeout)

const body = await renderToReadableStream(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={reactRouterContext} url={request.url} />,
{
signal: controller.signal,
onError(error: unknown) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/hooks/usePathTracker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useLocation } from '@remix-run/react'
import { useLocation } from 'react-router'
import { useEffect } from 'react'
import { TOOL_TYPES, toolActions, type ToolType } from '~/stores/toolStore'

Expand Down
7 changes: 4 additions & 3 deletions frontend/app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { LinksFunction, MetaFunction } from '@remix-run/cloudflare'
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useRouteError,
isRouteErrorResponse
} from '@remix-run/react'
isRouteErrorResponse,
type LinksFunction,
type MetaFunction
} from 'react-router'
import type { ReactNode } from 'react'
import stylesheet from '~/tailwind.css?url'
import { Button } from './components/index.js'
Expand Down
15 changes: 15 additions & 0 deletions frontend/app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { type RouteConfig, index, route } from '@react-router/dev/routes'

export default [
// Root index route
index('routes/_index.tsx'),
// Tool routes
route('banner/', 'routes/banner.tsx'),
route('link-tag/', 'routes/link-tag.tsx'),
route('payment-confirmation/', 'routes/payment-confirmation.tsx'),
route('prob-revshare/', 'routes/prob-revshare.tsx'),
route('widget/', 'routes/widget.tsx'),
// API routes
route('api/config/:type', 'routes/api.config.$type.ts'),
route('api/grant/:type', 'routes/api.grant.$type.ts')
] satisfies RouteConfig
2 changes: 1 addition & 1 deletion frontend/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MetaFunction } from '@remix-run/cloudflare'
import type { MetaFunction } from 'react-router'
import { ToolCard } from '~/components/redesign/components/landing/ToolCard'
import { Heading1, Heading3 } from '~/components/redesign/Typography'
import SVGLinkGenerator from '~/assets/images/landing/illustration_link_generator.svg?url'
Expand Down
49 changes: 23 additions & 26 deletions frontend/app/routes/api.config.$type.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {
json,
type ActionFunctionArgs,
type LoaderFunctionArgs
} from '@remix-run/cloudflare'
import { data } from 'react-router'
import type { Route } from './+types/api.config.$type'
import { getDefaultData } from '@shared/default-data'
import { filterDeepProperties } from '~/utils/utils.server.js'
import { sanitizeConfigFields } from '~/utils/sanitize.server.js'
Expand All @@ -16,7 +13,7 @@ import { APP_BASEPATH } from '~/lib/constants.js'
import { AWS_PREFIX } from '@shared/defines'
import { getWalletAddress, normalizeWalletAddress } from '@shared/utils'

export async function loader({ request, params, context }: LoaderFunctionArgs) {
export async function loader({ request, params, context }: Route.LoaderArgs) {
try {
const { env } = context.cloudflare
const url = new URL(request.url)
Expand All @@ -36,7 +33,7 @@ export async function loader({ request, params, context }: LoaderFunctionArgs) {
errors.fieldErrors = result.error?.flatten().fieldErrors || {
walletAddress: undefined
}
return json({ errors, success: false }, { status: 400 })
return data({ errors, success: false }, { status: 400 })
}

const ownerWalletAddress = normalizeWalletAddress(
Expand All @@ -50,17 +47,17 @@ export async function loader({ request, params, context }: LoaderFunctionArgs) {
let fileContent = Object.assign({}, fileContentString)
fileContent = filterDeepProperties(fileContent) as ConfigVersions

return json(fileContent)
return data(fileContent)
} catch (error) {
// @ts-expect-error TODO
if (error.name === 'NoSuchKey' || error.message.includes('404')) {
// no user config exists for this wallet address - return empty response
return json({})
return {}
}
throw error
}
} catch (error) {
return json(
return data(
{
error: `An error occurred while fetching data: ${(error as Error).message}`
},
Expand All @@ -69,14 +66,14 @@ export async function loader({ request, params, context }: LoaderFunctionArgs) {
}
}

export async function action({ request, params, context }: ActionFunctionArgs) {
export async function action({ request, params, context }: Route.ActionArgs) {
const { env } = context.cloudflare
const elementType = params.type

const formData = await request.formData()
const entries = Object.fromEntries(formData.entries())
if (!entries.walletAddress) {
return json(
return data(
{
errors: { fieldErrors: { walletAddress: 'Wallet address is required' } }
},
Expand All @@ -95,7 +92,7 @@ export async function action({ request, params, context }: ActionFunctionArgs) {
errors.fieldErrors = result.error?.flatten().fieldErrors || {
walletAddress: undefined
}
return json({ message, errors, success: false, intent }, { status: 400 })
return data({ message, errors, success: false, intent }, { status: 400 })
}

let ownerWalletAddress: string = payload.walletAddress
Expand All @@ -115,7 +112,7 @@ export async function action({ request, params, context }: ActionFunctionArgs) {
})
session.set('payment-grant', grant)

return json(
return data(
{
errors,
grantRequired: grant.interact.redirect,
Expand All @@ -133,7 +130,7 @@ export async function action({ request, params, context }: ActionFunctionArgs) {
errors.fieldErrors = {
walletAddress: ['Could not verify ownership of wallet address']
}
return json({ errors }, { status: 500 })
return data({ errors }, { status: 500 })
}
}

Expand All @@ -150,7 +147,7 @@ export async function action({ request, params, context }: ActionFunctionArgs) {
return handleDelete(storageService, formData, ownerWalletAddress)

default:
return json({ error: 'Method not allowed' }, { status: 405 })
return data({ error: 'Method not allowed' }, { status: 405 })
}
}

Expand All @@ -163,7 +160,7 @@ async function handleCreate(
const version = formData.get('version') as string

if (!version) {
return json(
return data(
{ errors: { fieldErrors: { version: 'Version required' } } },
{ status: 400 }
)
Expand All @@ -181,7 +178,7 @@ async function handleCreate(
const err = error as Error
if (err.name !== 'NoSuchKey') {
// for NoSuchKey, continue with defaults
return json(
return data(
{
error: `An error occurred while fetching data: ${(error as Error).message}`
},
Expand All @@ -192,7 +189,7 @@ async function handleCreate(

if (configs.default) {
if (configs[version]) {
return json(
return data(
{ errors: { fieldErrors: { version: 'Version already exists' } } },
{ status: 409 }
)
Expand All @@ -208,9 +205,9 @@ async function handleCreate(
}

await storageService.putJson(walletAddress, configs)
return json(configs)
return data(configs)
} catch (error) {
return json({ error: (error as Error).message }, { status: 500 })
return data({ error: (error as Error).message }, { status: 500 })
}
}

Expand Down Expand Up @@ -239,9 +236,9 @@ async function handleUpdate(
await configStorage.putJson(walletAddress, filteredData)

// TODO: reduce payload size, return only ok
return json(filteredData)
return data(filteredData)
} catch (error) {
return json({ error: (error as Error).message }, { status: 500 })
return data({ error: (error as Error).message }, { status: 500 })
}
}

Expand All @@ -254,7 +251,7 @@ async function handleDelete(
const version = formData.get('version') as string

if (!version) {
return json(
return data(
{ errors: { fieldErrors: { version: 'Version required' } } },
{ status: 400 }
)
Expand All @@ -272,9 +269,9 @@ async function handleDelete(
await configStorage.putJson(walletAddress, existingConfig)
}

return json(existingConfig)
return data(existingConfig)
} catch (error) {
return json(
return data(
{
error: `Error occurred while deleting version: ${(error as Error).message}`
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { redirect, type LoaderFunctionArgs } from '@remix-run/cloudflare'
import { redirect } from 'react-router'
import { commitSession, getSession } from '~/utils/session.server'
import { isGrantValidAndAccepted } from '~/utils/open-payments.server'

export async function loader({ params, request, context }: LoaderFunctionArgs) {
import type { Route } from './+types/api.grant.$type'

export async function loader({ params, request, context }: Route.LoaderArgs) {
const { env } = context.cloudflare

const elementType = params.type
Expand Down
9 changes: 5 additions & 4 deletions frontend/app/routes/banner.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useEffect, useState, useRef } from 'react'
import { useSnapshot } from 'valtio'
import { useLoaderData, useNavigate } from '@remix-run/react'
import { useUIActions } from '~/stores/uiStore'
import { usePathTracker } from '~/hooks/usePathTracker'
import {
useLoaderData,
useNavigate,
data,
type LoaderFunctionArgs,
json,
type MetaFunction
} from '@remix-run/cloudflare'
} from 'react-router'
import {
HeadingCore,
ToolsWalletAddress,
Expand Down Expand Up @@ -60,7 +61,7 @@ export async function loader({ request, context }: LoaderFunctionArgs) {
session.unset('is-grant-accepted')
session.unset('is-grant-response')

return json(
return data(
{
grantResponse,
isGrantAccepted,
Expand Down
3 changes: 1 addition & 2 deletions frontend/app/routes/link-tag.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { HeadingCore, LinkTagGenerator } from '@/components'
import { useNavigate } from '@remix-run/react'
import type { MetaFunction } from '@remix-run/cloudflare'
import { useNavigate, type MetaFunction } from 'react-router'

export const meta: MetaFunction = () => {
return [
Expand Down
Loading