Skip to content

Commit

Permalink
modal revamp
Browse files Browse the repository at this point in the history
  • Loading branch information
jay3332 committed Jul 13, 2024
1 parent fd948b7 commit 5c78b70
Show file tree
Hide file tree
Showing 28 changed files with 473 additions and 392 deletions.
56 changes: 29 additions & 27 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ import {
ErrorBoundary,
For,
JSX,
lazy, Match, on, onCleanup,
onMount, ParentProps,
lazy,
Match,
on,
onCleanup,
onMount,
ParentProps,
Show,
Signal, Switch, useContext
Signal,
Switch,
useContext
} from "solid-js";
import {A, useLocation, useNavigate, useParams} from "@solidjs/router";
import {createMediaQuery} from "@solid-primitives/media";
Expand All @@ -18,14 +24,15 @@ import {
ChannelDisplayMetadata,
displayChannel,
displayName,
flatMapIterator, humanizePings,
humanizeTimeDeltaShort, mapIterator,
snowflakes, sumIterator
flatMapIterator,
humanizePings,
humanizeTimeDeltaShort,
mapIterator,
snowflakes,
sumIterator
} from "./utils";

import tooltip from "./directives/tooltip";
void tooltip

import GuildSideSelect, {GuildContextMenu} from "./components/guilds/GuildSideSelect";
import StatusIndicator, {StatusIndicatorProps} from "./components/users/StatusIndicator";

Expand All @@ -49,12 +56,13 @@ import ContextMenu, {ContextMenuButton, DangerContextMenuButton} from "./compone
import Code from "./components/icons/svg/Code";
import GuildMemberList from "./components/guilds/GuildMemberList";
import Plus from "./components/icons/svg/Plus";
import {NewGuildModalContext} from "./components/guilds/NewGuildModal";
import {ModalPage, NewGuildModalContextMenu} from "./components/guilds/NewGuildModal";
import {HeaderContext} from "./components/ui/Header";
import {relationshipFilterFactory} from "./pages/friends/Requests";
import PenToSquare from "./components/icons/svg/PenToSquare";
import Modal from "./components/ui/Modal";
import SetPresence from "./components/users/SetPresenceModal";
import {ModalId, useModal} from "./components/ui/Modal";

void tooltip

export enum Tab { Quick, Conversations, Servers, Discover }

Expand Down Expand Up @@ -212,7 +220,7 @@ function DirectMessageButton({ channelId }: { channelId: bigint }) {
)}
<div class="flex flex-col ml-0.5 text-sm min-w-0">
<span classList={{
"text-fg font-title font-medium transition-all duration-200": true,
"text-fg font-title font-medium transition-all duration-200 truncate min-w-0": true,
"text-opacity-100": active() || hasUnread(),
"text-opacity-60 group-hover:text-opacity-80": !active() && !hasUnread(),
}}>
Expand Down Expand Up @@ -326,7 +334,7 @@ function HomeSidebar(props: { tabSignal: Signal<Tab> }) {
const cache = getApi()!.cache!

const contextMenu = useContextMenu()!
const { setShow: setShowNewGuildModal, NewGuildModalContextMenu } = useContext(NewGuildModalContext)!
const {showModal} = useModal()

const recentlyViewed = createMemo(() => [...cache.lastAckedMessages.entries()]
.filter((entry): entry is [bigint, bigint] => !!entry[1])
Expand Down Expand Up @@ -520,7 +528,7 @@ function HomeSidebar(props: { tabSignal: Signal<Tab> }) {
icon={Plus}
class="w-4 h-4 fill-fg/50 group-hover:fill-fg/100 transition"
tooltip="New Server"
onClick={() => setShowNewGuildModal(true)}
onClick={() => showModal(ModalId.NewGuild, ModalPage.New)}
onContextMenu={contextMenu.getHandler(<NewGuildModalContextMenu />)}
/>
</button>
Expand Down Expand Up @@ -574,6 +582,7 @@ export function Sidebar({ signal }: { signal: Signal<Tab> }) {
const status = createMemo(() => presence()?.status ?? 'online')
const showGuildBar = createMemo(() => route.pathname.startsWith('/guilds'))
const contextMenu = useContextMenu()!
const {showModal} = useModal()

const [showStatusSettings, setShowStatusSettings] = createSignal(false)
let containerRef: HTMLDivElement | null = null
Expand All @@ -583,16 +592,11 @@ export function Sidebar({ signal }: { signal: Signal<Tab> }) {
onMount(() => document.addEventListener('click', listener))
onCleanup(() => document.removeEventListener('click', listener))

const [showUpdatePresenceModal, setShowUpdatePresenceModal] = createSignal(false)

return (
<div class="flex flex-col w-full h-full justify-between backdrop-blur transition" classList={{
"bg-bg-0/80": !showGuildBar(),
"bg-bg-0": showGuildBar(),
}}>
<Modal get={showUpdatePresenceModal} set={setShowUpdatePresenceModal}>
<SetPresence setShow={setShowUpdatePresenceModal} />
</Modal>
<div class="flex flex-grow h-[calc(100%-56px)]">
<div classList={{
"flex flex-col items-center flex-grow overflow-x-hidden overflow-y-auto transition-all duration-500 h-full bg-0": true,
Expand Down Expand Up @@ -634,7 +638,7 @@ export function Sidebar({ signal }: { signal: Signal<Tab> }) {
<h3 class="mx-2 mt-3 text-fg/60 text-xs font-bold uppercase">Custom Status</h3>
<button
class="mx-2 mt-1 mb-2 text-sm font-light group text-fg/60 hover:text-fg/100 transition"
onClick={() => setShowUpdatePresenceModal(true)}
onClick={() => showModal(ModalId.UpdatePresence)}
>
{presence()?.custom_status || 'Set a custom status'}
<Icon icon={PenToSquare} class="ml-1 w-4 h-4 fill-fg/60 group-hover:fill-fg/100 transition inline-block align-top" />
Expand Down Expand Up @@ -700,7 +704,7 @@ export const [previousPage, setPreviousPage] = createSignal<string>('/')

export default function App(props: ParentProps) {
const isMobile = createMediaQuery("(max-width: 768px)")
const isWide = createMediaQuery("(min-width: 1080px)")
const isWide = createMediaQuery("(min-width: 1000px)")
const location = useLocation()

onMount(() => {
Expand Down Expand Up @@ -734,12 +738,11 @@ export default function App(props: ParentProps) {
if (tab) setSidebarSignal(tab)
})

const { NewGuildModal } = useContext(NewGuildModalContext)!
const [headers] = useContext(HeaderContext)!

return (
<div
class="w-full mobile:w-[calc(100%+22rem)] h-full flex overflow-hidden"
class="w-full mobile:w-[calc(100%+21rem)] h-full flex overflow-hidden"
onTouchStart={(event) => setSwipeStart(event.touches[0].clientX)}
onTouchEnd={(event) => {
if (!isMobile()) return
Expand All @@ -756,21 +759,20 @@ export default function App(props: ParentProps) {
}
}}
>
<NewGuildModal />
<div
classList={{
"my-2 rounded-2xl transition-all duration-300 overflow-hidden": true,
"opacity-0 w-0 ml-0": !showSidebar(),
"opacity-100 w-80 ml-2": showSidebar(),
"opacity-100 w-[19rem] ml-2": showSidebar(),
}}
>
<div class="w-80 h-full">
<div class="w-[19rem] h-full">
<Sidebar signal={sidebarSignal} />
</div>
</div>
<div classList={{
"flex flex-col md:flex-grow mobile:w-[100vw] h-full opacity-100 transition-opacity": true,
"mobile:opacity-50 md:w-[calc(100%-22rem)]": showSidebar(),
"mobile:opacity-50 md:w-[calc(100%-21rem)]": showSidebar(),
}}>
<div class="flex flex-grow w-full gap-x-2 pt-2 px-2">
<div class="flex flex-grow items-center bg-bg-0/80 backdrop-blur h-14 rounded-xl">
Expand Down
79 changes: 41 additions & 38 deletions src/Entrypoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import App from "./App";
import {Settings, SettingsRoot} from "./pages/settings/SettingsLayout";
import {GuildSettings, GuildSettingsRoot} from "./pages/guilds/settings/GuildSettings";
import {GuildChannelSettings, GuildChannelSettingsRoot} from "./pages/channels/settings/GuildChannelSettings";
import {ModalProvider} from "./components/ui/Modal";

const Loading = lazy(() => import('./pages/Loading'))

Expand Down Expand Up @@ -160,45 +161,47 @@ const Entrypoint: Component = () => {
</Show>
<div class="w-full h-full overflow-hidden">
<Router>
<Route path="/bots/:botId" component={AddBot} />
<Show when={ws()} fallback={<Route path="*" component={Loading} />}>
<Route path="/settings" component={Settings}>
<Route path="/account" component={AccountSettings} />
<Route path="/appearance" component={AppearanceSettings} />
<Route path="/plugins" component={PluginsSettings} />
<Route path="/bots/:botId" component={BotSettings} />
<Route path="/bots" component={BotsSettings} />
</Route>
<Route path="/settings" component={SettingsRoot} />
<Route path="/guilds/:guildId/settings" component={GuildSettings}>
<Route path="/overview" component={GuildSettingsOverview} />
<Route path="/roles/:roleId" component={GuildSettingsRole}>
<Route path="/permissions" component={GuildSettingsRolePermissions} />
<Route path="/members" component={GuildSettingsRoleMembers} />
<Route path="/" component={GuildSettingsRoleOverview} />
<Route component={ModalProvider}>
<Route path="/bots/:botId" component={AddBot} />
<Show when={ws()} fallback={<Route path="*" component={Loading} />}>
<Route path="/settings" component={Settings}>
<Route path="/account" component={AccountSettings} />
<Route path="/appearance" component={AppearanceSettings} />
<Route path="/plugins" component={PluginsSettings} />
<Route path="/bots/:botId" component={BotSettings} />
<Route path="/bots" component={BotsSettings} />
</Route>
<Route path="/roles" component={GuildSettingsRoles} />
<Route path="/emojis" component={GuildSettingsEmojis} />
<Route path="/invites" component={() => 'wip'} />
</Route>
<Route path="/guilds/:guildId/settings" component={GuildSettingsRoot} />
<Route path="/guilds/:guildId/:channelId/settings" component={GuildChannelSettings}>
<Route path="/overview" component={GuildChannelSettingsOverview} />
<Route path="/permissions" component={GuildChannelSettingsPermissions} />
</Route>
<Route path="/guilds/:guildId/:channelId/settings" component={GuildChannelSettingsRoot} />
<Route component={App}>
<Route path="/loading" component={Loading} />
<Route path="/friends/requests" component={FriendRequests} />
<Route path="/friends/*" component={FriendsList} />
<Route path="/dms/:channelId" component={DmChannel} />
<Route path="/guilds/:guildId/:channelId" component={GuildChannel} />
<Route path="/guilds/:guildId" component={GuildHome} />
<Route path="/invite/:code" component={Invite} />
<Route path="/" component={Home} />
<Route path="*" component={NotFound} />
</Route>
</Show>
<Route path="/settings" component={SettingsRoot} />
<Route path="/guilds/:guildId/settings" component={GuildSettings}>
<Route path="/overview" component={GuildSettingsOverview} />
<Route path="/roles/:roleId" component={GuildSettingsRole}>
<Route path="/permissions" component={GuildSettingsRolePermissions} />
<Route path="/members" component={GuildSettingsRoleMembers} />
<Route path="/" component={GuildSettingsRoleOverview} />
</Route>
<Route path="/roles" component={GuildSettingsRoles} />
<Route path="/emojis" component={GuildSettingsEmojis} />
<Route path="/invites" component={() => 'wip'} />
</Route>
<Route path="/guilds/:guildId/settings" component={GuildSettingsRoot} />
<Route path="/guilds/:guildId/:channelId/settings" component={GuildChannelSettings}>
<Route path="/overview" component={GuildChannelSettingsOverview} />
<Route path="/permissions" component={GuildChannelSettingsPermissions} />
</Route>
<Route path="/guilds/:guildId/:channelId/settings" component={GuildChannelSettingsRoot} />
<Route component={App}>
<Route path="/loading" component={Loading} />
<Route path="/friends/requests" component={FriendRequests} />
<Route path="/friends/*" component={FriendsList} />
<Route path="/dms/:channelId" component={DmChannel} />
<Route path="/guilds/:guildId/:channelId" component={GuildChannel} />
<Route path="/guilds/:guildId" component={GuildHome} />
<Route path="/invite/:code" component={Invite} />
<Route path="/" component={Home} />
<Route path="*" component={NotFound} />
</Route>
</Show>
</Route>
</Router>
</div>
</Show>
Expand Down
15 changes: 15 additions & 0 deletions src/api/ApiCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,21 @@ export default class ApiCache {
return this.getMemberPermissions(guildId, this.clientId!, channelId)
}

getDefaultRole(guildId: bigint): Role {
const defaultRoleId = snowflakes.withModelType(guildId, snowflakes.ModelType.Role)
return this.roles.get(defaultRoleId)!
}

getMemberTopRole(guildId: bigint, userId: bigint): Role {
const roles = this.getMemberRoles(guildId, userId)
return maxIterator(roles, role => role.position) ?? this.getDefaultRole(guildId)
}

clientCanManage(guildId: bigint, userId: bigint): boolean {
return this.guilds.get(guildId)?.owner_id == this.clientId!
|| this.getMemberTopRole(guildId, this.clientId!).position > this.getMemberTopRole(guildId, userId).position
}

getMemberColor(guildId: bigint, memberId: bigint): ExtendedColor | undefined {
const member = this.members.get(memberKey(guildId, memberId))
if (!member) return undefined
Expand Down
8 changes: 5 additions & 3 deletions src/api/MessageGrouper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ function last<T>(array: T[]): T | undefined {
return array[array.length - 1]
}

const MESSAGE_HISTORY_LIMIT = 100

/**
* Groups messages by their author and timestamp.
*/
Expand Down Expand Up @@ -170,15 +172,15 @@ export default class MessageGrouper {
return

this.fetchLock = true
const params: Record<string, any> = { limit: 200 }
const params: Record<string, any> = { limit: MESSAGE_HISTORY_LIMIT }
if (this.fetchBefore != null)
params.before = this.fetchBefore

const response = await this.api.request<Message[]>('GET', `/channels/${this.channelId}/messages`, { params })
const messages = response.ensureOk().jsonOrThrow()

this.fetchBefore = last(messages)?.id
if (messages.length < 200)
if (messages.length < MESSAGE_HISTORY_LIMIT)
this.setNoMoreMessages(true)
this.insertMessages(messages.reverse())
this.fetchLock = false
Expand Down Expand Up @@ -219,7 +221,7 @@ export default class MessageGrouper {
channel_id: this.channelId,
embeds: [],
flags: 0,
stars: 0,
reactions: [],
edited_at: null,
mentions: [],
}
Expand Down
10 changes: 5 additions & 5 deletions src/components/channels/ConfirmChannelDeleteModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ModalTemplate} from "../ui/Modal";
import {createMemo, createSignal, Setter} from "solid-js";
import {ModalTemplate, useModal} from "../ui/Modal";
import {createMemo, createSignal} from "solid-js";
import {GuildChannel} from "../../types/channel";
import {getApi} from "../../api/Api";
import {useNavigate, useParams} from "@solidjs/router";
Expand All @@ -8,13 +8,13 @@ import Trash from "../icons/svg/Trash";

type Props = {
channel: GuildChannel,
setConfirmChannelDeleteModal: Setter<boolean>,
}

export default function ConfirmChannelDeleteModal(props: Props) {
const api = getApi()!
const navigate = useNavigate()
const params = useParams()
const {hideModal} = useModal()

const guild = createMemo(() => api.cache!.guilds.get(BigInt(params.guildId)))
const [isDeleting, setIsDeleting] = createSignal<boolean>(false)
Expand All @@ -38,11 +38,11 @@ export default function ConfirmChannelDeleteModal(props: Props) {
throw err
}
setIsDeleting(false)
props.setConfirmChannelDeleteModal(false)
hideModal()
if (params.channelId && BigInt(params.channelId) === props.channel.id) navigate(`/guilds/${params.guildId}`)
}}
>
<button class="btn border-none btn-ghost" onClick={() => props.setConfirmChannelDeleteModal(false)}>
<button class="btn border-none btn-ghost" onClick={hideModal}>
Cancel
</button>
<button type="submit" class="btn btn-danger border-none" disabled={isDeleting()}>
Expand Down
11 changes: 6 additions & 5 deletions src/components/channels/CreateCategoryModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {createSignal, Setter, Show} from "solid-js";
import {ModalTemplate} from "../ui/Modal";
import {createSignal, Show} from "solid-js";
import {ModalTemplate, useModal} from "../ui/Modal";
import Icon from "../icons/Icon";
import ChevronLeft from "../icons/svg/ChevronLeft";
import Plus from "../icons/svg/Plus";
Expand All @@ -8,12 +8,13 @@ import {useNavigate} from "@solidjs/router";
import {snowflakes} from "../../utils";
import ListTree from "../icons/svg/ListTree";

type Props = { setter: Setter<boolean>, guildId: bigint }
type Props = { guildId: bigint, parentId?: bigint }

export default function CreateCategoryModal(props: Props) {
let channelNameRef: HTMLInputElement | null = null
let api = getApi()!
let navigate = useNavigate()
const {hideModal} = useModal()

const [focused, setFocused] = createSignal(false)
const [error, setError] = createSignal<string>()
Expand Down Expand Up @@ -46,7 +47,7 @@ export default function CreateCategoryModal(props: Props) {
return setError(response.errorJsonOrThrow().message)
}

props.setter(false)
hideModal()
}}
>
<label class="mt-5 mb-2 text-fg/50 text-xs ml-0.5 w-96 mobile:w-full" for="name">
Expand Down Expand Up @@ -78,7 +79,7 @@ export default function CreateCategoryModal(props: Props) {
</p>
</Show>
<div class="flex gap-3 mt-3">
<div class="flex gap-x-2 btn btn-neutral" onClick={() => props.setter(false)}>
<div class="flex gap-x-2 btn btn-neutral" onClick={hideModal}>
<Icon icon={ChevronLeft} class="fill-neutral-content/60 select-none w-4 h-4" />
Back
</div>
Expand Down
Loading

0 comments on commit 5c78b70

Please sign in to comment.