Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f8b0431
fix: Shrink home-screen flex layout to single-column for mobile
Pecacheu Feb 14, 2026
93ecdca
fix: More mobile UI fixes
Pecacheu Feb 14, 2026
a7de4d3
fix: Mobile UI marches onwards
Pecacheu Feb 15, 2026
e8e74ca
fix: Add back button to Channel & Server settings
Pecacheu Feb 15, 2026
e3fd1a8
fix: Simplified CSS
Pecacheu Feb 15, 2026
1b07b54
fix: Move the CSS again & misc
Pecacheu May 8, 2026
7d9fb6d
feat: Mobile UI
Pecacheu Feb 17, 2026
ec94388
feat: Slide that SlideDrawer real smooth-like
Pecacheu Feb 18, 2026
40af88e
fix: Enable mobile autocorrect and autocapitalize on text input
Pecacheu Feb 18, 2026
f52bfc8
fix: Un-round them corners for portrait phone view
Pecacheu Feb 19, 2026
512f23b
fix: Refine SlideDrawer inertia detection by always recalculating fin…
Pecacheu Mar 14, 2026
d14e046
fix: Sensible margains for modals on mobile to prevent weird scroll b…
Pecacheu Feb 21, 2026
437fbb9
fix: Small chance that two modals have the same ID using Math.random …
Pecacheu Feb 22, 2026
87c600b
fix: Clicking channels/DMs/settings buttons opens slide drawer on mobile
Pecacheu Feb 22, 2026
cc95f3e
fix: Cease this madness
Pecacheu Feb 22, 2026
0571b70
fix: Disable context menu when editing message, so you can accept OS …
Pecacheu Feb 23, 2026
476763a
feat: Adding a react button to the context menu was harder than antic…
Pecacheu Feb 28, 2026
3577780
fix: Prettier format
Pecacheu Mar 3, 2026
8cdc2b0
fix: Reset floating touch timer to prevent overlapping timer bug
Pecacheu May 9, 2026
11ae294
fix: Misc emoji and message box fixes
Pecacheu May 8, 2026
9d19973
fix: Misc console warnings
Pecacheu Mar 14, 2026
2123301
fix: Make reactPicker optional to make method signatures backward com…
Pecacheu Mar 5, 2026
e92b5d4
fix: Use text cursor on mdui-text-field
Pecacheu Mar 8, 2026
498b9ae
fix: Make class naming scheme compatible with #791
Pecacheu Mar 14, 2026
05b28bc
fix: Appearance menu and role editor color pane clipping past edge on…
Pecacheu Mar 19, 2026
d1fdcef
fix: Adjust channel bar bottom margin to match top margin
Pecacheu Mar 29, 2026
cff3caa
feat: Add reactive signals to SlideDrawer, will come in handy in othe…
Pecacheu Mar 30, 2026
bdfa8e0
fix: Use undefined rather than null for consistency
Pecacheu Apr 4, 2026
f9bf236
fix: Fixes for VoiceCallCardPiP
Pecacheu May 8, 2026
c067773
refactor: Use new resize & layout hooks
Pecacheu May 24, 2026
a9959c0
fix: Apply mobile login layout using hook
Pecacheu May 26, 2026
1221976
fix: Build error from missing import
Pecacheu May 30, 2026
3004de2
fix: Remember SlideDrawer state when rotating to landscape then portrait
Pecacheu Jun 12, 2026
7b98644
fix: Truncate channel name to ensure header buttons don't overflow sc…
Pecacheu Jun 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useState } from "@revolt/state";
import {
Attachment,
Avatar,
CompositionMediaPicker,
Embed,
MessageContainer,
MessageReply,
Expand All @@ -30,6 +31,8 @@ import {
floatingUserMenusFromMessage,
} from "../../../menus/UserContextMenu";

import { startsWithPackPUA } from "@revolt/markdown/emoji/UnicodeEmoji";
import { MediaPickerProps } from "@revolt/ui/components/features/messaging/composition/picker/CompositionMediaPicker";
import { EditMessage } from "./EditMessage";

/**
Expand Down Expand Up @@ -75,6 +78,8 @@ export function Message(props: Props) {
const client = useClient();

const [isHovering, setIsHovering] = createSignal(false);
const [reactPicker, setReactPicker] = createSignal<MediaPickerProps>();
let msgRef;

/**
* Determine whether this message only contains a GIF
Expand Down Expand Up @@ -104,6 +109,8 @@ export function Message(props: Props) {

return (
<MessageContainer
ref={msgRef}
reactPicker={reactPicker}
message={props.message}
onHover={setIsHovering}
username={
Expand Down Expand Up @@ -135,7 +142,16 @@ export function Message(props: Props) {
/>
</div>
}
contextMenu={() => <MessageContextMenu message={props.message} />}
contextMenu={
props.editing
? undefined
: () => (
<MessageContextMenu
message={props.message}
reactPicker={reactPicker}
/>
)
}
timestamp={props.message.createdAt}
edited={props.message.editedAt}
mentioned={props.message.mentioned}
Expand Down Expand Up @@ -266,6 +282,29 @@ export function Message(props: Props) {
</Match>
}
>
<CompositionMediaPicker
onMessage={(content) =>
props.message?.channel?.sendMessage({
content,
replies: [{ id: props.message.id, mention: true }],
})
}
onTextReplacement={(emoji) =>
react(
emoji.startsWith(":")
? emoji.slice(1, emoji.length - 1)
: startsWithPackPUA(emoji)
? emoji.slice(1)
: emoji,
)
}
>
{(trigProps) => {
trigProps.ref(msgRef);
setReactPicker(trigProps);
return <></>;
}}
</CompositionMediaPicker>
<Show when={props.message.systemMessage}>
<SystemMessage
systemMessage={props.message.systemMessage!}
Expand All @@ -291,30 +330,25 @@ export function Message(props: Props) {
</BreakText>
</Match>
</Switch>
<Show when={props.message.attachments}>
<For each={props.message.attachments}>
{(attachment) => (
<Attachment message={props.message} file={attachment} />
)}
</For>
</Show>
<Show when={props.message.embeds}>
<For each={props.message.embeds}>
{(embed) => <Embed embed={embed} />}
</For>
</Show>
<For each={props.message.attachments}>
{(attachment) => (
<Attachment
message={props.message}
file={attachment}
reactPicker={reactPicker}
/>
)}
</For>
<For each={props.message.embeds}>
{(embed) => <Embed embed={embed} />}
</For>
<Reactions
reactions={props.message.reactions as never as Map<string, Set<string>>}
interactions={props.message.interactions}
userId={client().user!.id}
addReaction={react}
removeReaction={unreact}
sendGIF={(content) =>
props.message?.channel?.sendMessage({
content,
replies: [{ id: props.message.id, mention: true }],
})
}
reactPicker={reactPicker}
/>
</MessageContainer>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ export function Messages(props: Props) {
// If we're not at the end, restore scroll position
if (existingState && !existingState.atEnd) {
setTimeout(() =>
listRef!.scrollTo({
listRef?.scrollTo({
top: existingState.scrollTop!,
behavior: "instant",
}),
Expand All @@ -290,7 +290,7 @@ export function Messages(props: Props) {
// Or... reset scroll to the end
else if (atEnd()) {
setTimeout(() =>
listRef!.scrollTo({
listRef?.scrollTo({
top: 9999999,
behavior: "instant",
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ChannelPermissionsEditor } from "./channel/permissions/ChannelPermissio
import { ChannelPermissionsOverview } from "./channel/permissions/ChannelPermissionsOverview";
import { ViewWebhook } from "./channel/webhooks/ViewWebhook";
import { WebhooksList } from "./channel/webhooks/WebhooksList";
import { BackCard } from "./user/_AccountCard";

const Config: SettingsConfiguration<Channel> = {
/**
Expand Down Expand Up @@ -98,11 +99,12 @@ const Config: SettingsConfiguration<Channel> = {
* Generate list of categories / entries for channel settings
* @returns List
*/
list(channel) {
list(channel, onClose) {
const { openModal } = useModals();

return {
context: channel,
prepend: <BackCard onClose={onClose} />,
entries: [
{
title: <TextWithEmoji content={channel.name} />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { EmojiList } from "./server/emojis/EmojiList";
import { ListServerInvites } from "./server/invites/ListServerInvites";
import { ServerRoleEditor } from "./server/roles/ServerRoleEditor";
import { ServerRoleOverview } from "./server/roles/ServerRoleOverview";
import { BackCard } from "./user/_AccountCard";

const Config: SettingsConfiguration<Server> = {
/**
Expand Down Expand Up @@ -89,12 +90,13 @@ const Config: SettingsConfiguration<Server> = {
* Generate list of categories / entries for server settings
* @returns List
*/
list(server) {
list(server, onClose) {
const user = useUser();
const { openModal } = useModals();

return {
context: server,
prepend: <BackCard onClose={onClose} />,
entries: [
{
title: <TextWithEmoji content={server.name} />,
Expand Down
16 changes: 12 additions & 4 deletions packages/client/components/app/interface/settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
createContext,
createMemo,
createSignal,
Setter,
untrack,
useContext,
} from "solid-js";
Expand All @@ -25,6 +26,8 @@ export interface SettingsProps {
* Settings context
*/
context: never;

contentRef: Setter<HTMLDivElement | undefined>;
}

/**
Expand Down Expand Up @@ -89,11 +92,16 @@ export function Settings(props: SettingsProps & SettingsConfiguration<never>) {
navigate,
}}
>
<MemoisedList context={props.context} list={props.list}>
<MemoisedList
context={props.context}
list={props.list}
onClose={props.onClose}
>
{(list) => (
<>
<SettingsSidebar list={list} page={page} setPage={setPage} />
<SettingsContent
ref={props.contentRef}
page={page}
list={list}
title={props.title}
Expand Down Expand Up @@ -156,14 +164,14 @@ export function Settings(props: SettingsProps & SettingsConfiguration<never>) {
*/
function MemoisedList(props: {
context: never;
list: (context: never) => SettingsList<unknown>;
onClose?: () => void;
list: (context: never, onClose?: () => void) => SettingsList<unknown>;
children: (list: Accessor<SettingsList<unknown>>) => JSX.Element;
}) {
/**
* Generate list of categories / links
*/
const list = createMemo(() => props.list(props.context));

const list = createMemo(() => props.list(props.context, props.onClose));
return <>{props.children(list)}</>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import MdWorkspacePremium from "@material-design-icons/svg/outlined/workspace_pr
import pkg from "../../../../../../package.json";

import { SettingsConfiguration } from ".";
import { AccountCard } from "./user/_AccountCard";
import { AccountCard, BackCard } from "./user/_AccountCard";
import { MyAccount } from "./user/Account";
import AdvancedSettings from "./user/Advanced";
import { AppearanceMenu } from "./user/appearance";
Expand Down Expand Up @@ -111,14 +111,15 @@ const Config: SettingsConfiguration<{ server: Server }> = {
* Generate list of categories / entries for client settings
* @returns List
*/
list() {
list(_, onClose) {
const { pop, openModal } = useModals();
const { logout } = useClientLifecycle();

return {
context: null!,
prepend: (
<Column gap="s">
<BackCard onClose={onClose} />
<AccountCard />
<div />
</Column>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Accessor, JSX, Show } from "solid-js";
import { Accessor, JSX, Setter, Show } from "solid-js";

import { css, cva } from "styled-system/css";
import { styled } from "styled-system/jsx";
Expand All @@ -19,34 +19,33 @@ export function SettingsContent(props: {
list: Accessor<SettingsList<unknown>>;
title: (ctx: SettingsList<never>, key: string) => string;
page: Accessor<string | undefined>;
ref: Setter<HTMLDivElement | undefined>;
}) {
const { navigate } = useSettingsNavigation();

return (
<div
use:scrollable={{
class: base(),
}}
>
<div ref={props.ref} class="settings" use:scrollable={{ class: base() }}>
<Show when={props.page()}>
<InnerContent>
<InnerContent class="settings_cont">
<InnerColumn>
<Text class="title" size="large">
<Breadcrumbs
elements={props.page()!.split("/")}
renderElement={(key) =>
props.title(props.list() as SettingsList<never>, key)
}
navigate={(keys) => navigate(keys.join("/"))}
/>
</Text>
<Show when={props.page() !== "account"}>
<Text class="title" size="large">
<Breadcrumbs
elements={props.page()!.split("/")}
renderElement={(key) =>
props.title(props.list() as SettingsList<never>, key)
}
navigate={(keys) => navigate(keys.join("/"))}
/>
</Text>
</Show>
{props.children}
<div class={css({ minHeight: "80px" })} />
</InnerColumn>
</InnerContent>
</Show>
<Show when={props.onClose}>
<CloseAction>
<CloseAction class="close">
<IconButton variant="tonal" onPress={props.onClose}>
<MdClose />
</IconButton>
Expand Down Expand Up @@ -121,7 +120,7 @@ const CloseAction = styled("div", {
marginTop: "4px",
display: "flex",
justifyContent: "center",
width: "36px",
width: "40px",
fontWeight: 600,
color: "var(--md-sys-color-on-surface)",
fontSize: "0.75rem",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,28 @@ import {
*/
export function SettingsSidebar(props: {
list: Accessor<SettingsList<unknown>>;

setPage: Setter<string | undefined>;
page: Accessor<string | undefined>;
}) {
const { navigate } = useSettingsNavigation();
const list = props.list();

/**
* Select first page on load
*/
onMount(() => {
if (!props.page()) {
props.setPage(props.list().entries[0].entries[0].id);
props.setPage(list.entries[0].entries[0].id);
}
});

return (
<Base>
<Base class="settings_sidebar">
<div use:invisibleScrollable>
<Content>
<Content class="content">
<Column gap="lg">
{props.list().prepend}
<For each={props.list().entries}>
{list.prepend}
<For each={list.entries}>
{(category) => (
<Show when={!category.hidden}>
<Column>
Expand Down Expand Up @@ -87,7 +87,7 @@ export function SettingsSidebar(props: {
</Show>
)}
</For>
{props.list().append}
{list.append}
</Column>
</Content>
</div>
Expand All @@ -104,6 +104,7 @@ const Base = styled("div", {
flex: "1 0 218px",
paddingLeft: "8px",
justifyContent: "flex-end",
height: "100%",
},
});

Expand Down
Loading
Loading