Add editing role name / colour.

Animate avatars on message hover.
Switch to 24-hour time format by default.
This commit is contained in:
Paul 2021-07-14 13:25:40 +01:00
parent 6f6020c474
commit 7374591458
9 changed files with 122 additions and 44 deletions

2
external/lang vendored

@ -1 +1 @@
Subproject commit 3931bf87e94264d92556a2f3ee96c6051be75b02 Subproject commit 9f72b064aad85293f332c3f7ce3f4fe5965def37

View file

@ -94,7 +94,7 @@
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scroll": "^1.8.2", "react-scroll": "^1.8.2",
"redux": "^4.1.0", "redux": "^4.1.0",
"revolt.js": "4.3.3-alpha.14", "revolt.js": "4.3.3-alpha.15",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sass": "^1.35.1", "sass": "^1.35.1",
"shade-blend-color": "^1.0.0", "shade-blend-color": "^1.0.0",

View file

@ -1,6 +1,6 @@
import { attachContextMenu } from "preact-context-menu"; import { attachContextMenu } from "preact-context-menu";
import { memo } from "preact/compat"; import { memo } from "preact/compat";
import { useContext } from "preact/hooks"; import { useContext, useState } from "preact/hooks";
import { QueuedMessage } from "../../../redux/reducers/queue"; import { QueuedMessage } from "../../../redux/reducers/queue";
@ -64,6 +64,9 @@ function Message({
const openProfile = () => const openProfile = () =>
openScreen({ id: "profile", user_id: message.author }); openScreen({ id: "profile", user_id: message.author });
// ! FIXME: animate on hover
const [animate, setAnimate] = useState(false);
return ( return (
<div id={message._id}> <div id={message._id}>
{message.replies?.map((message_id, index) => ( {message.replies?.map((message_id, index) => (
@ -88,7 +91,9 @@ function Message({
queued, queued,
}) })
: undefined : undefined
}> }
onMouseEnter={() => setAnimate(true)}
onMouseLeave={() => setAnimate(false)}>
<MessageInfo> <MessageInfo>
{head ? ( {head ? (
<UserIcon <UserIcon
@ -96,6 +101,7 @@ function Message({
size={36} size={36}
onContextMenu={userContext} onContextMenu={userContext}
onClick={openProfile} onClick={openProfile}
animate={animate}
/> />
) : ( ) : (
<MessageDetail message={message} position="left" /> <MessageDetail message={message} position="left" />

View file

@ -150,7 +150,10 @@ function Locale({ children, locale }: Props) {
const dayjs = obj.dayjs; const dayjs = obj.dayjs;
const defaults = dayjs.defaults; const defaults = dayjs.defaults;
const twelvehour = defaults?.twelvehour === "yes" || true; const twelvehour = defaults?.twelvehour
? defaults.twelvehour === "yes"
: false;
const separator: string = defaults?.date_separator ?? "/"; const separator: string = defaults?.date_separator ?? "/";
const date: "traditional" | "simplified" | "ISO8601" = const date: "traditional" | "simplified" | "ISO8601" =
defaults?.date_format ?? "traditional"; defaults?.date_format ?? "traditional";

View file

@ -141,7 +141,7 @@ export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
`linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url('${backgroundURL}')`, `linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url('${backgroundURL}')`,
}}> }}>
<div className={styles.profile}> <div className={styles.profile}>
<UserIcon size={80} target={user} status /> <UserIcon size={80} target={user} status animate />
<div className={styles.details}> <div className={styles.details}>
<Localizer> <Localizer>
<span <span

View file

@ -1,24 +1,41 @@
@keyframes open { @keyframes open {
0% {transform: scale(1.2);}; 0% {
100% {transform: scale(1);}; transform: scale(1.2);
}
100% {
transform: scale(1);
}
} }
@keyframes close { @keyframes close {
0% {transform: scale(1); opacity: 1;}; 0% {
100% {transform: scale(1.2); opacity: 0;}; transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1.2);
opacity: 0;
}
} }
@keyframes opacity { @keyframes opacity {
0% {opacity: 0;}; 0% {
20% {opacity: .5;} opacity: 0;
50% {opacity: 1;} }
20% {
opacity: 0.5;
}
50% {
opacity: 1;
}
} }
.settings[data-mobile="true"] { .settings[data-mobile="true"] {
flex-direction: column; flex-direction: column;
background: var(--primary-header); background: var(--primary-header);
.sidebar, .content { .sidebar,
.content {
background: var(--primary-background); background: var(--primary-background);
} }
@ -36,7 +53,6 @@
.version { .version {
place-items: center; place-items: center;
} }
} }
@ -52,8 +68,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
position: fixed; position: fixed;
animation: open .18s ease-out, animation: open 0.18s ease-out, opacity 0.18s;
opacity .18s;
} }
.settings { .settings {
@ -177,8 +192,8 @@
position: relative; position: relative;
color: var(--foreground); color: var(--foreground);
width: 40px; width: 40px;
opacity: .5; opacity: 0.5;
font-size: .75em; font-size: 0.75em;
} }
.closeButton { .closeButton {
@ -208,6 +223,10 @@
display: inline; display: inline;
} }
} }
section {
margin-bottom: 1em;
}
} }
.loader { .loader {

View file

@ -39,7 +39,8 @@
flex-direction: row; flex-direction: row;
background: var(--secondary-background); background: var(--secondary-background);
code, span { code,
span {
flex: 1; flex: 1;
} }
@ -95,10 +96,6 @@
flex-grow: 1; flex-grow: 1;
padding: 0 8px; padding: 0 8px;
overflow-y: scroll; overflow-y: scroll;
section {
margin-bottom: 1em;
}
} }
.title { .title {
@ -107,7 +104,8 @@
margin-bottom: 1em; margin-bottom: 1em;
align-items: center; align-items: center;
h1, h2 { h1,
h2 {
margin: 0; margin: 0;
min-width: 0; min-width: 0;
flex-grow: 1; flex-grow: 1;

View file

@ -15,6 +15,7 @@ import { AppContext } from "../../../context/revoltjs/RevoltClient";
import Button from "../../../components/ui/Button"; import Button from "../../../components/ui/Button";
import Checkbox from "../../../components/ui/Checkbox"; import Checkbox from "../../../components/ui/Checkbox";
import ColourSwatches from "../../../components/ui/ColourSwatches";
import IconButton from "../../../components/ui/IconButton"; import IconButton from "../../../components/ui/IconButton";
import InputBox from "../../../components/ui/InputBox"; import InputBox from "../../../components/ui/InputBox";
import Overline from "../../../components/ui/Overline"; import Overline from "../../../components/ui/Overline";
@ -40,21 +41,46 @@ export function Roles({ server }: Props) {
return null; return null;
} }
const v = (id: string) => function getPermissions(id: string) {
I32ToU32( return I32ToU32(
id === "default" id === "default"
? server.default_permissions ? server.default_permissions
: roles[id].permissions, : roles[id].permissions,
); );
const [perm, setPerm] = useState(v(role)); }
useEffect(() => setPerm(v(role)), [role, roles[role]?.permissions]);
const modified = !isEqual(perm, v(role)); const { name: roleName, colour: roleColour } = roles[role] ?? {};
const save = () =>
const [perm, setPerm] = useState(getPermissions(role));
const [name, setName] = useState(roleName);
const [colour, setColour] = useState(roleColour);
useEffect(
() => setPerm(getPermissions(role)),
[role, roles[role]?.permissions],
);
useEffect(() => setName(roleName), [role, roleName]);
useEffect(() => setColour(roleColour), [role, roleColour]);
const modified =
!isEqual(perm, getPermissions(role)) ||
!isEqual(name, roleName) ||
!isEqual(colour, roleColour);
const save = () => {
if (!isEqual(perm, getPermissions(role))) {
client.servers.setPermissions(server._id, role, { client.servers.setPermissions(server._id, role, {
server: perm[0], server: perm[0],
channel: perm[1], channel: perm[1],
}); });
}
if (!isEqual(name, roleName) || !isEqual(colour, roleColour)) {
client.servers.editRole(server._id, role, { name, colour });
}
};
const deleteRole = () => { const deleteRole = () => {
setRole("default"); setRole("default");
client.servers.deleteRole(server._id, role); client.servers.deleteRole(server._id, role);
@ -92,7 +118,8 @@ export function Roles({ server }: Props) {
return ( return (
<ButtonItem <ButtonItem
active={role === id} active={role === id}
onClick={() => setRole(id)}> onClick={() => setRole(id)}
style={{ color: roles[id].colour }}>
{roles[id].name} {roles[id].name}
</ButtonItem> </ButtonItem>
); );
@ -111,6 +138,31 @@ export function Roles({ server }: Props) {
Save Save
</Button> </Button>
</div> </div>
{role !== "default" && (
<>
<section>
<Overline type="subtle">Role Name</Overline>
<p>
<InputBox
value={name}
onChange={(e) =>
setName(e.currentTarget.value)
}
contrast
/>
</p>
</section>
<section>
<Overline type="subtle">Role Colour</Overline>
<p>
<ColourSwatches
value={colour ?? "gray"}
onChange={(value) => setColour(value)}
/>
</p>
</section>
</>
)}
<section> <section>
<Overline type="subtle"> <Overline type="subtle">
<Text id="app.settings.permissions.server" /> <Text id="app.settings.permissions.server" />

View file

@ -3563,10 +3563,10 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
revolt.js@4.3.3-alpha.14: revolt.js@4.3.3-alpha.15:
version "4.3.3-alpha.14" version "4.3.3-alpha.15"
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.14.tgz#f4912ee25725de6e43ba1d4fd8ab84c711678271" resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.15.tgz#e511ad500a20f658b15b7bad0fdb9e2a5465d1b1"
integrity sha512-4p3DhEu+GUKZxczCPXR2JM04fzGlFfZdwHYjgkgU48NgPXgzxQrSv4x0FjpyIIv3xNpuO59z35mYRMLxAnBXsQ== integrity sha512-24hIQEO+FIRIAQXITBH2qVvWH6LA1MeJW2/3lj6cqBgJz7lnb3ZNIXZBu5sHbUEJpIDtJiHcOEeaeh3sE2RwxA==
dependencies: dependencies:
"@insertish/mutable" "1.1.0" "@insertish/mutable" "1.1.0"
axios "^0.19.2" axios "^0.19.2"