mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 01:15:28 +00:00
Port navigation.
This commit is contained in:
157
src/components/navigation/items/ButtonItem.tsx
Normal file
157
src/components/navigation/items/ButtonItem.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import classNames from 'classnames';
|
||||
import styles from "./Item.module.scss";
|
||||
import UserIcon from '../../common/UserIcon';
|
||||
import { Localizer, Text } from "preact-i18n";
|
||||
import { X, Zap } from "@styled-icons/feather";
|
||||
import UserStatus from '../../common/UserStatus';
|
||||
import { Children } from "../../../types/Preact";
|
||||
import ChannelIcon from '../../common/ChannelIcon';
|
||||
import { Channels, Users } from "revolt.js/dist/api/objects";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
interface CommonProps {
|
||||
active?: boolean
|
||||
alert?: 'unread' | 'mention'
|
||||
alertCount?: number
|
||||
}
|
||||
|
||||
type UserProps = CommonProps & {
|
||||
user: Users.User,
|
||||
context?: Channels.Channel,
|
||||
channel?: Channels.DirectMessageChannel
|
||||
}
|
||||
|
||||
export function UserButton({ active, alert, alertCount, user, context, channel }: UserProps) {
|
||||
// const { openScreen } = useContext(IntermediateContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(styles.item, styles.user)}
|
||||
data-active={active}
|
||||
data-alert={typeof alert === 'string'}
|
||||
data-online={typeof channel !== 'undefined' || (user.online && user.status?.presence !== Users.Presence.Invisible)}
|
||||
/*onContextMenu={attachContextMenu('Menu', {
|
||||
user: user._id,
|
||||
channel: channel?._id,
|
||||
unread: alert,
|
||||
contextualChannel: context?._id
|
||||
})}*/
|
||||
>
|
||||
<div className={styles.avatar}>
|
||||
<UserIcon target={user} size={32} status />
|
||||
</div>
|
||||
<div className={styles.name}>
|
||||
<div>{user.username}</div>
|
||||
{
|
||||
<div className={styles.subText}>
|
||||
{ channel?.last_message && alert ? (
|
||||
channel.last_message.short
|
||||
) : (
|
||||
<UserStatus user={user} />
|
||||
) }
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className={styles.button}>
|
||||
{ context?.channel_type === "Group" &&
|
||||
context.owner === user._id && (
|
||||
<Localizer>
|
||||
{/*<Tooltip
|
||||
content={
|
||||
<Text id="app.main.groups.owner" />
|
||||
}
|
||||
>*/}
|
||||
<Zap size={20} />
|
||||
{/*</Tooltip>*/}
|
||||
</Localizer>
|
||||
)}
|
||||
{alert && <div className={styles.alert} data-style={alert}>{ alertCount }</div>}
|
||||
{ !isTouchscreenDevice && channel &&
|
||||
/*<IconButton
|
||||
className={styles.icon}
|
||||
style="default"
|
||||
onClick={() => openScreen({ id: 'special_prompt', type: 'close_dm', target: channel })}
|
||||
>*/
|
||||
<X size={24} />
|
||||
/*</IconButton>*/
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type ChannelProps = CommonProps & {
|
||||
channel: Channels.Channel,
|
||||
user?: Users.User
|
||||
compact?: boolean
|
||||
}
|
||||
|
||||
export function ChannelButton({ active, alert, alertCount, channel, user, compact }: ChannelProps) {
|
||||
if (channel.channel_type === 'SavedMessages') throw "Invalid channel type.";
|
||||
if (channel.channel_type === 'DirectMessage') {
|
||||
if (typeof user === 'undefined') throw "No user provided.";
|
||||
return <UserButton {...{ active, alert, channel, user }} />
|
||||
}
|
||||
|
||||
//const { openScreen } = useContext(IntermediateContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-active={active}
|
||||
data-alert={typeof alert === 'string'}
|
||||
className={classNames(styles.item, { [styles.compact]: compact })}
|
||||
//onContextMenu={attachContextMenu('Menu', { channel: channel._id })}>
|
||||
>
|
||||
<div className={styles.avatar}>
|
||||
<ChannelIcon target={channel} size={compact ? 24 : 32} />
|
||||
</div>
|
||||
<div className={styles.name}>
|
||||
<div>{channel.name}</div>
|
||||
{ channel.channel_type === 'Group' &&
|
||||
<div className={styles.subText}>
|
||||
{(channel.last_message && alert) ? (
|
||||
channel.last_message.short
|
||||
) : (
|
||||
<Text
|
||||
id="quantities.members"
|
||||
plural={channel.recipients.length}
|
||||
fields={{ count: channel.recipients.length }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className={styles.button}>
|
||||
{alert && <div className={styles.alert} data-style={alert}>{ alertCount }</div>}
|
||||
{!isTouchscreenDevice && channel.channel_type === "Group" && (
|
||||
/*<IconButton
|
||||
className={styles.icon}
|
||||
style="default"
|
||||
onClick={() => openScreen({ id: 'special_prompt', type: 'leave_group', target: channel })}
|
||||
>*/
|
||||
<X size={24} />
|
||||
/*</IconButton>*/
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type ButtonProps = CommonProps & {
|
||||
onClick?: () => void
|
||||
children?: Children
|
||||
className?: string
|
||||
compact?: boolean
|
||||
}
|
||||
|
||||
export default function ButtonItem({ active, alert, alertCount, onClick, className, children, compact }: ButtonProps) {
|
||||
return (
|
||||
<div className={classNames(styles.item, { [styles.compact]: compact, [styles.normal]: !compact }, className)}
|
||||
onClick={onClick}
|
||||
data-active={active}
|
||||
data-alert={typeof alert === 'string'}>
|
||||
<div className={styles.content}>{ children }</div>
|
||||
{alert && <div className={styles.alert} data-style={alert}>{ alertCount }</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
35
src/components/navigation/items/ConnectionStatus.tsx
Normal file
35
src/components/navigation/items/ConnectionStatus.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Text } from "preact-i18n";
|
||||
import Banner from "../../ui/Banner";
|
||||
import { useContext } from "preact/hooks";
|
||||
import { AppContext, ClientStatus } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
export default function ConnectionStatus() {
|
||||
const { status } = useContext(AppContext);
|
||||
|
||||
if (status === ClientStatus.OFFLINE) {
|
||||
return (
|
||||
<Banner>
|
||||
<Text id="app.special.status.offline" />
|
||||
</Banner>
|
||||
);
|
||||
} else if (status === ClientStatus.DISCONNECTED) {
|
||||
return (
|
||||
<Banner>
|
||||
<Text id="app.special.status.disconnected" />
|
||||
</Banner>
|
||||
);
|
||||
} else if (status === ClientStatus.CONNECTING) {
|
||||
return (
|
||||
<Banner>
|
||||
<Text id="app.special.status.connecting" />
|
||||
</Banner>
|
||||
);
|
||||
} else if (status === ClientStatus.RECONNECTING) {
|
||||
return (
|
||||
<Banner>
|
||||
<Text id="app.special.status.reconnecting" />
|
||||
</Banner>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
303
src/components/navigation/items/Item.module.scss
Normal file
303
src/components/navigation/items/Item.module.scss
Normal file
@@ -0,0 +1,303 @@
|
||||
.item {
|
||||
height: 48px;
|
||||
display: flex;
|
||||
padding: 0 8px;
|
||||
user-select: none;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: .1s ease-in-out background-color;
|
||||
|
||||
color: var(--tertiary-foreground);
|
||||
stroke: var(--tertiary-foreground);
|
||||
|
||||
&.normal {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
&.compact {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
&.user {
|
||||
opacity: 0.4;
|
||||
cursor: pointer;
|
||||
transition: .15s ease opacity;
|
||||
|
||||
&[data-online="true"],
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
|
||||
div {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
transition: color .1s ease-in-out;
|
||||
|
||||
&.content {
|
||||
gap: 8px;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
&.avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.name {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
font-weight: 600;
|
||||
font-size: .90625rem;
|
||||
flex-direction: column;
|
||||
|
||||
.subText {
|
||||
margin-top: -1px;
|
||||
font-weight: 500;
|
||||
font-size: .6875rem;
|
||||
color: var(--tertiary-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
&.button {
|
||||
flex-shrink: 0;
|
||||
|
||||
svg {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
transition: .1s ease-in-out opacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.compact):hover {
|
||||
div.button .alert {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.button svg {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-active="true"] {
|
||||
cursor: default;
|
||||
background: var(--hover);
|
||||
|
||||
.unread {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-alert="true"], &[data-active="true"], &:hover {
|
||||
color: var(--foreground);
|
||||
stroke: var(--foreground);
|
||||
|
||||
.subText {
|
||||
color: var(--secondary-foreground) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.alert {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin: 9px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background: var(--foreground);
|
||||
|
||||
display: grid;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
place-items: center;
|
||||
|
||||
&[data-style="mention"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: white;
|
||||
background: var(--error);
|
||||
}
|
||||
}
|
||||
|
||||
/* ! FIXME: check if anything is missing, then remove this block
|
||||
.olditem {
|
||||
display: flex;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
|
||||
gap: 8px;
|
||||
height: 48px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
border-radius: 6px;
|
||||
box-sizing: content-box;
|
||||
transition: .1s ease background-color;
|
||||
|
||||
color: var(--tertiary-foreground);
|
||||
stroke: var(--tertiary-foreground);
|
||||
|
||||
.avatar {
|
||||
flex-shrink: 0;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
padding: 10px 0;
|
||||
box-sizing: content-box;
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
transition: color .1s ease-in-out;
|
||||
|
||||
&.content {
|
||||
gap: 8px;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
&.name {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: .90625rem;
|
||||
font-weight: 600;
|
||||
|
||||
.subText {
|
||||
font-size: .6875rem;
|
||||
margin-top: -1px;
|
||||
color: var(--tertiary-foreground);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&.unread {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin: 9px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background: var(--foreground);
|
||||
}
|
||||
|
||||
&.button {
|
||||
flex-shrink: 0;
|
||||
|
||||
.icon {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
transition: 0.1s ease opacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&[data-active="true"] {
|
||||
color: var(--foreground);
|
||||
stroke: var(--foreground);
|
||||
background: var(--hover);
|
||||
cursor: default;
|
||||
|
||||
.subText {
|
||||
color: var(--secondary-foreground) !important;
|
||||
}
|
||||
|
||||
.unread {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-alert="true"] {
|
||||
color: var(--secondary-foreground);
|
||||
}
|
||||
|
||||
&[data-type="user"] {
|
||||
opacity: 0.4;
|
||||
color: var(--foreground);
|
||||
transition: 0.15s ease opacity;
|
||||
cursor: pointer;
|
||||
|
||||
&[data-online="true"],
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
//background: none;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-size="compact"] {
|
||||
margin-bottom: 2px;
|
||||
height: 32px;
|
||||
transition: border-inline-start .1s ease-in-out;
|
||||
border-inline-start: 4px solid transparent;
|
||||
|
||||
&[data-active="true"] {
|
||||
border-inline-start: 4px solid var(--accent);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-size="small"] {
|
||||
margin-bottom: 2px;
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--hover);
|
||||
|
||||
div.button .unread {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.button .icon {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
Reference in New Issue
Block a user