mirror of
https://github.com/Steffo99/festa.git
synced 2024-12-22 14:44:21 +00:00
Do more prototyping!
This commit is contained in:
parent
91359e7299
commit
e1409f3545
21 changed files with 264 additions and 103 deletions
28
components/InputEventSlug.tsx
Normal file
28
components/InputEventSlug.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import * as React from "react"
|
||||
|
||||
export interface InputSlug extends React.HTMLProps<HTMLInputElement> {
|
||||
onSlugChange?: (val: string) => void,
|
||||
}
|
||||
|
||||
export function InputSlug(props: InputSlug) {
|
||||
const [text, setText] = React.useState("")
|
||||
|
||||
const handleChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
props.onChange?.(event)
|
||||
|
||||
let slug = event.target.value.toLowerCase().replaceAll(/[^a-z0-9]/g, "-")
|
||||
props.onSlugChange?.(slug)
|
||||
setText(slug)
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
value={text}
|
||||
onChange={handleChange}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
|
@ -1,31 +1,50 @@
|
|||
import { Trans, useTranslation } from "next-i18next"
|
||||
import { LoginContext } from "../contexts/login"
|
||||
import { useDefinedContext } from "../hooks/useDefinedContext"
|
||||
import { useDefinedContext } from "../utils/definedContext"
|
||||
import { InputSlug } from "./InputEventSlug"
|
||||
import { LoginButton } from "./LoginButton"
|
||||
import { TelegramUserLink } from "./TelegramUserLink"
|
||||
import { LogoutLink } from "./LogoutLink"
|
||||
import { TelegramUser } from "./TelegramUser"
|
||||
|
||||
|
||||
export function Intro() {
|
||||
const {t} = useTranslation("common")
|
||||
const [login, setLogin] = useDefinedContext(LoginContext)
|
||||
const [login, _] = useDefinedContext(LoginContext)
|
||||
|
||||
const loginMessage = login ? (
|
||||
const loginMessage = login ? <>
|
||||
<Trans i18nKey="introTelegramLoggedIn">
|
||||
Sei connesso come <TelegramUserLink u={login}/>!
|
||||
introTelegramLoggedIn(1: <TelegramUser u={login}/>)
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans i18nKey="introTelegramLogin">
|
||||
Effettua il login con Telegram per iniziare a creare il tuo evento.
|
||||
</Trans>
|
||||
)
|
||||
|
||||
<br/>
|
||||
<LogoutLink/>
|
||||
</> : <>
|
||||
{t("introTelegramLogin")}
|
||||
</>
|
||||
|
||||
const input = login ? <>
|
||||
<p>
|
||||
{t("introCreateEvent")}
|
||||
</p>
|
||||
<form>
|
||||
{window.location.protocol}//
|
||||
{window.location.host}/events/
|
||||
<InputSlug
|
||||
placeholder={t("introCreateEventSlugPlaceholder")}
|
||||
/>
|
||||
<button aria-label="Continue" className="input-square input-positive">
|
||||
→
|
||||
</button>
|
||||
</form>
|
||||
</> : <>
|
||||
<LoginButton
|
||||
botName="festaappbot"
|
||||
/>
|
||||
</>
|
||||
|
||||
return <>
|
||||
<p>
|
||||
{loginMessage}
|
||||
</p>
|
||||
<LoginButton
|
||||
botName="festaappbot"
|
||||
/>
|
||||
{input}
|
||||
</>
|
||||
}
|
|
@ -1,25 +1,18 @@
|
|||
import * as React from 'react';
|
||||
import {LoginContext} from "../contexts/login"
|
||||
import OriginalTelegramLoginButton from 'react-telegram-login'
|
||||
import { useDefinedContext } from '../hooks/useDefinedContext';
|
||||
import { useDefinedContext } from '../utils/definedContext';
|
||||
|
||||
export function LoginButton(props: any) {
|
||||
const [login, setLogin] = useDefinedContext(LoginContext)
|
||||
|
||||
return React.useMemo(() => (
|
||||
login ?
|
||||
<button onClick={() => setLogin(null)} className="btn-telegram">
|
||||
Log out
|
||||
</button>
|
||||
:
|
||||
<div className="container-btn-telegram">
|
||||
<OriginalTelegramLoginButton
|
||||
dataOnauth={setLogin}
|
||||
usePic={false}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
[login]
|
||||
return (
|
||||
<div className="container-btn-telegram">
|
||||
<OriginalTelegramLoginButton
|
||||
dataOnauth={setLogin}
|
||||
usePic={false}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
16
components/LogoutLink.tsx
Normal file
16
components/LogoutLink.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { useTranslation } from "next-i18next"
|
||||
import { LoginContext } from "../contexts/login"
|
||||
import { useDefinedContext } from "../utils/definedContext"
|
||||
|
||||
export function LogoutLink() {
|
||||
const [login, setLogin] = useDefinedContext(LoginContext)
|
||||
const {t} = useTranslation("common")
|
||||
|
||||
return (
|
||||
<small>
|
||||
<a href="javascript:void(0)" onClick={() => setLogin(null)}>
|
||||
{t("introTelegramLogout")}
|
||||
</a>
|
||||
</small>
|
||||
)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import Image from "next/image";
|
||||
import { PostcardContext } from "../contexts/postcard";
|
||||
import { useDefinedContext } from "../hooks/useDefinedContext";
|
||||
import { useDefinedContext } from "../utils/definedContext";
|
||||
|
||||
export function Postcard() {
|
||||
const [postcard, _] = useDefinedContext(PostcardContext)
|
||||
|
|
21
components/TelegramAvatar.tsx
Normal file
21
components/TelegramAvatar.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { LoginContext } from "../contexts/login";
|
||||
import { useDefinedContext } from "../utils/definedContext";
|
||||
import { UserData } from "../utils/telegram";
|
||||
|
||||
|
||||
export interface TelegramAvatarProps {
|
||||
u: UserData
|
||||
}
|
||||
|
||||
|
||||
export function TelegramAvatar({u}: TelegramAvatarProps) {
|
||||
const [login, _] = useDefinedContext(LoginContext)
|
||||
|
||||
return login ?
|
||||
<img
|
||||
src={u.photo_url}
|
||||
className="avatar-telegram-inline"
|
||||
/>
|
||||
:
|
||||
null
|
||||
}
|
|
@ -1,24 +1,28 @@
|
|||
import { UserData } from "../utils/telegram";
|
||||
import { TelegramAvatar } from "./TelegramAvatar";
|
||||
|
||||
interface TelegramUserLinkProps {
|
||||
u: UserData
|
||||
}
|
||||
|
||||
export function TelegramUserLink({u}: TelegramUserLinkProps) {
|
||||
export function TelegramUser({u}: TelegramUserLinkProps) {
|
||||
|
||||
if(u.username) return (
|
||||
<a href={`https://t.me/${u.username}`}>
|
||||
<TelegramAvatar u={u}/>
|
||||
{u.username}
|
||||
</a>
|
||||
)
|
||||
else if(u.last_name) return (
|
||||
<span>
|
||||
<TelegramAvatar u={u}/>
|
||||
{u.first_name} {u.last_name}
|
||||
</span>
|
||||
)
|
||||
else return (
|
||||
<span>
|
||||
u.first_name
|
||||
<TelegramAvatar u={u}/>
|
||||
{u.first_name}
|
||||
</span>
|
||||
)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import { LoginContext } from "../contexts/login";
|
||||
import { useDefinedContext } from "../hooks/useDefinedContext";
|
||||
|
||||
export function UserAvatar() {
|
||||
const [login, _] = useDefinedContext(LoginContext)
|
||||
|
||||
return login ?
|
||||
<img
|
||||
src={login?.photo_url}
|
||||
className="avatar-telegram"
|
||||
/>
|
||||
:
|
||||
null
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { createStateContext } from "../hooks/useStateContext";
|
||||
import { useStorageState } from "react-storage-hooks";
|
||||
import { createStateContext } from "../utils/stateContext";
|
||||
import * as Telegram from "../utils/telegram"
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createStateContext } from "../hooks/useStateContext";
|
||||
import { createStateContext } from "../utils/stateContext";
|
||||
import { StaticImageData } from "next/image";
|
||||
import * as Telegram from "../utils/telegram"
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "3.14.0",
|
||||
"classnames": "^2.3.1",
|
||||
"next": "12.1.6",
|
||||
"next-i18next": "^11.0.0",
|
||||
"prisma": "^3.14.0",
|
||||
"react": "18.1.0",
|
||||
"react-dom": "18.1.0",
|
||||
"react-storage-hooks": "^4.0.1",
|
||||
"react-telegram-login": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -4,21 +4,47 @@ import '../styles/postcard.css'
|
|||
import "../styles/index.css"
|
||||
import type { AppProps } from 'next/app'
|
||||
import { LoginContext } from '../contexts/login'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import * as Telegram from "../utils/telegram"
|
||||
import defaultPostcard from "../public/postcards/adi-goldstein-Hli3R6LKibo-unsplash.jpg"
|
||||
import { Postcard } from '../components/Postcard'
|
||||
import { PostcardContext } from '../contexts/postcard'
|
||||
import { StaticImageData } from 'next/image'
|
||||
import { appWithTranslation } from 'next-i18next'
|
||||
import { useStorageState } from 'react-storage-hooks'
|
||||
|
||||
const dummyStorage = {
|
||||
getItem: () => null,
|
||||
setItem: () => {},
|
||||
removeItem: () => {},
|
||||
};
|
||||
|
||||
const App = ({ Component, pageProps }: AppProps): JSX.Element => {
|
||||
const loginHook = useState<Telegram.LoginData | null>(null)
|
||||
const postcardHook = useState<string | StaticImageData>(defaultPostcard)
|
||||
const [login, setLogin] = useState<Telegram.LoginData | null>(null)
|
||||
const [postcard, setPostcard] = useState<string | StaticImageData>(defaultPostcard)
|
||||
|
||||
// Ha ha ha. Fooled you again, silly SSR!
|
||||
const thatStorageOverThere = typeof sessionStorage !== "undefined" ? sessionStorage : undefined
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if(thatStorageOverThere === undefined) return
|
||||
|
||||
const raw = sessionStorage.getItem("login")
|
||||
if(raw === null) return
|
||||
|
||||
const parsed = JSON.parse(raw) as Telegram.LoginData
|
||||
const response = new Telegram.LoginResponse(parsed)
|
||||
if(!response.isRecent) return
|
||||
|
||||
setLogin(parsed)
|
||||
},
|
||||
[thatStorageOverThere]
|
||||
)
|
||||
|
||||
return (
|
||||
<PostcardContext.Provider value={postcardHook}>
|
||||
<LoginContext.Provider value={loginHook}>
|
||||
<PostcardContext.Provider value={[postcard, setPostcard]}>
|
||||
<LoginContext.Provider value={[login, setLogin]}>
|
||||
<Postcard/>
|
||||
<Component {...pageProps} />
|
||||
</LoginContext.Provider>
|
||||
|
|
|
@ -1,6 +1 @@
|
|||
{
|
||||
"siteTitle": "Festa",
|
||||
"siteSubtitle": "Organizza il tuo evento ora!",
|
||||
"introTelegramLogin": "Effettua il login con Telegram per iniziare a creare il tuo evento.",
|
||||
"introTelegramLoggedIn": "Sei connesso come <1/>! Non sei tu?"
|
||||
}
|
||||
{}
|
8
public/locales/it-IT/common.json.disabled
Normal file
8
public/locales/it-IT/common.json.disabled
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"siteTitle": "Festa",
|
||||
"siteSubtitle": "Organizza il tuo evento ora!",
|
||||
"introTelegramLogin": "Effettua il login con Telegram per iniziare a creare il tuo evento.",
|
||||
"introTelegramLoggedIn": "Sei connesso come <1/>!",
|
||||
"introTelegramLogout": "Non sei tu?",
|
||||
"introCreateEvent": ""
|
||||
}
|
|
@ -1,7 +1,43 @@
|
|||
html, body {
|
||||
@media (prefers-color-scheme: light) {
|
||||
body {
|
||||
--background: #fafafa;
|
||||
--foreground: black;
|
||||
--border: darkgray;
|
||||
|
||||
--anchor: #4444ff;
|
||||
--anchor-visited: #aa44ff;
|
||||
--anchor-active: #ff4444;
|
||||
|
||||
--positive: #008800;
|
||||
--negative: #880000;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
--background: #050505;
|
||||
--foreground: white;
|
||||
--border: darkgray;
|
||||
|
||||
--anchor: #8888ff;
|
||||
--anchor-visited: #aa88ff;
|
||||
--anchor-active: #ff8888;
|
||||
|
||||
--positive: #88ff88;
|
||||
--negative: #ff8888;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
background-color: var(--background);
|
||||
|
||||
font-family: sans-serif;
|
||||
color: var(--foreground);
|
||||
text-shadow: 1px 1px 1px var(--background);
|
||||
}
|
||||
|
||||
* {
|
||||
|
@ -10,43 +46,61 @@ html, body {
|
|||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
text-shadow: 2px 2px 2px var(--background);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
body {
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-shadow: 1px 1px 1px white;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #4444ff;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #aa44ff;
|
||||
}
|
||||
a {
|
||||
color: var(--anchor);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
text-shadow: 1px 1px 1px black;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-shadow: 2px 2px 2px black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #8888ff;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #aa88ff;
|
||||
}
|
||||
a:visited {
|
||||
color: var(--anchor-visited);
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: var(--anchor-active);
|
||||
}
|
||||
|
||||
input, button {
|
||||
padding: 8px;
|
||||
margin: 2px 4px;
|
||||
|
||||
color: var(--foreground);
|
||||
background-color: var(--background);
|
||||
|
||||
border-width: 2px;
|
||||
border-color: var(--border);
|
||||
border-radius: 16px;
|
||||
|
||||
font-size: medium;
|
||||
height: 40px;
|
||||
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
border-style: inset;
|
||||
}
|
||||
|
||||
input[type="submit"], button {
|
||||
border-style: outset;
|
||||
}
|
||||
|
||||
input[type="submit"]:active, button:active {
|
||||
border-style: inset;
|
||||
}
|
||||
|
||||
.input-square {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.input-positive {
|
||||
border-color: var(--positive);
|
||||
color: var(--positive);
|
||||
}
|
||||
|
||||
.input-negative {
|
||||
border-color: var(--negative);
|
||||
color: var(--negative);
|
||||
}
|
|
@ -21,10 +21,8 @@
|
|||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.avatar-telegram {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
.avatar-telegram-inline {
|
||||
height: 1em;
|
||||
border-radius: 20px;
|
||||
|
||||
margin-left: 4px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react"
|
||||
import { useContext } from "react"
|
||||
import { createDefinedContext } from "./useDefinedContext"
|
||||
import { createDefinedContext } from "./definedContext"
|
||||
|
||||
/**
|
||||
* Create a new defined context (see {@link createDefinedContext}) containing the tuple returned by {@link React.useState} for the given type.
|
|
@ -1,6 +1,6 @@
|
|||
import nodecrypto from "crypto"
|
||||
import { ParsedUrlQuery } from "querystring"
|
||||
import * as QueryString from "./querystring"
|
||||
import * as QueryString from "./queryString"
|
||||
|
||||
/**
|
||||
* Serializable Telegram user data without any technical information.
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -391,6 +391,11 @@ chalk@^4.0.0:
|
|||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
classnames@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
||||
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
|
||||
|
||||
color-convert@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
||||
|
@ -1504,6 +1509,11 @@ react-is@^16.13.1, react-is@^16.7.0:
|
|||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-storage-hooks@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/react-storage-hooks/-/react-storage-hooks-4.0.1.tgz#e30ed5cda48c77c431ecc02ec3824bd615f5b7fb"
|
||||
integrity sha512-fetDkT5RDHGruc2NrdD1iqqoLuXgbx6AUpQSQLLkrCiJf8i97EtwJNXNTy3+GRfsATLG8TZgNc9lGRZOaU5yQA==
|
||||
|
||||
react-telegram-login@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/react-telegram-login/-/react-telegram-login-1.1.2.tgz#28b9bdd68bb2710afca19354ac1f9428092836f0"
|
||||
|
|
Loading…
Reference in a new issue