-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
85 changed files
with
5,379 additions
and
1,782 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,9 @@ | ||
{ | ||
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] | ||
"recommendations": [ | ||
"dbaeumer.vscode-eslint", | ||
"esbenp.prettier-vscode", | ||
"clinyong.vscode-css-modules", | ||
"vunguyentuan.vscode-css-variables", | ||
"vunguyentuan.vscode-postcss" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# xmtp.chat app | ||
|
||
Use this React app as a tool to start building an app with XMTP. | ||
|
||
The app is built using the [XMTP client browser SDK](/sdks/browser-sdk/README.md), [React](https://react.dev/), and [RainbowKit](https://www.rainbowkit.com/). | ||
|
||
To keep up with the latest React app developments, see the [Issues tab](https://github.com/xmtp/xmtp-js/issues) in this repo. | ||
|
||
To learn more about XMTP and get answers to frequently asked questions, see the [XMTP documentation](https://xmtp.org/docs). | ||
|
||
### Limitations | ||
|
||
This React app isn't a complete solution. For example, the list of conversations doesn't update when new messages arrive in existing conversations. | ||
|
||
## Useful commands | ||
|
||
- `yarn clean`: Removes `node_modules` and `.turbo` folders | ||
- `yarn dev`: Runs the app in development mode | ||
- `yarn typecheck`: Runs `tsc` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
{ | ||
"name": "@xmtp/xmtp.chat", | ||
"version": "0.0.0", | ||
"private": true, | ||
"type": "module", | ||
"exports": { | ||
".": "./src/index.ts" | ||
}, | ||
"main": "src/index.ts", | ||
"scripts": { | ||
"build": "vite build", | ||
"clean": "rm -rf .turbo && rm -rf node_modules && yarn clean:dist", | ||
"clean:dist": "rm -rf dist", | ||
"dev": "vite", | ||
"typecheck": "tsc" | ||
}, | ||
"dependencies": { | ||
"@mantine/core": "^7.14.3", | ||
"@mantine/form": "^7.14.3", | ||
"@mantine/hooks": "^7.14.3", | ||
"@mantine/modals": "^7.14.3", | ||
"@mantine/notifications": "^7.14.3", | ||
"@tanstack/react-query": "^5.61.5", | ||
"@xmtp/browser-sdk": "^0.0.13", | ||
"@xmtp/content-type-group-updated": "^2.0.0", | ||
"@xmtp/content-type-primitives": "^2.0.0", | ||
"@xmtp/content-type-reaction": "^2.0.0", | ||
"@xmtp/content-type-remote-attachment": "^2.0.0", | ||
"@xmtp/content-type-reply": "^2.0.0", | ||
"@xmtp/content-type-text": "^2.0.0", | ||
"date-fns": "^4.1.0", | ||
"react": "^18.3.1", | ||
"react-18-blockies": "^1.0.6", | ||
"react-confetti": "^6.1.0", | ||
"react-dom": "^18.3.1", | ||
"react-router": "^7.0.2", | ||
"uint8array-extras": "^1.4.0", | ||
"viem": "^2.21.52", | ||
"wagmi": "^2.13.2" | ||
}, | ||
"devDependencies": { | ||
"@types/react": "^18.3.3", | ||
"@types/react-dom": "^18.3.1", | ||
"@vitejs/plugin-react": "^4.3.3", | ||
"postcss": "^8.4.49", | ||
"postcss-preset-mantine": "^1.17.0", | ||
"postcss-simple-vars": "^7.0.1", | ||
"tsconfig": "workspace:*", | ||
"typescript": "^5.7.2", | ||
"vite": "^6.0.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module.exports = { | ||
plugins: { | ||
"postcss-preset-mantine": {}, | ||
"postcss-simple-vars": { | ||
variables: { | ||
"mantine-breakpoint-xs": "36em", | ||
"mantine-breakpoint-sm": "48em", | ||
"mantine-breakpoint-md": "62em", | ||
"mantine-breakpoint-lg": "75em", | ||
"mantine-breakpoint-xl": "88em", | ||
}, | ||
}, | ||
}, | ||
}; |
Binary file not shown.
File renamed without changes
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { Button, Flex, useMatches } from "@mantine/core"; | ||
import { useEffect, useRef } from "react"; | ||
import { useNavigate } from "react-router"; | ||
import { useClient } from "../hooks/useClient"; | ||
import { IconMessagePlus } from "../icons/IconMessagePlus"; | ||
import { useRefManager } from "./RefManager"; | ||
|
||
export const Actions: React.FC = () => { | ||
const { client } = useClient(); | ||
const navigate = useNavigate(); | ||
const { setRef } = useRefManager(); | ||
const ref = useRef<HTMLButtonElement>(null); | ||
const label: React.ReactNode = useMatches({ | ||
base: <IconMessagePlus size={24} />, | ||
sm: "New conversation", | ||
}); | ||
const px = useMatches({ | ||
base: "xs", | ||
sm: "md", | ||
}); | ||
const handleClick = () => { | ||
void navigate("/conversations/new"); | ||
}; | ||
|
||
useEffect(() => { | ||
setRef("new-conversation-button", ref); | ||
}, []); | ||
|
||
return ( | ||
client && ( | ||
<Flex align="center" gap="xs"> | ||
<Button px={px} onClick={handleClick} ref={ref}> | ||
{label} | ||
</Button> | ||
</Flex> | ||
) | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { Badge, Flex, Text, Tooltip } from "@mantine/core"; | ||
import { useClipboard } from "@mantine/hooks"; | ||
import { shortAddress } from "../helpers/address"; | ||
|
||
export type AddressTooltipLabelProps = { | ||
address: string; | ||
}; | ||
|
||
export const AddressTooltipLabel: React.FC<AddressTooltipLabelProps> = ({ | ||
address, | ||
}) => { | ||
return ( | ||
<Flex direction="column"> | ||
<Text size="sm">{address}</Text> | ||
<Text size="xs" c="dimmed" ta="center"> | ||
click to copy | ||
</Text> | ||
</Flex> | ||
); | ||
}; | ||
|
||
export type AddressBadgeProps = { | ||
address: string; | ||
}; | ||
|
||
export const AddressBadge: React.FC<AddressBadgeProps> = ({ address }) => { | ||
const clipboard = useClipboard({ timeout: 1000 }); | ||
|
||
const handleCopy = () => { | ||
clipboard.copy(address); | ||
}; | ||
|
||
const handleKeyboardCopy = (event: React.KeyboardEvent<HTMLDivElement>) => { | ||
if (event.key === "Enter" || event.key === " ") { | ||
handleCopy(); | ||
} | ||
}; | ||
|
||
return ( | ||
<Tooltip | ||
label={ | ||
clipboard.copied ? ( | ||
<Text size="xs">Copied!</Text> | ||
) : ( | ||
<AddressTooltipLabel address={address} /> | ||
) | ||
} | ||
withArrow | ||
events={{ hover: true, focus: true, touch: true }}> | ||
<Badge | ||
variant="default" | ||
size="lg" | ||
radius="md" | ||
onKeyDown={handleKeyboardCopy} | ||
onClick={handleCopy} | ||
miw={100} | ||
tabIndex={0} | ||
styles={{ | ||
label: { | ||
textTransform: "none", | ||
}, | ||
}} | ||
flex="1 0"> | ||
{shortAddress(address)} | ||
</Badge> | ||
</Tooltip> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
@media (max-width: 48em) { | ||
.main { | ||
--app-shell-footer-offset: 0px !important; | ||
} | ||
|
||
.navbar { | ||
--app-shell-footer-offset: 0px !important; | ||
} | ||
|
||
.footer { | ||
--app-shell-footer-height: 0px; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { | ||
AppShell, | ||
Button, | ||
Group, | ||
Modal, | ||
Stack, | ||
Text, | ||
Title, | ||
} from "@mantine/core"; | ||
import { useDisclosure } from "@mantine/hooks"; | ||
import { useEffect, useState } from "react"; | ||
import { useLocation, useNavigate } from "react-router"; | ||
import { useClient } from "../hooks/useClient"; | ||
import classes from "./App.module.css"; | ||
import { AppFooter } from "./AppFooter"; | ||
import { AppHeader } from "./AppHeader"; | ||
import { Main } from "./Main"; | ||
import { Navbar } from "./Navbar"; | ||
|
||
export const App: React.FC = () => { | ||
const [opened, { toggle }] = useDisclosure(); | ||
const [collapsed, setCollapsed] = useState(true); | ||
const [unhandledRejectionError, setUnhandledRejectionError] = useState< | ||
string | null | ||
>(null); | ||
const { client } = useClient(); | ||
const location = useLocation(); | ||
const navigate = useNavigate(); | ||
|
||
useEffect(() => { | ||
if (!client && location.pathname !== "/") { | ||
void navigate("/"); | ||
return; | ||
} | ||
|
||
if ( | ||
location.pathname.startsWith("/conversations") || | ||
location.pathname.startsWith("/identity") | ||
) { | ||
setCollapsed(false); | ||
} else { | ||
setCollapsed(true); | ||
} | ||
|
||
if (location.pathname === "/" && client) { | ||
void navigate("/conversations"); | ||
} | ||
}, [location.pathname, client, navigate]); | ||
|
||
useEffect(() => { | ||
const handleUnhandledRejection = (event: PromiseRejectionEvent) => { | ||
setUnhandledRejectionError( | ||
(event.reason as Error).message || "Unknown error", | ||
); | ||
}; | ||
window.addEventListener("unhandledrejection", handleUnhandledRejection); | ||
return () => { | ||
window.removeEventListener( | ||
"unhandledrejection", | ||
handleUnhandledRejection, | ||
); | ||
}; | ||
}, []); | ||
|
||
return ( | ||
<> | ||
{unhandledRejectionError && ( | ||
<Modal | ||
opened={!!unhandledRejectionError} | ||
onClose={() => { | ||
setUnhandledRejectionError(null); | ||
}} | ||
withCloseButton={false} | ||
centered> | ||
<Stack gap="md"> | ||
<Title order={4}>Application error</Title> | ||
<Text>{unhandledRejectionError}</Text> | ||
<Group justify="flex-end"> | ||
<Button | ||
onClick={() => { | ||
setUnhandledRejectionError(null); | ||
}}> | ||
OK | ||
</Button> | ||
</Group> | ||
</Stack> | ||
</Modal> | ||
)} | ||
<AppShell | ||
header={{ height: 68 }} | ||
footer={{ | ||
height: 64, | ||
}} | ||
navbar={{ | ||
width: { base: 300, lg: 420 }, | ||
breakpoint: "sm", | ||
collapsed: { desktop: collapsed, mobile: !opened }, | ||
}} | ||
padding="md"> | ||
<AppShell.Header> | ||
<AppHeader opened={opened} toggle={toggle} collapsed={collapsed} /> | ||
</AppShell.Header> | ||
<AppShell.Navbar className={classes.navbar}> | ||
{client && <Navbar />} | ||
</AppShell.Navbar> | ||
<AppShell.Main className={classes.main}> | ||
<Main /> | ||
</AppShell.Main> | ||
<AppShell.Footer display="flex" className={classes.footer}> | ||
<AppFooter /> | ||
</AppShell.Footer> | ||
</AppShell> | ||
</> | ||
); | ||
}; |
Oops, something went wrong.