Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email App Revamp #31

Merged
merged 14 commits into from
Dec 24, 2024
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
4 changes: 2 additions & 2 deletions examples/core-connection/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6822,7 +6822,7 @@ __metadata:

"shared-components@file:../../packages/shared-components::locator=core-connection%40workspace%3A.":
version: 0.1.0
resolution: "shared-components@file:../../packages/shared-components#../../packages/shared-components::hash=498aac&locator=core-connection%40workspace%3A."
resolution: "shared-components@file:../../packages/shared-components#../../packages/shared-components::hash=799d4e&locator=core-connection%40workspace%3A."
dependencies:
"@emotion/react": "npm:^11.13.0"
"@radix-ui/react-dialog": "npm:^1.1.1"
Expand All @@ -6843,7 +6843,7 @@ __metadata:
peerDependencies:
react: ^18.3.1
react-dom: ^18.3.1
checksum: 10/f9032d46c349f59ae84062f96110e8c1df66b69f8aabd583eb25aeb826c9bc383f017a32f4fd9e016f3abcfbab507a86e09b80ec0bfa9703516305eea5e8247e
checksum: 10/b85739b98051e191bf7118f0007d9b9aef1f6eb01d6ba62a9fe627945b724416ea96fbf36ceaea9953092ba5d1172b92ea1dfd6841f5071e2a6a4f8d5538e1d1
languageName: node
linkType: hard

Expand Down
3 changes: 2 additions & 1 deletion examples/email/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview",
"deploy:vercel": "vercel build --prod && vercel deploy --prebuilt --prod",
Expand All @@ -16,6 +16,7 @@
"@privy-io/wagmi": "^0.2.12",
"@pushprotocol/node-core": "^0.0.29",
"@pushprotocol/push-chain": "^0.1.7",
"@pushprotocol/pushchain-ui-kit": "^1.0.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-scroll-area": "^1.2.0",
Expand Down
Binary file added examples/email/public/DummyDiscord.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/email/public/DummyEmail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/email/public/DummyHeader.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/email/public/EmailBanner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/email/public/EmailLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 31 additions & 18 deletions examples/email/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
import { usePrivy } from '@privy-io/react-auth';
import { getBlocksCSSVariables, themeConfig } from 'shared-components';
import { createGlobalStyle, ThemeProvider } from 'styled-components';
import AppRoutes from './routes';
import { BrowserRouter } from 'react-router-dom';
import {
ENV,
PushWalletIFrame,
PushWalletProvider,
} from '@pushprotocol/pushchain-ui-kit';
import { AppProvider } from './context/AppContext';

import { useAppContext } from './context/app-context';
import LoggedInView from './components/logged-in-view';
import Login from './components/login';
const GlobalStyle = createGlobalStyle`
:root{
/* Font Family */
--font-family: 'FK Grotesk Neu';

/* New blocks theme css variables*/

${(props) => getBlocksCSSVariables(props.theme.blocksTheme)}
}
`;

function App() {
const { ready, authenticated } = usePrivy();
const { pushAccount } = useAppContext();
return (
<>
{ready ? (
<main className="h-screen w-screen">
{authenticated || pushAccount ? <LoggedInView /> : <Login />}
</main>
) : (
<div className="flex flex-col gap-4 items-center justify-center h-screen w-full">
<div className="w-8 h-8 animate-ping bg-primary rounded-full"></div>
<p>Loading</p>
</div>
)}
</>
<ThemeProvider theme={themeConfig.light}>
<GlobalStyle />
<PushWalletProvider env={ENV.PROD}>
<AppProvider>
<BrowserRouter>
<PushWalletIFrame />
<AppRoutes />
</BrowserRouter>
</AppProvider>
</PushWalletProvider>
</ThemeProvider>
);
}

Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 0 additions & 1 deletion examples/email/src/assets/react.svg

This file was deleted.

31 changes: 31 additions & 0 deletions examples/email/src/common/common.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
type Email = {
from: string;
to: string[];
subject: string;
timestamp: number;
body: string;
type?: string;
attachments?: FileAttachments;
txHash: string;
};

type FileAttachment = {
filename: string;
type: string;
content: string;
};

type Wallet = {
address: string;
chainId: string | null;
chain: string | null;
};

enum EMAIL_BOX {
INBOX = 'inbox',
SENT = 'sent',
}

type FileAttachments = FileAttachment[];
export type { Email, FileAttachments, FileAttachment, Wallet };
export { EMAIL_BOX };
140 changes: 140 additions & 0 deletions examples/email/src/common/common.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { EMAIL_BOX, Email } from './common.types';

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function trimAddress(address: string) {
return `${address.slice(0, 6)}...${address.slice(-4)}`;
}

export function formatTimestamp(
timestamp: string,
showAgo: boolean = false
): string {
const date = new Date(parseInt(timestamp, 10));
const now = new Date();

if (isNaN(date.getTime())) {
return 'Invalid Date';
}

const timeDiff = now.getTime() - date.getTime();
const seconds = Math.floor(timeDiff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const months = Math.floor(days / 30);
const years = Math.floor(days / 365);

let agoText = '';
if (showAgo) {
if (seconds < 60) {
agoText = `(${seconds} seconds ago)`;
} else if (minutes < 60) {
agoText = `(${minutes} minutes ago)`;
} else if (hours < 24) {
agoText = `(${hours} hours ago)`;
} else if (days < 30) {
agoText = `(${days} days ago)`;
} else if (months < 12) {
agoText = `(${months} months ago)`;
} else {
agoText = `(${years} years ago)`;
}
}

if (
date.getDate() === now.getDate() &&
date.getMonth() === now.getMonth() &&
date.getFullYear() === now.getFullYear()
) {
return (
date.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: true,
}) + (showAgo ? ` ${agoText}` : '')
);
}

const yesterday = new Date(now);
yesterday.setDate(now.getDate() - 1);

if (
date.getDate() === yesterday.getDate() &&
date.getMonth() === yesterday.getMonth() &&
date.getFullYear() === yesterday.getFullYear()
) {
return 'Yesterday' + (showAgo ? ` ${agoText}` : '');
}

if (date.getFullYear() === now.getFullYear()) {
return (
date.toLocaleDateString('en-US', {
month: 'short',
day: '2-digit',
}) + (showAgo ? ` ${agoText}` : '')
);
}

return (
date.toLocaleDateString('en-US', {
month: 'short',
day: '2-digit',
year: 'numeric',
}) + (showAgo ? ` ${agoText}` : '')
);
}

export const dummyEmail = {
txHash: 'welcome',
to: [],
from: 'push.fam',
subject: 'GM! Web3 Email',
timestamp: 0,
body: `Hello Degen, <br />Welcome to the future of email, where web3 meets seamless unified across all chains! With this app, you can easily connect with your fellow Solana, Ethereum, Polygon, Optimism and other blockchain users.`,
type: EMAIL_BOX.INBOX,
};

export const formatReplyBody = (email: Email) => {
return `

On ${formatTimestamp(email.timestamp.toString())}, ${
email.from.split(':')[2]
} wrote:

${email.body
.split('\n')
.map((line) => `> ${line}`)
.join('\n')}
`;
};

export const extractWalletAddress = (address: string) => {
if (address.includes(':')) {
const parts = address.split(':');
return parts[parts.length - 1];
}
return address;
};

export const getChainFromCAIP = (caip: string) => {
const chainId = caip.split(':')[1];
if (chainId === '1') return 'eth';
if (chainId === '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') return 'sol';
return 'push';
};

export const getInCAIP = (address: string, chain: string) => {
return `${
chain === 'eth'
? 'eip155:1'
: chain === 'sol'
? 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
: chain === 'bnb'
? 'eip155:56'
: 'push:devnet'
}:${address}`;
};
Original file line number Diff line number Diff line change
@@ -1,76 +1,75 @@
import * as React from "react"

import { cn } from "@/lib/utils"
import * as React from 'react';
import { cn } from '../common.utils';

// TODO: Replace these components with Box
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border bg-card text-card-foreground shadow",
'bg-card border-b-[1px] py-4 px-6 flex flex-col items-start gap-3',
className
)}
{...props}
/>
))
Card.displayName = "Card"
));
Card.displayName = 'Card';

const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
<div ref={ref} className={cn('flex flex-col', className)} {...props} />
));
CardHeader.displayName = 'CardHeader';

const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
className={cn('font-semibold leading-none tracking-tight', className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
));
CardTitle.displayName = 'CardTitle';

const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
));
CardDescription.displayName = 'CardDescription';

const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
));
CardContent.displayName = 'CardContent';

const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
<div ref={ref} className={cn('flex items-center', className)} {...props} />
));
CardFooter.displayName = 'CardFooter';

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
};
Loading