improv tos page

This commit is contained in:
Roj Serbest 2021-11-05 20:04:19 +03:00
parent 03fc697dd9
commit 9187eb0253
11 changed files with 246 additions and 75 deletions

11
components/Header.tsx Normal file
View File

@ -0,0 +1,11 @@
import { AppBar, Typography } from "@mui/material";
export default function Header() {
return (
<AppBar position="sticky" color="transparent">
<Typography component="h1" variant="h4">
Socialvoid
</Typography>
</AppBar>
);
}

82
components/Post.tsx Normal file
View File

@ -0,0 +1,82 @@
import {
Card,
CardHeader,
CardContent,
Button,
Typography,
CardActions,
} from "@mui/material";
import {
Star,
StarOutline,
ChangeCircle,
ChangeCircleOutlined,
} from "@mui/icons-material";
import { Post as PostType } from "socialvoid";
import { useState } from "react";
import { unparse } from "../utils";
export default function Post({ post }: { post: PostType }) {
const [liked, setLiked] = useState(post.flags.includes("liked"));
const [reposted, setReposted] = useState(post.flags.includes("reposted"));
const like = async () => {
if (liked) {
setLiked(false);
} else {
setLiked(true);
}
};
const repost = async () => {
console.log(reposted);
if (reposted) {
setReposted(false);
} else {
setReposted(true);
}
};
return (
<Card>
<CardHeader
title={post.peer?.name}
subheader={"@" + post.peer?.username}
/>
<CardContent>
<Typography
variant="body2"
color="text.secondary"
dangerouslySetInnerHTML={{
__html: unparse(post.text!, post.entities!),
}}
></Typography>
</CardContent>
<CardActions
sx={{
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-evenly",
}}
>
<Button
color="success"
startIcon={liked ? <Star /> : <StarOutline />}
onClick={like}
>
{" "}
{post.like_count}
</Button>
<Button
color="info"
startIcon={reposted ? <ChangeCircle /> : <ChangeCircleOutlined />}
onClick={repost}
>
{" "}
{post.like_count}
</Button>
</CardActions>
</Card>
);
}

View File

@ -1,20 +0,0 @@
import Typography from "@mui/material/Typography";
import Link from "@mui/material/Link";
export default function Copyright(props: any) {
return (
<Typography
variant="body2"
color="text.secondary"
align="center"
{...props}
>
{"Copyright © "}
<Link color="inherit" href="https://rojser.best">
Socialvoid Next
</Link>{" "}
{new Date().getFullYear()}
{"."}
</Typography>
);
}

View File

@ -20,7 +20,7 @@
"pullstate": "^1.23.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"socialvoid": "0.0.0-alpha.3"
"socialvoid": "^0.0.0-alpha.7"
},
"devDependencies": {
"@types/nprogress": "^0.2.0",

View File

@ -1,10 +1,14 @@
import { useMemo } from "react";
import { AppProps } from "next/app";
import useMediaQuery from "@mui/material/useMediaQuery";
import CssBaseline from "@mui/material/CssBaseline";
import Container from "@mui/material/Container";
import { createTheme, ThemeProvider } from "@mui/material/styles";
import {
createTheme,
ThemeProvider,
useMediaQuery,
CssBaseline,
Container,
} from "@mui/material";
import { SnackbarProvider } from "notistack";
import Header from "../components/Header";
function App({ Component, pageProps }: AppProps) {
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
@ -23,7 +27,7 @@ function App({ Component, pageProps }: AppProps) {
<ThemeProvider theme={theme}>
<CssBaseline />
<SnackbarProvider>
<Container component="main" maxWidth="md" sx={{ mt: 3 }}>
<Container component="main" maxWidth="sm" sx={{ mt: 3 }}>
<Component {...pageProps} />
</Container>
</SnackbarProvider>

View File

@ -1,34 +0,0 @@
export default function Test() {
const a = "Hello".repeat(10);
return (
<>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
<p>{a}</p>
</>
);
}

32
pages/timeline.tsx Normal file
View File

@ -0,0 +1,32 @@
import { useState } from "react";
import { useRouter } from "next/router";
import { useSnackbar } from "notistack";
import Post from "../components/Post";
import { dispatch } from "../utils";
import { Post as PostType } from "socialvoid";
export default function Timeline() {
const router = useRouter();
const snackbar = useSnackbar();
const [posts, setPosts] = useState<PostType[]>();
dispatch(
async (client) => {
const posts = await client.timeline.retrieveFeed();
setPosts(posts);
},
router,
snackbar,
{
requireToBeAuthenticated: true,
}
);
return (
<>
{posts?.map((post) => (
<Post post={post} />
))}
</>
);
}

View File

@ -6,14 +6,18 @@ import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import FormControlLabel from "@mui/material/FormControlLabel";
import { useSnackbar } from "notistack";
import { pushWithQuery, dispatch } from "../utils";
import { pushWithQuery, dispatch, unparse } from "../utils";
import { HelpDocument } from "socialvoid";
export default function TOS() {
const router = useRouter();
const snackbar = useSnackbar();
const [tosId, setTosId] = useState("");
const [text, setText] = useState("Loading...");
const [document, setDocument] = useState<HelpDocument>({
id: "",
text: "Loading...",
entities: [],
});
const [disabled, setDisabled] = useState(true);
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
@ -34,9 +38,8 @@ export default function TOS() {
dispatch(
async (client) => {
const { id, text } = await client.help.getTermsOfService();
setTosId(id);
setText(text);
const document = await client.help.getTermsOfService();
setDocument(document);
setDisabled(false);
},
router,
@ -46,12 +49,14 @@ export default function TOS() {
return (
<>
<Typography component="h1" variant="h5" hidden={disabled}>
Socialvoid - Terms of Service
</Typography>
<p>{text}</p>
<Typography
variant="body1"
dangerouslySetInnerHTML={{
__html: unparse(document.text, document.entities),
}}
></Typography>
<Box component="form" noValidate sx={{ mt: 3 }} onSubmit={handleSubmit}>
<input type="hidden" name="tosId" value={tosId} />
<input type="hidden" name="tosId" value={document.id} />
<FormControlLabel
control={
<Checkbox

View File

@ -3,3 +3,4 @@ export * from "./dispatch";
export * from "./router";
export * from "./params";
export * from "./validation";
export * from "./parser";

90
utils/parser.ts Normal file
View File

@ -0,0 +1,90 @@
import { TextEntity } from "socialvoid";
function is(x: TextEntity, y: TextEntity) {
return (
x.offset == y.offset &&
x.length == y.length &&
x.type == y.type &&
x.value == y.value
);
}
function htmlEscape(s: string) {
return s
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function _unparse(
text: string,
entities: TextEntity[],
offset: number,
length: number
) {
let noffset = offset;
let ntext = "";
for (const entity of entities) {
if (offset >= entity.length + entity.offset) {
continue;
}
ntext += htmlEscape(text.slice(offset, entity.offset));
const netntities = entities.filter(
(entity2) =>
entity.offset + entity.length >= entity2.offset && !is(entity, entity2)
);
let atext = "";
if (netntities) {
atext = _unparse(text, netntities, entity.offset, entity.length);
} else {
atext = htmlEscape(
text.slice(entity.offset, entity.offset + entity.length)
);
}
switch (entity.type) {
case "BOLD":
ntext += `<b>${atext}</b>`;
break;
case "ITALIC":
ntext += `<i>${atext}</i>`;
break;
case "CODE":
ntext += `<code>${atext}</code>`;
break;
case "STRIKE":
ntext += `<s>${atext}</s>`;
break;
case "UNDERLINE":
ntext += `<u>${atext}</u>`;
break;
case "URL":
ntext += `<a href="${htmlEscape(entity.value!)}">${atext}</a>`;
break;
case "MENTION":
ntext += `<a href="sv://peer/${encodeURIComponent(
entity.value!
)}">${atext}</a>`;
break;
case "HASHTAG":
ntext += atext;
}
offset = entity.offset + entity.length;
}
return ntext + htmlEscape(text.slice(offset, length + noffset));
}
export function unparse(text: string, entities: TextEntity[]) {
entities = entities.sort((a) => a.offset - a.length);
return _unparse(text, entities, 0, text.length).replace(/\n/g, "<br />");
}

View File

@ -3215,10 +3215,10 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
socialvoid@0.0.0-alpha.3:
version "0.0.0-alpha.3"
resolved "https://registry.yarnpkg.com/socialvoid/-/socialvoid-0.0.0-alpha.3.tgz#84b5bafc1ca93fc23411801deba60b350e138a10"
integrity sha512-Ljznz3Yp+1Sf3hRQnMzNhVevyhPz9PUi13h/SNNRqgu2ttAnE7ee+PQJjGaArYvVjfTScWezLz36tPw5O82paQ==
socialvoid@^0.0.0-alpha.7:
version "0.0.0-alpha.7"
resolved "https://registry.yarnpkg.com/socialvoid/-/socialvoid-0.0.0-alpha.7.tgz#524d93ca5fa6d4d422118c4f7bf971ff71cc5ad5"
integrity sha512-5S2d2GCBqO2IWSL4RA0eC2UDpsKYwWFQANcLL0jDodQUakfG2DJ3e5hV7FWW59fTRqYpQ9ykNOgjJWYmQlB8sQ==
dependencies:
form-data "^4.0.0"
node-fetch "^2.6.5"