mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2026-01-31 12:04:23 +01:00
Merge pull request #5 from MutinyWallet/waitlist-skipper
add waitlist skipper
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@@ -19,6 +19,7 @@
|
||||
"react-router-dom": "^6.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.14.0",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/react-linkify": "^1.0.1",
|
||||
@@ -2427,9 +2428,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz",
|
||||
"integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==",
|
||||
"version": "18.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.0.tgz",
|
||||
"integrity": "sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"react-router-dom": "^6.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.14.0",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/react-linkify": "^1.0.1",
|
||||
|
||||
10
src/App.tsx
10
src/App.tsx
@@ -1,9 +1,12 @@
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
|
||||
import Join from "./Join";
|
||||
import Layout from "./components/Layout";
|
||||
import Join from "@/routes/Join";
|
||||
import Layout from "@/components/Layout";
|
||||
import SecretWaitlistSkipper from "@/routes/SecretWaitlistSkipper";
|
||||
import Home from "@/routes/Home";
|
||||
|
||||
function App() {
|
||||
let active = localStorage.getItem('active') || "";
|
||||
return (
|
||||
<div className="App">
|
||||
|
||||
@@ -12,7 +15,8 @@ function App() {
|
||||
<Routes>
|
||||
{/* <Route path="/" element={<Home />} /> */}
|
||||
<Route path="/" element={<Layout />}>
|
||||
<Route index element={<Join />} />
|
||||
<Route index element={active === "true" ? <Home /> : <Join />} />
|
||||
<Route path="secretwaitlistskipper" element={<SecretWaitlistSkipper />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</div>
|
||||
|
||||
115
src/Home.tsx
115
src/Home.tsx
@@ -1,115 +0,0 @@
|
||||
import Sheet from 'react-modal-sheet';
|
||||
import { useState } from 'react';
|
||||
import logo from './assets/mutiny-logo.svg';
|
||||
import mutiny_m from './assets/m.svg';
|
||||
import scan from './assets/scan.svg';
|
||||
import settings from './assets/settings.svg';
|
||||
import send from './assets/send.svg';
|
||||
|
||||
|
||||
function ActivityItem() {
|
||||
return (
|
||||
<div className="flex flex-row border-b border-gray-500 gap-4 py-2">
|
||||
<img src={send} className="App-logo" alt="logo" />
|
||||
<div className='flex flex-col flex-1'>
|
||||
<h1>Bitcoin Beefsteak</h1>
|
||||
<h2>-1,441,851 SAT</h2>
|
||||
<h3 className='text-sm text-gray-500'>Jul 24</h3>
|
||||
</div>
|
||||
<div className='text-sm font-semibold uppercase text-[#E23A5E]'>SEND</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="safe-top safe-left safe-right safe-bottom">
|
||||
<div className="disable-scrollbars max-h-screen h-full overflow-y-scroll mx-4">
|
||||
<main className='flex flex-col gap-4 py-8'>
|
||||
<header>
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
</header>
|
||||
<div className='border border-white rounded-xl border-b-4 p-4 flex flex-col gap-2'>
|
||||
<header className='text-sm font-semibold uppercase'>
|
||||
Balance
|
||||
</header>
|
||||
<div>
|
||||
<h1 className='text-4xl font-light'>
|
||||
69,420 <span className='text-xl'>SAT</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex gap-2 py-4">
|
||||
<button onClick={() => setOpen(true)} className='bg-[#1EA67F] p-4 flex-1 rounded-xl text-xl font-semibold '><span className="drop-shadow-sm shadow-black">Send</span></button>
|
||||
<button className='bg-[#3B6CCC] p-4 flex-1 rounded-xl text-xl font-semibold '><span className="drop-shadow-sm shadow-black">Receive</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='rounded-xl p-4 flex flex-col gap-2 bg-[rgba(0,0,0,0.5)]'>
|
||||
<header className='text-sm font-semibold uppercase'>
|
||||
Activity
|
||||
</header>
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<div className='flex justify-end py-4'>
|
||||
<a href="#" className='underline text-sm'>
|
||||
MORE
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/* safety div */}
|
||||
<div className="h-32" />
|
||||
</main>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<Sheet isOpen={isOpen} onClose={() => setOpen(false)}>
|
||||
<Sheet.Container>
|
||||
<Sheet.Header />
|
||||
<Sheet.Content>
|
||||
<div className='p-4 flex flex-col gap-2'>
|
||||
<header className='text-sm font-semibold uppercase'>
|
||||
Activity
|
||||
</header>
|
||||
<ActivityItem />
|
||||
<h1 className='text-4xl font-light'>
|
||||
It's a sheet! Like a modal, but a sheet.
|
||||
</h1>
|
||||
</div>
|
||||
</Sheet.Content>
|
||||
</Sheet.Container>
|
||||
|
||||
<Sheet.Backdrop />
|
||||
</Sheet>
|
||||
|
||||
|
||||
<nav className='bg-black fixed bottom-0 shadow-lg z-40 w-full safe-bottom'>
|
||||
<ul className='h-16 flex justify-between px-16 items-center'>
|
||||
<li className='h-full border-t-2 border-b-2 border-b-black flex flex-col justify-center'>
|
||||
<img src={mutiny_m} className="App-logo" alt="logo" />
|
||||
</li>
|
||||
<li>
|
||||
<img src={scan} className="App-logo" alt="logo" />
|
||||
</li>
|
||||
<li>
|
||||
<img src={settings} className="App-logo" alt="logo" />
|
||||
</li>
|
||||
{/* <li>home</li> */}
|
||||
{/* <li>scan</li> */}
|
||||
{/* <li>settings</li> */}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
function Receive() {
|
||||
const pageMotion = {
|
||||
initial: { opacity: 0, x: 0 },
|
||||
animate: { opacity: 1, x: 50, transition: { duration: 2 } },
|
||||
exit: { opacity: 0, x: 0, transition: { duration: 2 } }
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="about">
|
||||
<motion.div initial="initial" animate="animate" exit="exit" variants={pageMotion}>about page</motion.div>
|
||||
<Link to="/">Go to home page</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Receive
|
||||
@@ -18,9 +18,9 @@ function Note({ e }: { e: NostrEvent }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-4 border-b border-faint-white py-6 items-start">
|
||||
<img className="bg-black rounded-xl" src="../180.png" width={45} height={45} />
|
||||
<div className="flex flex-col gap-2 max-w-sm">
|
||||
<div className="flex gap-4 border-b border-faint-white py-6 items-start w-full">
|
||||
<img className="bg-black rounded-xl flex-0" src="../180.png" width={45} height={45} />
|
||||
<div className="flex flex-col gap-2 flex-1">
|
||||
{/* <p>{JSON.stringify(e, null, 2)}</p> */}
|
||||
<p className="break-words">
|
||||
<Linkify>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Notes from "./Notes";
|
||||
import { NostrProvider } from "nostr-react";
|
||||
import { useState } from "react";
|
||||
|
||||
const relayUrls = [
|
||||
"wss://nostr.zebedee.cloud",
|
||||
@@ -12,18 +13,28 @@ const relayUrls = [
|
||||
]
|
||||
|
||||
export function WaitlistAlreadyIn() {
|
||||
const [skipCount, setSkipCount] = useState(0);
|
||||
|
||||
function skipCounter() {
|
||||
setSkipCount(skipCount + 1);
|
||||
if (skipCount >= 6) {
|
||||
window.location.href = "/secretwaitlistskipper";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<main className='flex flex-col gap-4 py-8 max-w-xl mx-auto items-center drop-shadow-blue-glow'>
|
||||
<main className='flex flex-col gap-2 sm:gap-4 py-8 px-4 max-w-xl mx-auto items-center drop-shadow-blue-glow'>
|
||||
<h1 className="text-4xl font-bold">You're on a list!</h1>
|
||||
<h2 className="text-xl">
|
||||
We'll message you when Mutiny Wallet is ready.
|
||||
</h2>
|
||||
<div className="p-8 rounded-xl bg-half-black">
|
||||
<div className="px-4 sm:px-8 py-8 rounded-xl bg-half-black">
|
||||
<h2 className="text-sm font-semibold uppercase">Recent Updates</h2>
|
||||
<NostrProvider relayUrls={relayUrls} debug={true}>
|
||||
<Notes />
|
||||
</NostrProvider>
|
||||
</div>
|
||||
<button className="opacity-0" onClick={skipCounter}>Skip</button>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import button from "./button";
|
||||
import button from "@/styles/button";
|
||||
|
||||
const INPUT = "w-full mb-4 p-2 rounded-lg text-black"
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function WaitlistForm() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main className='flex flex-col gap-4 py-8 max-w-xl mx-auto drop-shadow-blue-glow'>
|
||||
<main className='flex flex-col gap-4 py-8 px-4 max-w-xl mx-auto drop-shadow-blue-glow'>
|
||||
<h1 className='text-4xl font-bold'>Join Waitlist</h1>
|
||||
{/* HTML form with three inputs: nostr pubkey (text), email (text), and a textarea for comments */}
|
||||
<h2 className="text-xl">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import App from './App'
|
||||
|
||||
112
src/routes/Home.tsx
Normal file
112
src/routes/Home.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import Sheet from 'react-modal-sheet';
|
||||
import { useState } from 'react';
|
||||
import logo from '@/assets/mutiny-logo.svg';
|
||||
import mutiny_m from '@/assets/m.svg';
|
||||
import scan from '@/assets/scan.svg';
|
||||
import settings from '@/assets/settings.svg';
|
||||
import send from '@/assets/send.svg';
|
||||
|
||||
function ActivityItem() {
|
||||
return (
|
||||
<div className="flex flex-row border-b border-gray-500 gap-4 py-2">
|
||||
<img src={send} className="App-logo" alt="logo" />
|
||||
<div className='flex flex-col flex-1'>
|
||||
<h1>Bitcoin Beefsteak</h1>
|
||||
<h2>-1,441,851 SAT</h2>
|
||||
<h3 className='text-sm text-gray-500'>Jul 24</h3>
|
||||
</div>
|
||||
<div className='text-sm font-semibold uppercase text-[#E23A5E]'>SEND</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className='flex flex-col gap-4 py-8 px-4'>
|
||||
<header>
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
</header>
|
||||
<div className='border border-white rounded-xl border-b-4 p-4 flex flex-col gap-2'>
|
||||
<header className='text-sm font-semibold uppercase'>
|
||||
Balance
|
||||
</header>
|
||||
<div>
|
||||
<h1 className='text-4xl font-light'>
|
||||
69,420 <span className='text-xl'>SAT</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex gap-2 py-4">
|
||||
<button onClick={() => setOpen(true)} className='bg-[#1EA67F] p-4 flex-1 rounded-xl text-xl font-semibold '><span className="drop-shadow-sm shadow-black">Send</span></button>
|
||||
<button className='bg-[#3B6CCC] p-4 flex-1 rounded-xl text-xl font-semibold '><span className="drop-shadow-sm shadow-black">Receive</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='rounded-xl p-4 flex flex-col gap-2 bg-[rgba(0,0,0,0.5)]'>
|
||||
<header className='text-sm font-semibold uppercase'>
|
||||
Activity
|
||||
</header>
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<ActivityItem />
|
||||
<div className='flex justify-end py-4'>
|
||||
<a href="#" className='underline text-sm'>
|
||||
MORE
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/* safety div */}
|
||||
<div className="h-32" />
|
||||
</main>
|
||||
|
||||
|
||||
|
||||
<Sheet isOpen={isOpen} onClose={() => setOpen(false)}>
|
||||
<Sheet.Container>
|
||||
<Sheet.Header />
|
||||
<Sheet.Content>
|
||||
<div className='p-4 flex flex-col gap-2'>
|
||||
<header className='text-sm font-semibold uppercase'>
|
||||
Activity
|
||||
</header>
|
||||
<ActivityItem />
|
||||
<h1 className='text-4xl font-light'>
|
||||
It's a sheet! Like a modal, but a sheet.
|
||||
</h1>
|
||||
</div>
|
||||
</Sheet.Content>
|
||||
</Sheet.Container>
|
||||
|
||||
<Sheet.Backdrop />
|
||||
</Sheet>
|
||||
|
||||
|
||||
<nav className='bg-black fixed bottom-0 shadow-lg z-40 w-full safe-bottom'>
|
||||
<ul className='h-16 flex justify-between px-16 items-center'>
|
||||
<li className='h-full border-t-2 border-b-2 border-b-black flex flex-col justify-center'>
|
||||
<img src={mutiny_m} className="App-logo" alt="logo" />
|
||||
</li>
|
||||
<li>
|
||||
<img src={scan} className="App-logo" alt="logo" />
|
||||
</li>
|
||||
<li>
|
||||
<img src={settings} className="App-logo" alt="logo" />
|
||||
</li>
|
||||
{/* <li>home</li> */}
|
||||
{/* <li>scan</li> */}
|
||||
{/* <li>settings</li> */}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import WaitlistForm from "./components/WaitlistForm";
|
||||
import { WaitlistAlreadyIn } from "./components/WaitlistAlreadyIn";
|
||||
import WaitlistForm from "@/components/WaitlistForm";
|
||||
import { WaitlistAlreadyIn } from "@/components/WaitlistAlreadyIn";
|
||||
|
||||
export default function Join() {
|
||||
// On load, check if the user is already on the waitlist
|
||||
30
src/routes/SecretWaitlistSkipper.tsx
Normal file
30
src/routes/SecretWaitlistSkipper.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import button from "@/styles/button";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function SecretWaitlistSkipper() {
|
||||
const [active] = useState(localStorage.getItem('active') || "");
|
||||
|
||||
function toggleSkipWaitlist() {
|
||||
// If active in localstorage is true, set to false, otherwie set to true
|
||||
if (active === "true") {
|
||||
localStorage.setItem("active", "false");
|
||||
} else {
|
||||
localStorage.setItem("active", "true");
|
||||
}
|
||||
|
||||
// Redirect to index
|
||||
window.location.href = "/";
|
||||
}
|
||||
return (
|
||||
<div className="w-full h-screen flex flex-col items-center justify-center">
|
||||
<div className="flex-0">
|
||||
{active === "true" &&
|
||||
<button className={button({ intent: "active" })} onClick={toggleSkipWaitlist}><span className="drop-shadow-sm shadow-black">I love waiting, hate cheating</span></button>
|
||||
}
|
||||
{active !== "true" &&
|
||||
<button className={button({ intent: "active" })} onClick={toggleSkipWaitlist}><span className="drop-shadow-sm shadow-black">I hate waiting, love cheating</span></button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -14,7 +14,11 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "react-jsx",
|
||||
"types": ["node"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
|
||||
@@ -2,6 +2,8 @@ import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { VitePWA } from 'vite-plugin-pwa'
|
||||
|
||||
import * as path from 'path'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
|
||||
@@ -29,4 +31,7 @@ export default defineConfig({
|
||||
enabled: true
|
||||
}
|
||||
})],
|
||||
resolve: {
|
||||
alias: [{ find: '@', replacement: path.resolve(__dirname, './src') }]
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user