Skip to content
Snippets Groups Projects
ButtonItem.tsx 6.07 KiB
Newer Older
insert's avatar
insert committed
import classNames from 'classnames';
import styles from "./Item.module.scss";
insert's avatar
insert committed
import Tooltip from '../../common/Tooltip';
import IconButton from '../../ui/IconButton';
insert's avatar
insert committed
import { Localizer, Text } from "preact-i18n";
import { X, Crown } from "@styled-icons/boxicons-regular";
insert's avatar
insert committed
import { Children } from "../../../types/Preact";
insert's avatar
insert committed
import UserIcon from '../../common/user/UserIcon';
insert's avatar
insert committed
import ChannelIcon from '../../common/ChannelIcon';
insert's avatar
insert committed
import UserStatus from '../../common/user/UserStatus';
insert's avatar
insert committed
import { attachContextMenu } from 'preact-context-menu';
insert's avatar
insert committed
import { Channels, Users } from "revolt.js/dist/api/objects";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
insert's avatar
insert committed
import { useIntermediate } from '../../../context/intermediate/Intermediate';
import { stopPropagation } from '../../../lib/stopPropagation';
insert's avatar
insert committed

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) {
insert's avatar
insert committed
    const { openScreen } = useIntermediate();
insert's avatar
insert committed

    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)}
insert's avatar
insert committed
            onContextMenu={attachContextMenu('Menu', {
insert's avatar
insert committed
                user: user._id,
                channel: channel?._id,
                unread: alert,
                contextualChannel: context?._id
insert's avatar
insert committed
            })}>
            <UserIcon className={styles.avatar} target={user} size={32} status />
insert's avatar
insert committed
            <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>
insert's avatar
insert committed
                            <Tooltip
insert's avatar
insert committed
                                content={
                                    <Text id="app.main.groups.owner" />
                                }
insert's avatar
insert committed
                            >
                                <Crown size={20} />
insert's avatar
insert committed
                            </Tooltip>
insert's avatar
insert committed
                        </Localizer>
                )}
                {alert && <div className={styles.alert} data-style={alert}>{ alertCount }</div>}
                { !isTouchscreenDevice && channel &&
                    <IconButton className={styles.icon}
                        onClick={e => stopPropagation(e) && openScreen({ id: 'special_prompt', type: 'close_dm', target: channel })}>
insert's avatar
insert committed
                        <X size={24} />
insert's avatar
insert committed
                    </IconButton>
insert's avatar
insert committed
                }
            </div>
        </div>
    )
}

type ChannelProps = CommonProps & {
    channel: Channels.Channel & { unread?: string },
insert's avatar
insert committed
    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 }} />
    }

insert's avatar
insert committed
    const { openScreen } = useIntermediate();
insert's avatar
insert committed

    return (
        <div data-active={active}
insert's avatar
insert committed
            data-alert={typeof alert === 'string'}
            aria-label={{}} /*TOFIX: ADD ARIA LABEL*/
insert's avatar
insert committed
            className={classNames(styles.item, { [styles.compact]: compact })}
            onContextMenu={attachContextMenu('Menu', { channel: channel._id, unread: typeof channel.unread !== 'undefined' })}>
            <ChannelIcon className={styles.avatar} target={channel} size={compact ? 24 : 32} />
insert's avatar
insert committed
            <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" && (
insert's avatar
insert committed
                    <IconButton
insert's avatar
insert committed
                        className={styles.icon}
insert's avatar
insert committed
                        onClick={() => openScreen({ id: 'special_prompt', type: 'leave_group', target: channel })}>
insert's avatar
insert committed
                        <X size={24} />
insert's avatar
insert committed
                    </IconButton>
insert's avatar
insert committed
                )}
            </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>
    )
}