Add i18n to replies.

Add Fluent design category button.
Update Account page with new design.
This commit is contained in:
Paul 2021-08-04 16:03:38 +01:00
parent 8cc92e0c42
commit fe75f5a6ca
9 changed files with 229 additions and 222 deletions

2
external/lang vendored

@ -1 +1 @@
Subproject commit da8e3f8bb3cc01867e2f3fb731e2a5a26cdab0f5 Subproject commit ede822613d642a345595f690ab99e35c78aa09c4

View file

@ -187,12 +187,18 @@ export const MessageReply = observer(({ index, channel, id }: Props) => {
); );
} }
}}> }}>
{message.attachments && ( {message.attachments &&
message.attachments.length > 0 && (
<> <>
<File size={16} /> <File size={16} />
<em>{message.attachments.length > 1 ? <em>
"Sent multiple attachments" : {message.attachments.length >
"Sent an attachment"}</em> 0 ? (
<Text id="app.main.channel.misc.sent_multiple_files" />
) : (
<Text id="app.main.channel.misc.sent_file" />
)}
</em>
</> </>
)} )}
<Markdown <Markdown

View file

@ -32,25 +32,12 @@ const Base = styled.div`
user-select: none; user-select: none;
align-items: center; align-items: center;
background: var(--message-box); background: var(--message-box);
.username {
display: flex;
align-items: center;
gap: 6px;
font-weight: 600;
}
}
> div { > div {
flex-grow: 1; flex-grow: 1;
margin-bottom: 0; margin-bottom: 0;
} }
.actions {
gap: 12px;
display: flex;
}
.toggle { .toggle {
gap: 4px; gap: 4px;
display: flex; display: flex;
@ -59,6 +46,22 @@ const Base = styled.div`
font-weight: 600; font-weight: 600;
} }
.username {
display: flex;
align-items: center;
gap: 6px;
font-weight: 600;
}
.message {
display: flex;
}
.actions {
gap: 12px;
display: flex;
}
/*@media (pointer: coarse) { //FIXME: Make action buttons bigger on pointer coarse /*@media (pointer: coarse) { //FIXME: Make action buttons bigger on pointer coarse
.actions > svg { .actions > svg {
height: 25px; height: 25px;
@ -109,14 +112,20 @@ export default observer(({ channel, replies, setReplies }: Props) => {
<UserShort user={message.author} size={16} /> <UserShort user={message.author} size={16} />
</div> </div>
<div class="message"> <div class="message">
{message.attachments && ( {message.attachments &&
<> message.attachments.length > 0 && (
<File size={16} /> <>
<em>{message.attachments.length > 1 ? <File size={16} />
"Sent multiple attachments" : <em>
"Sent an attachment"}</em> {message.attachments!.length >
</> 1 ? (
)} <Text id="app.main.channel.misc.sent_multiple_files" />
) : (
<Text id="app.main.channel.misc.sent_file" />
)}
</em>
</>
)}
{message.author_id === SYSTEM_USER_ID ? ( {message.author_id === SYSTEM_USER_ID ? (
<SystemMessage message={message} /> <SystemMessage message={message} />
) : ( ) : (

View file

@ -8,13 +8,19 @@ type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, "children" | "as"> & {
error?: string; error?: string;
block?: boolean; block?: boolean;
spaced?: boolean; spaced?: boolean;
noMargin?: boolean;
children?: Children; children?: Children;
type?: "default" | "subtle" | "error"; type?: "default" | "subtle" | "error";
}; };
const OverlineBase = styled.div<Omit<Props, "children" | "error">>` const OverlineBase = styled.div<Omit<Props, "children" | "error">>`
display: inline; display: inline;
margin: 0.4em 0;
${(props) =>
!props.noMargin &&
css`
margin: 0.4em 0;
`}
${(props) => ${(props) =>
props.spaced && props.spaced &&

View file

@ -1,4 +1,5 @@
import styled from "styled-components"; import { ChevronRight } from "@styled-icons/boxicons-regular";
import styled, { css } from "styled-components";
import { Children } from "../../../types/Preact"; import { Children } from "../../../types/Preact";
@ -14,13 +15,69 @@ const CategoryBase = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: row; flex-direction: row;
.content {
display: flex;
flex-grow: 1;
flex-direction: column;
font-weight: 600;
font-size: 14px;
.description {
font-size: 11px;
font-weight: 400;
a:hover {
text-decoration: underline;
}
}
}
${(props) =>
typeof props.onClick !== "undefined" &&
css`
transition: 0.1s ease background-color;
&:hover {
background: var(--tertiary-background);
}
a {
cursor: pointer;
}
`}
`; `;
interface Props { interface Props {
icon?: Children; icon?: Children;
children?: Children; children?: Children;
description?: Children;
onClick?: () => void;
action?: "chevron" | Children;
} }
export default function CategoryButton({ icon, children }: Props) { export default function CategoryButton({
return <CategoryBase>{icon}</CategoryBase>; icon,
children,
description,
onClick,
action,
}: Props) {
return (
<CategoryBase onClick={onClick}>
{icon}
<div class="content">
{children}
<div className="description">{description}</div>
</div>
<div class="action">
{typeof action === "string" ? (
<ChevronRight size={24} />
) : (
action
)}
</div>
</CategoryBase>
);
} }

View file

@ -80,9 +80,9 @@ export const Languages: { [key in Language]: LanguageEntry } = {
fr: { display: "Français", emoji: "🇫🇷", i18n: "fr" }, fr: { display: "Français", emoji: "🇫🇷", i18n: "fr" },
hi: { display: "हिन्दी", emoji: "🇮🇳", i18n: "hi" }, hi: { display: "हिन्दी", emoji: "🇮🇳", i18n: "hi" },
hr: { display: "Hrvatski", emoji: "🇭🇷", i18n: "hr" }, hr: { display: "Hrvatski", emoji: "🇭🇷", i18n: "hr" },
hu: { display: "magyar", emoji: "🇭🇺", i18n: "hu" }, hu: { display: "Magyar", emoji: "🇭🇺", i18n: "hu" },
id: { display: "bahasa Indonesia", emoji: "🇮🇩", i18n: "id" }, id: { display: "bahasa Indonesia", emoji: "🇮🇩", i18n: "id" },
it: { display: "italiano", emoji: "🇮🇹", i18n: "it" }, it: { display: "Italiano", emoji: "🇮🇹", i18n: "it" },
lt: { display: "Lietuvių", emoji: "🇱🇹", i18n: "lt" }, lt: { display: "Lietuvių", emoji: "🇱🇹", i18n: "lt" },
mk: { display: "Македонски", emoji: "🇲🇰", i18n: "mk" }, mk: { display: "Македонски", emoji: "🇲🇰", i18n: "mk" },
nl: { display: "Nederlands", emoji: "🇳🇱", i18n: "nl" }, nl: { display: "Nederlands", emoji: "🇳🇱", i18n: "nl" },

View file

@ -1,5 +1,5 @@
export const stopPropagation = ( export const stopPropagation = (
ev: JSX.TargetedMouseEvent<HTMLDivElement>, ev: JSX.TargetedMouseEvent<HTMLElement>,
_consume?: any, _consume?: any,
) => { ) => {
ev.preventDefault(); ev.preventDefault();

View file

@ -1,5 +1,10 @@
import { At, Key, Block } from "@styled-icons/boxicons-regular"; import { At, Key, Block } from "@styled-icons/boxicons-regular";
import { Envelope, HelpCircle, Lock, Trash } from "@styled-icons/boxicons-solid"; import {
Envelope,
HelpCircle,
Lock,
Trash,
} from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Link, useHistory } from "react-router-dom"; import { Link, useHistory } from "react-router-dom";
import { Profile } from "revolt-api/types/Users"; import { Profile } from "revolt-api/types/Users";
@ -8,6 +13,8 @@ import styles from "./Panes.module.scss";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks"; import { useContext, useEffect, useState } from "preact/hooks";
import { stopPropagation } from "../../../lib/stopPropagation";
import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { import {
ClientStatus, ClientStatus,
@ -18,7 +25,9 @@ import {
import Tooltip from "../../../components/common/Tooltip"; import Tooltip from "../../../components/common/Tooltip";
import UserIcon from "../../../components/common/user/UserIcon"; import UserIcon from "../../../components/common/user/UserIcon";
import Button from "../../../components/ui/Button"; import Button from "../../../components/ui/Button";
import Overline from "../../../components/ui/Overline";
import Tip from "../../../components/ui/Tip"; import Tip from "../../../components/ui/Tip";
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
export const Account = observer(() => { export const Account = observer(() => {
const { openScreen, writeClipboard } = useIntermediate(); const { openScreen, writeClipboard } = useIntermediate();
@ -62,25 +71,29 @@ export const Account = observer(() => {
<div className={styles.userDetail}> <div className={styles.userDetail}>
@{client.user!.username} @{client.user!.username}
<div className={styles.userid}> <div className={styles.userid}>
<Tooltip content={<Text id="app.settings.pages.account.unique_id" />}> <Tooltip
content={
<Text id="app.settings.pages.account.unique_id" />
}>
<HelpCircle size={16} /> <HelpCircle size={16} />
</Tooltip> </Tooltip>
<Tooltip content={<Text id="app.special.copy" />}> <Tooltip content={<Text id="app.special.copy" />}>
<a onClick={() => writeClipboard(client.user!._id)}> <a
onClick={() =>
writeClipboard(client.user!._id)
}>
{client.user!._id} {client.user!._id}
</a> </a>
</Tooltip> </Tooltip>
</div> </div>
</div> </div>
</div> </div>
<Button <Button onClick={() => switchPage("profile")} contrast>
onClick={() => switchPage("profile")} <Text id="app.settings.pages.profile.edit_profile" />
contrast>
Edit Profile
</Button> </Button>
</div> </div>
<div className={styles.details}> <div>
{( {(
[ [
["username", client.user!.username, <At size={24} />], ["username", client.user!.username, <At size={24} />],
@ -88,64 +101,64 @@ export const Account = observer(() => {
["password", "•••••••••", <Key size={24} />], ["password", "•••••••••", <Key size={24} />],
] as const ] as const
).map(([field, value, icon]) => ( ).map(([field, value, icon]) => (
<div> <CategoryButton
{icon} icon={icon}
<div className={styles.detail}> description={
<div className={styles.subtext}> field === "email" ? (
<Text id={`login.${field}`} /> revealEmail ? (
</div> <>
<div className={styles.entry}> {value}{" "}
{field === "email" ? ( <a
revealEmail ? ( onClick={(ev) =>
<> stopPropagation(
{value}{" "} ev,
<a setRevealEmail(false),
onClick={() => )
setRevealEmail(false) }>
}> <Text id="app.special.modals.actions.hide" />
<Text id="app.special.modals.actions.hide" /> </a>
</a> </>
</>
) : (
<>
@{value.split("@").pop()}{" "}
<a
onClick={() =>
setRevealEmail(true)
}>
<Text id="app.special.modals.actions.reveal" />
</a>
</>
)
) : ( ) : (
value <>
)} @{value.split("@").pop()}{" "}
</div> <a
</div> onClick={(ev) =>
<div> stopPropagation(
<Button ev,
onClick={() => setRevealEmail(true),
openScreen({ )
id: "modify_account", }>
field, <Text id="app.special.modals.actions.reveal" />
}) </a>
} </>
plain> )
<Text id="app.settings.pages.account.change_field" /> ) : (
</Button> value
</div> )
</div> }
action="chevron"
onClick={() =>
openScreen({
id: "modify_account",
field,
})
}>
<Overline type="subtle" noMargin>
<Text id={`login.${field}`} />
</Overline>
</CategoryButton>
))} ))}
</div> </div>
<h3> <h3>
<Text id="app.settings.pages.account.2fa.title" /> <Text id="app.settings.pages.account.2fa.title" />
</h3> </h3>
<h5><Text id="app.settings.pages.account.2fa.description" /></h5> <h5>
<div className={styles.entrytest}> <Text id="app.settings.pages.account.2fa.description" />
<Lock size={24}/> </h5>
<div className={styles.content}> <CategoryButton
Currently not available icon={<Lock size={24} color="var(--error)" />}
<div className={styles.description}> description={
<>
Two-factor auth is currently work-in-progress, see{" "} Two-factor auth is currently work-in-progress, see{" "}
<a <a
href="https://gitlab.insrt.uk/insert/rauth/-/issues/2" href="https://gitlab.insrt.uk/insert/rauth/-/issues/2"
@ -154,54 +167,45 @@ export const Account = observer(() => {
tracking issue here tracking issue here
</a> </a>
. .
</div> </>
</div> }
<Button accent compact disabled> action={
<Text id="app.settings.pages.account.2fa.add_auth" /> <Button accent compact disabled>
</Button> <Text id="app.settings.pages.account.2fa.add_auth" />
</div> </Button>
}>
Currently not available
</CategoryButton>
<h3> <h3>
<Text id="app.settings.pages.account.manage.title" /> <Text id="app.settings.pages.account.manage.title" />
</h3> </h3>
<h5> <h5>
<Text id="app.settings.pages.account.manage.description" /> <Text id="app.settings.pages.account.manage.description" />
</h5> </h5>
<div className={styles.entrytest}> <CategoryButton
<Block size={24}/> icon={<Block size={24} color="var(--error)" />}
<div className={styles.content}> description={
<Text id="app.settings.pages.account.manage.disable" /> "Disable your account. You won't be able to access it unless you log back in."
<div className={styles.description}> }
Disable your account. You won't be able to access it unless you log back in. action={
</div> <Button accent compact disabled>
</div> <Text id="general.unavailable" />
<Button accent compact disabled>
Unavailable
</Button>
</div>
<div className={styles.entrytest}>
<Trash size={24}/>
<div className={styles.content}>
<Text id="app.settings.pages.account.manage.delete" />
<div className={styles.description}>
Delete your account, including all of your data.
</div>
</div>
<a href="mailto:contact@revolt.chat?subject=Delete%20my%20account">
<Button error compact>
<Text id="app.settings.pages.account.manage.delete" />
</Button> </Button>
</a> }>
</div> <Text id="app.settings.pages.account.manage.disable" />
<div className={styles.buttons}> </CategoryButton>
{/* <Button contrast> <CategoryButton
<Text id="app.settings.pages.account.manage.disable" /> icon={<Trash size={24} color="var(--error)" />}
</Button> description={"Delete your account, including all of your data."}
<a href="mailto:contact@revolt.chat?subject=Delete%20my%20account"> action={
<Button error compact> <a href="mailto:contact@revolt.chat?subject=Delete%20my%20account">
<Text id="app.settings.pages.account.manage.delete" /> <Button error compact>
</Button> <Text id="app.settings.pages.account.manage.delete" />
</a>*/} </Button>
</div> </a>
}>
<Text id="app.settings.pages.account.manage.delete" />
</CategoryButton>
<Tip> <Tip>
<span> <span>
<Text id="app.settings.tips.account.a" /> <Text id="app.settings.tips.account.a" />

View file

@ -78,42 +78,6 @@
} }
} }
.detail {
flex-grow: 1;
min-width: 0;
display: flex;
flex-direction: column;
.subtext {
display: inline;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
color: var(--secondary-foreground);
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.entry {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-size: 15px;
}
a {
font-size: .875rem;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}
p { p {
margin: 0; margin: 0;
font-size: 1rem; font-size: 1rem;
@ -126,7 +90,7 @@
display: grid; display: grid;
place-items: center; place-items: center;
grid-template-columns: minmax(auto, 100%); grid-template-columns: minmax(auto, 100%);
> div { > div {
width: 100%; width: 100%;
max-width: 560px; max-width: 560px;
@ -152,40 +116,6 @@
display: flex; display: flex;
gap: 12px; gap: 12px;
} }
.entrytest {
gap: 12px;
/* padding: 4px; */
height: 54px;
padding: 8px 12px;
display: flex;
align-items: center;
flex-direction: row;
background: var(--secondary-header);
border-radius: 6px;
margin-bottom: 10px;
> svg {
color: var(--error);
}
.content {
display: flex;
flex-grow: 1;
flex-direction: column;
font-weight: 600;
font-size: 14px;
.description {
font-size: 11px;
font-weight: 400;
a:hover {
text-decoration: underline;
}
}
}
}
} }
@media only screen and (max-width: 800px) { @media only screen and (max-width: 800px) {
@ -236,14 +166,13 @@
} }
details { details {
summary { summary {
font-size: .8125rem; font-size: 0.8125rem;
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
color: var(--secondary-foreground); color: var(--secondary-foreground);
cursor: pointer; cursor: pointer;
} }
} }
.emojiPack { .emojiPack {
@ -297,15 +226,12 @@
text-transform: unset; text-transform: unset;
a { a {
opacity: 0.7; opacity: 0.7;
color: var(--accent); color: var(--accent);
font-weight: 600; font-weight: 600;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
} }
} }
@media only screen and (max-width: 800px) { @media only screen and (max-width: 800px) {
@ -331,7 +257,7 @@
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
font-size: .875rem; font-size: 0.875rem;
min-width: 0; min-width: 0;
flex-grow: 1; flex-grow: 1;
padding: 8px; padding: 8px;
@ -352,7 +278,7 @@
row-gap: 8px; row-gap: 8px;
display: grid; display: grid;
column-gap: 16px; column-gap: 16px;
grid-template-columns: repeat(auto-fill,minmax(200px,1fr)); grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
margin-bottom: 20px; margin-bottom: 20px;
.entry { .entry {
@ -365,7 +291,7 @@
flex: 1; flex: 1;
display: block; display: block;
font-weight: 600; font-weight: 600;
font-size: .875rem; font-size: 0.875rem;
margin-bottom: 8px; margin-bottom: 8px;
text-transform: capitalize; text-transform: capitalize;
@ -427,7 +353,7 @@
display: flex; display: flex;
gap: 12px; gap: 12px;
flex-grow: 1; flex-grow: 1;
svg { svg {
margin-top: 1px; margin-top: 1px;
} }
@ -452,7 +378,6 @@
border-bottom: 2px solid var(--primary-background); border-bottom: 2px solid var(--primary-background);
} }
} }
} }
&[data-deleting="true"] { &[data-deleting="true"] {
@ -487,7 +412,7 @@
.label { .label {
margin-bottom: 8px; margin-bottom: 8px;
color: var(--primary-text); color: var(--primary-text);
font-size: .75rem; font-size: 0.75rem;
font-weight: 600; font-weight: 600;
} }
@ -507,7 +432,7 @@
} }
.time { .time {
font-size: .75rem; font-size: 0.75rem;
color: var(--teriary-text); color: var(--teriary-text);
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
@ -524,7 +449,7 @@
align-items: unset; align-items: unset;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
> button { > button {
width: 100%; width: 100%;
} }
@ -542,7 +467,7 @@
flex-direction: column; flex-direction: column;
margin-bottom: 1em; margin-bottom: 1em;
gap: 8px; gap: 8px;
.entry { .entry {
display: flex; display: flex;
height: 45px; height: 45px;
@ -601,4 +526,4 @@
section { section {
margin-bottom: 20px; margin-bottom: 20px;
} }