Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Showing
with 293 additions and 410 deletions
import { Plus } from "@styled-icons/boxicons-regular"; import { Plus } from "@styled-icons/boxicons-regular";
import isEqual from "lodash.isequal"; import isEqual from "lodash.isequal";
import { Servers } from "revolt.js/dist/api/objects"; import { observer } from "mobx-react-lite";
import { import { ChannelPermission, ServerPermission } from "revolt.js";
ChannelPermission, import { Server } from "revolt.js/dist/maps/Servers";
ServerPermission,
} from "revolt.js/dist/api/permissions";
import styles from "./Panes.module.scss"; import styles from "./Panes.module.scss";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useEffect, useState } from "preact/hooks"; import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { useIntermediate } from "../../../context/intermediate/Intermediate";
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 ColourSwatches from "../../../components/ui/ColourSwatches";
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";
import Tip from "../../../components/ui/Tip";
import ButtonItem from "../../../components/navigation/items/ButtonItem"; import ButtonItem from "../../../components/navigation/items/ButtonItem";
interface Props { interface Props {
server: Servers.Server; server: Server;
} }
const I32ToU32 = (arr: number[]) => arr.map((x) => x >>> 0); const I32ToU32 = (arr: number[]) => arr.map((x) => x >>> 0);
// ! FIXME: bad code :) // ! FIXME: bad code :)
export function Roles({ server }: Props) { export const Roles = observer(({ server }: Props) => {
const [role, setRole] = useState("default"); const [role, setRole] = useState("default");
const { openScreen } = useIntermediate(); const { openScreen } = useIntermediate();
const client = useContext(AppContext); const roles = useMemo(() => server.roles ?? {}, [server]);
const roles = server.roles ?? {};
if (role !== "default" && typeof roles[role] === "undefined") { if (role !== "default" && typeof roles[role] === "undefined") {
useEffect(() => setRole("default")); useEffect(() => setRole("default"), [role]);
return null; return null;
} }
function getPermissions(id: string) { const {
return I32ToU32( name: roleName,
id === "default" colour: roleColour,
? server.default_permissions permissions,
: roles[id].permissions, } = roles[role] ?? {};
);
} const getPermissions = useCallback(
(id: string) => {
const { name: roleName, colour: roleColour } = roles[role] ?? {}; return I32ToU32(
id === "default"
? server.default_permissions
: roles[id].permissions,
);
},
[roles, server],
);
const [perm, setPerm] = useState(getPermissions(role)); const [perm, setPerm] = useState(getPermissions(role));
const [name, setName] = useState(roleName); const [name, setName] = useState(roleName);
...@@ -57,7 +58,7 @@ export function Roles({ server }: Props) { ...@@ -57,7 +58,7 @@ export function Roles({ server }: Props) {
useEffect( useEffect(
() => setPerm(getPermissions(role)), () => setPerm(getPermissions(role)),
[role, roles[role]?.permissions], [getPermissions, role, permissions],
); );
useEffect(() => setName(roleName), [role, roleName]); useEffect(() => setName(roleName), [role, roleName]);
...@@ -70,20 +71,20 @@ export function Roles({ server }: Props) { ...@@ -70,20 +71,20 @@ export function Roles({ server }: Props) {
const save = () => { const save = () => {
if (!isEqual(perm, getPermissions(role))) { if (!isEqual(perm, getPermissions(role))) {
client.servers.setPermissions(server._id, role, { server.setPermissions(role, {
server: perm[0], server: perm[0],
channel: perm[1], channel: perm[1],
}); });
} }
if (!isEqual(name, roleName) || !isEqual(colour, roleColour)) { if (!isEqual(name, roleName) || !isEqual(colour, roleColour)) {
client.servers.editRole(server._id, role, { name, colour }); server.editRole(role, { name, colour });
} }
}; };
const deleteRole = () => { const deleteRole = () => {
setRole("default"); setRole("default");
client.servers.deleteRole(server._id, role); server.deleteRole(role);
}; };
return ( return (
...@@ -99,7 +100,7 @@ export function Roles({ server }: Props) { ...@@ -99,7 +100,7 @@ export function Roles({ server }: Props) {
openScreen({ openScreen({
id: "special_input", id: "special_input",
type: "create_role", type: "create_role",
server: server._id, server,
callback: (id) => setRole(id), callback: (id) => setRole(id),
}) })
} }
...@@ -117,6 +118,7 @@ export function Roles({ server }: Props) { ...@@ -117,6 +118,7 @@ export function Roles({ server }: Props) {
} }
return ( return (
<ButtonItem <ButtonItem
key={id}
active={role === id} active={role === id}
onClick={() => setRole(id)} onClick={() => setRole(id)}
style={{ color: roles[id].colour }}> style={{ color: roles[id].colour }}>
...@@ -176,6 +178,7 @@ export function Roles({ server }: Props) { ...@@ -176,6 +178,7 @@ export function Roles({ server }: Props) {
return ( return (
<Checkbox <Checkbox
key={key}
checked={(perm[0] & value) > 0} checked={(perm[0] & value) > 0}
onChange={() => onChange={() =>
setPerm([perm[0] ^ value, perm[1]]) setPerm([perm[0] ^ value, perm[1]])
...@@ -201,6 +204,7 @@ export function Roles({ server }: Props) { ...@@ -201,6 +204,7 @@ export function Roles({ server }: Props) {
return ( return (
<Checkbox <Checkbox
key={key}
checked={((perm[1] >>> 0) & value) > 0} checked={((perm[1] >>> 0) & value) > 0}
onChange={() => onChange={() =>
setPerm([perm[0], perm[1] ^ value]) setPerm([perm[0], perm[1] ^ value])
...@@ -227,4 +231,4 @@ export function Roles({ server }: Props) { ...@@ -227,4 +231,4 @@ export function Roles({ server }: Props) {
</div> </div>
</div> </div>
); );
} });
// eslint-disable-next-line @typescript-eslint/no-unused-vars /* eslint-disable */
import JSX = preact.JSX; import JSX = preact.JSX;
import localForage from "localforage"; import localForage from "localforage";
import { createStore } from "redux"; import { createStore } from "redux";
import { Core } from "revolt.js/dist/api/objects"; import { RevoltConfiguration } from "revolt-api/types/Core";
import { Language } from "../context/Locale"; import { Language } from "../context/Locale";
...@@ -14,17 +14,15 @@ import { QueuedMessage } from "./reducers/queue"; ...@@ -14,17 +14,15 @@ import { QueuedMessage } from "./reducers/queue";
import { SectionToggle } from "./reducers/section_toggle"; import { SectionToggle } from "./reducers/section_toggle";
import { Settings } from "./reducers/settings"; import { Settings } from "./reducers/settings";
import { SyncOptions } from "./reducers/sync"; import { SyncOptions } from "./reducers/sync";
import { Typing } from "./reducers/typing";
import { Unreads } from "./reducers/unreads"; import { Unreads } from "./reducers/unreads";
export type State = { export type State = {
config: Core.RevoltNodeConfiguration; config: RevoltConfiguration;
locale: Language; locale: Language;
auth: AuthState; auth: AuthState;
settings: Settings; settings: Settings;
unreads: Unreads; unreads: Unreads;
queue: QueuedMessage[]; queue: QueuedMessage[];
typing: Typing;
drafts: Drafts; drafts: Drafts;
sync: SyncOptions; sync: SyncOptions;
experiments: ExperimentOptions; experiments: ExperimentOptions;
......
import type { Auth } from "revolt.js/dist/api/objects"; import { Session } from "revolt-api/types/Auth";
export interface AuthState { export interface AuthState {
accounts: { accounts: {
[key: string]: { [key: string]: {
session: Auth.Session; session: Session;
}; };
}; };
active?: string; active?: string;
...@@ -13,7 +13,7 @@ export type AuthAction = ...@@ -13,7 +13,7 @@ export type AuthAction =
| { type: undefined } | { type: undefined }
| { | {
type: "LOGIN"; type: "LOGIN";
session: Auth.Session; session: Session;
} }
| { | {
type: "LOGOUT"; type: "LOGOUT";
......
export type Experiments = "search"; export type Experiments = "search";
export const AVAILABLE_EXPERIMENTS: Experiments[] = ['search']; export const AVAILABLE_EXPERIMENTS: Experiments[] = ["search"];
export const EXPERIMENTS: { export const EXPERIMENTS: {
[key in Experiments]: { title: string; description: string }; [key in Experiments]: { title: string; description: string };
} = { } = {
......
...@@ -12,7 +12,6 @@ import { sectionToggle, SectionToggleAction } from "./section_toggle"; ...@@ -12,7 +12,6 @@ import { sectionToggle, SectionToggleAction } from "./section_toggle";
import { config, ConfigAction } from "./server_config"; import { config, ConfigAction } from "./server_config";
import { settings, SettingsAction } from "./settings"; import { settings, SettingsAction } from "./settings";
import { sync, SyncAction } from "./sync"; import { sync, SyncAction } from "./sync";
import { typing, TypingAction } from "./typing";
import { unreads, UnreadsAction } from "./unreads"; import { unreads, UnreadsAction } from "./unreads";
export default combineReducers({ export default combineReducers({
...@@ -22,7 +21,6 @@ export default combineReducers({ ...@@ -22,7 +21,6 @@ export default combineReducers({
settings, settings,
unreads, unreads,
queue, queue,
typing,
drafts, drafts,
sync, sync,
experiments, experiments,
...@@ -38,7 +36,6 @@ export type Action = ...@@ -38,7 +36,6 @@ export type Action =
| SettingsAction | SettingsAction
| UnreadsAction | UnreadsAction
| QueueAction | QueueAction
| TypingAction
| DraftAction | DraftAction
| SyncAction | SyncAction
| ExperimentsAction | ExperimentsAction
......
import type { Channel, Message } from "revolt.js"; import { Channel } from "revolt.js/dist/maps/Channels";
import { Message } from "revolt.js/dist/maps/Messages";
import type { SyncUpdateAction } from "./sync"; import type { SyncUpdateAction } from "./sync";
...@@ -35,7 +36,7 @@ export function shouldNotify( ...@@ -35,7 +36,7 @@ export function shouldNotify(
case "none": case "none":
return false; return false;
case "mention": { case "mention": {
if (!message.mentions?.includes(user_id)) return false; if (!message.mention_ids?.includes(user_id)) return false;
} }
} }
......
import type { MessageObject } from "../../context/revoltjs/util";
export enum QueueStatus { export enum QueueStatus {
SENDING = "sending", SENDING = "sending",
ERRORED = "errored", ERRORED = "errored",
...@@ -10,7 +8,11 @@ export interface Reply { ...@@ -10,7 +8,11 @@ export interface Reply {
mention: boolean; mention: boolean;
} }
export type QueuedMessageData = Omit<MessageObject, "content" | "replies"> & { export type QueuedMessageData = {
_id: string;
author: string;
channel: string;
content: string; content: string;
replies: Reply[]; replies: Reply[];
}; };
......
import type { Core } from "revolt.js/dist/api/objects"; import type { RevoltConfiguration } from "revolt-api/types/Core";
export type ConfigAction = export type ConfigAction =
| { type: undefined } | { type: undefined }
| { | {
type: "SET_CONFIG"; type: "SET_CONFIG";
config: Core.RevoltNodeConfiguration; config: RevoltConfiguration;
}; };
export function config( export function config(
state = {} as Core.RevoltNodeConfiguration, state = {} as RevoltConfiguration,
action: ConfigAction, action: ConfigAction,
): Core.RevoltNodeConfiguration { ): RevoltConfiguration {
switch (action.type) { switch (action.type) {
case "SET_CONFIG": case "SET_CONFIG":
return action.config; return action.config;
......
export type TypingUser = { id: string; started: number };
export type Typing = { [key: string]: TypingUser[] };
export type TypingAction =
| { type: undefined }
| {
type: "TYPING_START";
channel: string;
user: string;
}
| {
type: "TYPING_STOP";
channel: string;
user: string;
}
| {
type: "RESET";
};
export function typing(state: Typing = {}, action: TypingAction): Typing {
switch (action.type) {
case "TYPING_START":
return {
...state,
[action.channel]: [
...(state[action.channel] ?? []).filter(
(x) => x.id !== action.user,
),
{
id: action.user,
started: +new Date(),
},
],
};
case "TYPING_STOP":
return {
...state,
[action.channel]:
state[action.channel]?.filter(
(x) => x.id !== action.user,
) ?? [],
};
case "RESET":
return {};
default:
return state;
}
}
import type { Sync } from "revolt.js/dist/api/objects"; import type { ChannelUnread } from "revolt-api/types/Sync";
export interface Unreads { export interface Unreads {
[key: string]: Partial<Omit<Sync.ChannelUnread, "_id">>; [key: string]: Partial<Omit<ChannelUnread, "_id">>;
} }
export type UnreadsAction = export type UnreadsAction =
...@@ -13,7 +13,7 @@ export type UnreadsAction = ...@@ -13,7 +13,7 @@ export type UnreadsAction =
} }
| { | {
type: "UNREADS_SET"; type: "UNREADS_SET";
unreads: Sync.ChannelUnread[]; unreads: ChannelUnread[];
} }
| { | {
type: "UNREADS_MENTION"; type: "UNREADS_MENTION";
......
export const REPO_URL = "https://gitlab.insrt.uk/revolt/revite/-/commit"; /* eslint-disable */
export const GIT_REVISION = "__GIT_REVISION__"; // Strings needs to be explictly stated here as they can cause type issues elsewhere.
export const GIT_BRANCH = "__GIT_BRANCH__";
export const REPO_URL: string =
"https://gitlab.insrt.uk/revolt/revite/-/commit";
export const GIT_REVISION: string = "__GIT_REVISION__";
export const GIT_BRANCH: string = "__GIT_BRANCH__";
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
background: var(--scrollbar-thumb); background: var(--scrollbar-thumb);
} }
::-webkit-scrollbar-corner {
background: transparent;
}
::selection { ::selection {
background: var(--accent); background: var(--accent);
color: var(--foreground); color: var(--foreground);
...@@ -44,3 +48,7 @@ hr { ...@@ -44,3 +48,7 @@ hr {
height: 1px; height: 1px;
flex-grow: 1; flex-grow: 1;
} }
foreignObject > svg {
vertical-align: top !important;
}
...@@ -7,13 +7,20 @@ ...@@ -7,13 +7,20 @@
--font: "Open Sans"; --font: "Open Sans";
--codeblock-font: "Fira Code"; --codeblock-font: "Fira Code";
--sidebar-active: var(--secondary-background); --sidebar-active: var(--secondary-background);
/**
* Native
*/
--titlebar-height: 29px;
--titlebar-action-padding: 8px;
--titlebar-logo-color: var(--secondary-foreground);
/** /**
* Layout * Layout
*/ */
--app-height: 100vh; --app-height: 100vh;
--border-radius: 6px; --border-radius: 6px;
--input-border-width: 2px; --input-border-width: 2px;
--textarea-padding: 16px; --textarea-padding: 16px;
--textarea-line-height: 20px; --textarea-line-height: 20px;
...@@ -29,6 +36,13 @@ ...@@ -29,6 +36,13 @@
/** /**
* Experimental * Experimental
*/ */
--background-rgb: (25,25,25); //THIS IS SO THAT WE CAN HAVE CUSTOM BACKGROUNDS FOR THE CLIENT, CONVERTS THE HEX TO AN RGB VALUE FROM --background --background-rgb: (
--background-rgba: rgba(var(--background-rgb), .8); //make the opacity also customizable 25,
25,
25
); //THIS IS SO THAT WE CAN HAVE CUSTOM BACKGROUNDS FOR THE CLIENT, CONVERTS THE HEX TO AN RGB VALUE FROM --background
--background-rgba: rgba(
var(--background-rgb),
0.8
); //make the opacity also customizable
} }
/// <reference lib="webworker" /> /// <reference lib="webworker" />
import { IDBPDatabase, openDB } from "idb";
import { getItem } from "localforage";
import { Channel, Message, User } from "revolt.js";
import { Server } from "revolt.js/dist/api/objects";
import { precacheAndRoute } from "workbox-precaching"; import { precacheAndRoute } from "workbox-precaching";
import type { State } from "./redux";
import {
getNotificationState,
shouldNotify,
} from "./redux/reducers/notifications";
declare let self: ServiceWorkerGlobalScope; declare let self: ServiceWorkerGlobalScope;
self.addEventListener("message", (event) => { self.addEventListener("message", (event) => {
...@@ -19,130 +9,10 @@ self.addEventListener("message", (event) => { ...@@ -19,130 +9,10 @@ self.addEventListener("message", (event) => {
precacheAndRoute(self.__WB_MANIFEST); precacheAndRoute(self.__WB_MANIFEST);
// ulid decodeTime(id: string)
// since crypto is not available in sw.js
const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
const ENCODING_LEN = ENCODING.length;
const TIME_LEN = 10;
function decodeTime(id: string) {
const time = id
.substr(0, TIME_LEN)
.split("")
.reverse()
.reduce((carry, char, index) => {
const encodingIndex = ENCODING.indexOf(char);
if (encodingIndex === -1) throw `invalid character found: ${char}`;
return (carry += encodingIndex * Math.pow(ENCODING_LEN, index));
}, 0);
return time;
}
self.addEventListener("push", (event) => { self.addEventListener("push", (event) => {
async function process() { async function process() {
if (event.data === null) return; if (event.data === null) return;
const data: Message = event.data.json(); // Need to write notification generator on server.
const item = await localStorage.getItem("state");
if (!item) return;
const state: State = JSON.parse(item);
const autumn_url = state.config.features.autumn.url;
const user_id = state.auth.active!;
let db: IDBPDatabase;
try {
// Match RevoltClient.tsx#L55
db = await openDB("state", 3, {
upgrade(db) {
for (const store of [
"channels",
"servers",
"users",
"members",
]) {
db.createObjectStore(store, {
keyPath: "_id",
});
}
},
});
} catch (err) {
console.error(
"Failed to open IndexedDB store, continuing without.",
);
return;
}
async function get<T>(
store: string,
key: string,
): Promise<T | undefined> {
try {
return await db.get(store, key);
} catch (err) {
return undefined;
}
}
const channel = await get<Channel>("channels", data.channel);
const user = await get<User>("users", data.author);
if (channel) {
const notifs = getNotificationState(state.notifications, channel);
if (!shouldNotify(notifs, data, user_id)) return;
}
let title = `@${data.author}`;
const username = user?.username ?? data.author;
let image;
if (data.attachments) {
const attachment = data.attachments[0];
if (attachment.metadata.type === "Image") {
image = `${autumn_url}/${attachment.tag}/${attachment._id}`;
}
}
switch (channel?.channel_type) {
case "SavedMessages":
break;
case "DirectMessage":
title = `@${username}`;
break;
case "Group":
if (user?._id === "00000000000000000000000000") {
title = channel.name;
} else {
title = `@${user?.username} - ${channel.name}`;
}
break;
case "TextChannel":
{
const server = await get<Server>("servers", channel.server);
title = `@${user?.username} (#${channel.name}, ${server?.name})`;
}
break;
}
await self.registration.showNotification(title, {
icon: user?.avatar
? `${autumn_url}/${user.avatar.tag}/${user.avatar._id}`
: `https://api.revolt.chat/users/${data.author}/default_avatar`,
image,
body:
typeof data.content === "string"
? data.content
: JSON.stringify(data.content),
timestamp: decodeTime(data._id),
tag: data.channel,
badge: "https://app.revolt.chat/assets/icons/mono-48x48.png",
data:
channel?.channel_type === "TextChannel"
? `/server/${channel.server}/channel/${channel._id}`
: `/channel/${data.channel}`,
});
} }
event.waitUntil(process()); event.waitUntil(process());
......
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"], "lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Node", "moduleResolution": "Node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,
"jsx": "preserve", "jsx": "preserve",
"jsxFactory": "h", "jsxFactory": "h",
"jsxFragmentFactory": "Fragment", "jsxFragmentFactory": "Fragment",
"types": [ "types": ["vite-plugin-pwa/client"],
"vite-plugin-pwa/client" "experimentalDecorators": true
] },
}, "include": ["src", "ui/ui.tsx"]
"include": ["src", "ui/ui.tsx"]
} }
import { useState } from 'preact/hooks'; import styled from "styled-components";
import styled from 'styled-components';
import '../src/styles/index.scss'
import { render } from 'preact'
import Theme from '../src/context/Theme'; import "../src/styles/index.scss";
import { render } from "preact";
import { useState } from "preact/hooks";
import Theme from "../src/context/Theme";
import Banner from "../src/components/ui/Banner";
import Button from "../src/components/ui/Button";
import Checkbox from "../src/components/ui/Checkbox";
import ColourSwatches from "../src/components/ui/ColourSwatches";
import ComboBox from "../src/components/ui/ComboBox";
import InputBox from "../src/components/ui/InputBox";
import Overline from "../src/components/ui/Overline";
import Radio from "../src/components/ui/Radio";
import Tip from "../src/components/ui/Tip";
export const UIDemo = styled.div` export const UIDemo = styled.div`
gap: 12px; gap: 12px;
padding: 12px; padding: 12px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
`; `;
import Button from '../src/components/ui/Button';
import Banner from '../src/components/ui/Banner';
import Checkbox from '../src/components/ui/Checkbox';
import ComboBox from '../src/components/ui/ComboBox';
import InputBox from '../src/components/ui/InputBox';
import ColourSwatches from '../src/components/ui/ColourSwatches';
import Tip from '../src/components/ui/Tip';
import Radio from '../src/components/ui/Radio';
import Overline from '../src/components/ui/Overline';
export function UI() { export function UI() {
let [checked, setChecked] = useState(false); let [checked, setChecked] = useState(false);
let [colour, setColour] = useState('#FD6671'); let [colour, setColour] = useState("#FD6671");
let [selected, setSelected] = useState<'a' | 'b' | 'c'>('a'); let [selected, setSelected] = useState<"a" | "b" | "c">("a");
return ( return (
<> <>
<Button>Button (normal)</Button> <Button>Button (normal)</Button>
<Button contrast>Button (contrast)</Button> <Button contrast>Button (contrast)</Button>
<Button error>Button (error)</Button> <Button error>Button (error)</Button>
<Button contrast error>Button (contrast + error)</Button> <Button contrast error>
Button (contrast + error)
</Button>
<Banner>I am a banner!</Banner> <Banner>I am a banner!</Banner>
<Checkbox checked={checked} onChange={setChecked} description="ok gamer">Do you want thing??</Checkbox> <Checkbox
checked={checked}
onChange={setChecked}
description="ok gamer">
Do you want thing??
</Checkbox>
<ComboBox> <ComboBox>
<option>Select an option.</option> <option>Select an option.</option>
<option>1</option> <option>1</option>
...@@ -46,24 +54,35 @@ export function UI() { ...@@ -46,24 +54,35 @@ export function UI() {
<InputBox placeholder="Contrast input box..." contrast /> <InputBox placeholder="Contrast input box..." contrast />
<InputBox value="Input box with value" /> <InputBox value="Input box with value" />
<InputBox value="Contrast with value" contrast /> <InputBox value="Contrast with value" contrast />
<ColourSwatches value={colour} onChange={v => setColour(v)} /> <ColourSwatches value={colour} onChange={(v) => setColour(v)} />
<Tip>I am a tip! I provide valuable information.</Tip> <Tip hideSeparator>I am a tip! I provide valuable information.</Tip>
<Radio checked={selected === 'a'} onSelect={() => setSelected('a')}>First option</Radio> <Radio checked={selected === "a"} onSelect={() => setSelected("a")}>
<Radio checked={selected === 'b'} onSelect={() => setSelected('b')}>Second option</Radio> First option
<Radio checked={selected === 'c'} onSelect={() => setSelected('c')}>Last option</Radio> </Radio>
<Radio checked={selected === "b"} onSelect={() => setSelected("b")}>
Second option
</Radio>
<Radio checked={selected === "c"} onSelect={() => setSelected("c")}>
Last option
</Radio>
<Overline>Normal overline</Overline> <Overline>Normal overline</Overline>
<Overline type="subtle">Subtle overline</Overline> <Overline type="subtle">Subtle overline</Overline>
<Overline type="error">Error overline</Overline> <Overline type="error">Error overline</Overline>
<Overline error="with error">Normal overline</Overline> <Overline error="with error">Normal overline</Overline>
<Overline type="subtle" error="with error">Subtle overline</Overline> <Overline type="subtle" error="with error">
Subtle overline
</Overline>
</> </>
) );
} }
render(<> render(
<Theme> <>
<UIDemo> <Theme>
<UI /> <UIDemo>
</UIDemo> <UI />
</Theme> </UIDemo>
</>, document.getElementById('app')!) </Theme>
</>,
document.getElementById("app")!,
);
import { resolve } from 'path' import replace from "@rollup/plugin-replace";
import { readFileSync } from 'fs' import { readFileSync } from "fs";
import { defineConfig } from 'vite' import { resolve } from "path";
import preact from '@preact/preset-vite' import { defineConfig } from "vite";
import { VitePWA } from 'vite-plugin-pwa' import { VitePWA } from "vite-plugin-pwa";
import replace from '@rollup/plugin-replace'
import preact from "@preact/preset-vite";
function getGitRevision() { function getGitRevision() {
try { try {
const rev = readFileSync('.git/HEAD').toString().trim(); const rev = readFileSync(".git/HEAD").toString().trim();
if (rev.indexOf(':') === -1) { if (rev.indexOf(":") === -1) {
return rev; return rev;
} else { } else {
return readFileSync('.git/' + rev.substring(5)).toString().trim(); return readFileSync(".git/" + rev.substring(5))
.toString()
.trim();
}
} catch (err) {
console.error("Failed to get Git revision.");
return "?";
} }
} catch (err) {
console.error('Failed to get Git revision.');
return '?';
}
} }
function getGitBranch() { function getGitBranch() {
try { try {
const rev = readFileSync('.git/HEAD').toString().trim(); const rev = readFileSync(".git/HEAD").toString().trim();
if (rev.indexOf(':') === -1) { if (rev.indexOf(":") === -1) {
return 'DETACHED'; return "DETACHED";
} else { } else {
return rev.split('/').pop(); return rev.split("/").pop();
}
} catch (err) {
console.error("Failed to get Git branch.");
return "?";
} }
} catch (err) {
console.error('Failed to get Git branch.');
return '?';
}
} }
function getVersion() { function getVersion() {
return readFileSync('VERSION').toString(); return readFileSync("VERSION").toString();
} }
const branch = getGitBranch(); const branch = getGitBranch();
const isNightly = false;//branch !== 'production'; const isNightly = false; //branch !== 'production';
const iconPrefix = isNightly ? 'nightly-' : ''; const iconPrefix = isNightly ? "nightly-" : "";
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
preact(), preact(),
VitePWA({ VitePWA({
srcDir: 'src', srcDir: "src",
filename: 'sw.ts', filename: "sw.ts",
strategies: 'injectManifest', strategies: "injectManifest",
manifest: { manifest: {
name: isNightly ? "Revolt Nightly" : "Revolt", name: isNightly ? "Revolt Nightly" : "Revolt",
short_name: "Revolt", short_name: "Revolt",
description: isNightly ? "Early preview builds of Revolt." : "User-first, privacy-focused chat platform.", description: isNightly
categories: ["messaging"], ? "Early preview builds of Revolt."
start_url: "/", : "User-first, privacy-focused chat platform.",
orientation: "portrait", categories: ["messaging"],
display: "standalone", start_url: "/",
background_color: "#101823", orientation: "portrait",
theme_color: "#101823", display: "standalone",
icons: [ background_color: "#101823",
{ theme_color: "#101823",
"src": `/assets/icons/${iconPrefix}android-chrome-192x192.png`, icons: [
"type": "image/png", {
"sizes": "192x192" src: `/assets/icons/${iconPrefix}android-chrome-192x192.png`,
}, type: "image/png",
{ sizes: "192x192",
"src": `/assets/icons/${iconPrefix}android-chrome-512x512.png`, },
"type": "image/png", {
"sizes": "512x512" src: `/assets/icons/${iconPrefix}android-chrome-512x512.png`,
type: "image/png",
sizes: "512x512",
},
{
src: `/assets/icons/monochrome.svg`,
type: "image/svg+xml",
sizes: "48x48 72x72 96x96 128x128 256x256",
purpose: "monochrome",
},
{
src: `/assets/icons/masking-512x512.png`,
type: "image/png",
sizes: "512x512",
purpose: "maskable",
},
],
}, },
{ }),
"src": `/assets/icons/monochrome.svg`, replace({
"type": "image/svg+xml", __GIT_REVISION__: getGitRevision(),
"sizes": "48x48 72x72 96x96 128x128 256x256", __GIT_BRANCH__: getGitBranch(),
"purpose": "monochrome" __APP_VERSION__: getVersion(),
preventAssignment: true,
}),
],
build: {
sourcemap: true,
rollupOptions: {
input: {
main: resolve(__dirname, "index.html"),
ui: resolve(__dirname, "ui/index.html"),
}, },
{ },
"src": `/assets/icons/masking-512x512.png`, },
"type": "image/png", });
"sizes": "512x512",
"purpose": "maskable"
}
]
}
}),
replace({
__GIT_REVISION__: getGitRevision(),
__GIT_BRANCH__: getGitBranch(),
__APP_VERSION__: getVersion(),
preventAssignment: true
})
],
build: {
sourcemap: true,
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
ui: resolve(__dirname, 'ui/index.html')
}
}
}
})
...@@ -879,6 +879,13 @@ ...@@ -879,6 +879,13 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/runtime@^7.14.8":
version "7.14.8"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446"
integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.12.13", "@babel/template@^7.14.5": "@babel/template@^7.12.13", "@babel/template@^7.14.5":
version "7.14.5" version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
...@@ -1114,14 +1121,6 @@ ...@@ -1114,14 +1121,6 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
"@insertish/mutable@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@insertish/mutable/-/mutable-1.1.0.tgz#06f95f855691ccb69ee3c339887a80bcd1498116"
integrity sha512-NH7aCGFAKRE1gFprrW/HsJoWCWQy18TZBarxLdeLVWdLFvkb2lD6Z5B70oOoUHFNpykiTC8IcRonsd9Xn13n8Q==
dependencies:
eventemitter3 "^4.0.7"
lodash.isequal "^4.5.0"
"@mdn/browser-compat-data@^2.0.7": "@mdn/browser-compat-data@^2.0.7":
version "2.0.7" version "2.0.7"
resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-2.0.7.tgz#72ec37b9c1e00ce0b4e0309d753be18e2da12ee3" resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-2.0.7.tgz#72ec37b9c1e00ce0b4e0309d753be18e2da12ee3"
...@@ -1252,12 +1251,12 @@ ...@@ -1252,12 +1251,12 @@
"@babel/runtime" "^7.14.0" "@babel/runtime" "^7.14.0"
"@styled-icons/styled-icon" "^10.6.3" "@styled-icons/styled-icon" "^10.6.3"
"@styled-icons/boxicons-solid@^10.34.0": "@styled-icons/boxicons-solid@^10.37.0":
version "10.34.0" version "10.37.0"
resolved "https://registry.yarnpkg.com/@styled-icons/boxicons-solid/-/boxicons-solid-10.34.0.tgz#4f31b873a1d52c85230f86eb7106c04f416a12c9" resolved "https://registry.yarnpkg.com/@styled-icons/boxicons-solid/-/boxicons-solid-10.37.0.tgz#49e3ec72b560967f1ba12c433f28270d8a74e9b9"
integrity sha512-0DTsuysRgIO/XoSq5sFPeknnidLzhTEmaG5uJuLmCPBw78VjxNt6DQFRcn8ytn+ba5Qhu+Ps3Km8nbY7Zf14/g== integrity sha512-u6/urwIWesGArSHW98TFHnzMduInQYkhWE1LdNCWkmzs0CvQ0Xmmvnl1Lz9LnAHatQR+UFpKLz2y08Fhu7zBgQ==
dependencies: dependencies:
"@babel/runtime" "^7.14.0" "@babel/runtime" "^7.14.8"
"@styled-icons/styled-icon" "^10.6.3" "@styled-icons/styled-icon" "^10.6.3"
"@styled-icons/simple-icons@^10.33.0": "@styled-icons/simple-icons@^10.33.0":
...@@ -2614,11 +2613,6 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react- ...@@ -2614,11 +2613,6 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-
dependencies: dependencies:
react-is "^16.7.0" react-is "^16.7.0"
idb@^6.1.2:
version "6.1.2"
resolved "https://registry.yarnpkg.com/idb/-/idb-6.1.2.tgz#82ef5c951b8e1f47875d36ccafa4bedafc62f2f1"
integrity sha512-1DNDVu3yDhAZkFDlJf0t7r+GLZ248F5pTAtA7V0oVG3yjmV125qZOx3g0XpAEkGZVYQiFDAsSOnGet2bhugc3w==
ignore@^4.0.6: ignore@^4.0.6:
version "4.0.6" version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
...@@ -3077,6 +3071,16 @@ minimist@^1.2.5: ...@@ -3077,6 +3071,16 @@ minimist@^1.2.5:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mobx-react-lite@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.2.0.tgz#331d7365a6b053378dfe9c087315b4e41c5df69f"
integrity sha512-q5+UHIqYCOpBoFm/PElDuOhbcatvTllgRp3M1s+Hp5j0Z6XNgDbgqxawJ0ZAUEyKM8X1zs70PCuhAIzX1f4Q/g==
mobx@^6.3.2:
version "6.3.2"
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.2.tgz#125590961f702a572c139ab69392bea416d2e51b"
integrity sha512-xGPM9dIE1qkK9Nrhevp0gzpsmELKU4MFUJRORW/jqxVFIHHWIoQrjDjL8vkwoJYY3C2CeVJqgvl38hgKTalTWg==
ms@2.0.0: ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
...@@ -3288,7 +3292,7 @@ preact-markup@^2.0.0: ...@@ -3288,7 +3292,7 @@ preact-markup@^2.0.0:
resolved "https://registry.yarnpkg.com/preact-markup/-/preact-markup-2.1.1.tgz#0451e7eed1dac732d7194c34a7f16ff45a2cfdd7" resolved "https://registry.yarnpkg.com/preact-markup/-/preact-markup-2.1.1.tgz#0451e7eed1dac732d7194c34a7f16ff45a2cfdd7"
integrity sha512-8JL2p36mzK8XkspOyhBxUSPjYwMxDM0L5BWBZWxsZMVW8WsGQrYQDgVuDKkRspt2hwrle+Cxr/053hpc9BJwfw== integrity sha512-8JL2p36mzK8XkspOyhBxUSPjYwMxDM0L5BWBZWxsZMVW8WsGQrYQDgVuDKkRspt2hwrle+Cxr/053hpc9BJwfw==
preact@^10.0.0, preact@^10.4.6, preact@^10.5.13: preact@^10.0.0, preact@^10.4.6, preact@^10.5.14:
version "10.5.14" version "10.5.14"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.5.14.tgz#0b14a2eefba3c10a57116b90d1a65f5f00cd2701" resolved "https://registry.yarnpkg.com/preact/-/preact-10.5.14.tgz#0b14a2eefba3c10a57116b90d1a65f5f00cd2701"
integrity sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ== integrity sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ==
...@@ -3563,17 +3567,23 @@ reusify@^1.0.4: ...@@ -3563,17 +3567,23 @@ 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.17: revolt-api@0.5.1-alpha.10-patch.0:
version "4.3.3-alpha.17" version "0.5.1-alpha.10-patch.0"
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.17.tgz#0745d251c695840b87e98098bcc4d67c7cc15de5" resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.1-alpha.10-patch.0.tgz#97d31bec7dfa4573567097443acb059c4feaac20"
integrity sha512-MjxVnkkeX5md5NxZNRS9fl06jsjcDciAxKnbZ2rkBYJofQ94tvr1CYBWvFhS/u/tAR80HAPIEjJVC9HKJDK9Fg== integrity sha512-UyM890HkGlYNQOxpHuEpUsJHLt8Ujnjg9/zPEDGpbvS4iy0jmHX23Hh8tOCfb/ewxbNrtT3G1HpSWKOneW/vYg==
revolt.js@5.0.0-alpha.18:
version "5.0.0-alpha.18"
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.0-alpha.18.tgz#fffd63a4f4f93a4a6422de6a68c1ba3f3f9b55e5"
integrity sha512-NVd00P4CYLVJf1AuYwo65mPeLaST/RdU7dMLFwCZPvQAHvyPwTfLekCc9dfKPT4BkS2sjF8Vxi2xUFpMRMZYfw==
dependencies: dependencies:
"@insertish/mutable" "1.1.0"
axios "^0.19.2" axios "^0.19.2"
eventemitter3 "^4.0.7" eventemitter3 "^4.0.7"
exponential-backoff "^3.1.0" exponential-backoff "^3.1.0"
isomorphic-ws "^4.0.1" isomorphic-ws "^4.0.1"
lodash.defaultsdeep "^4.6.1" lodash.defaultsdeep "^4.6.1"
lodash.isequal "^4.5.0"
mobx "^6.3.2"
tsc-watch "^4.1.0" tsc-watch "^4.1.0"
ulid "^2.3.0" ulid "^2.3.0"
ws "^7.2.1" ws "^7.2.1"
......