import { CursorShape, Direction, QApplication, QBoxLayout, QClipboardMode, QCursor, QLabel, QPixmap, QPoint, QSize, QWidget, WidgetEventTypes, } from '@nodegui/nodegui'; import { Client, Constants, DQConstants } from 'discord.js'; import { __ } from 'i18n'; import { join } from 'path'; import { app, MAX_QSIZE } from '../..'; import { CustomStatus } from '../../structures/CustomStatus'; import { Events as AppEvents } from '../../structures/Events'; import { PresenceStatusColor } from '../../structures/PresenceStatusColor'; import { pictureWorker } from '../../utilities/PictureWorker'; import { resolveEmoji } from '../../utilities/ResolveEmoji'; import { DIconButton } from '../DIconButton/DIconButton'; import { UserMenu } from './UserMenu'; export class UserPanel extends QWidget { private avatar = new QLabel(this); private nameLabel = new QLabel(this); private discLabel = new QLabel(this); private statusIcon = new QLabel(this); private statusText = new QLabel(this); private statusCircle = new QLabel(this.avatar); private controls = new QBoxLayout(Direction.LeftToRight); private clipboard = QApplication.clipboard(); constructor() { super(); this.initComponent(); app.on(AppEvents.NEW_CLIENT, this.bindEvents.bind(this)); app.on(AppEvents.READY, () => { if (!app.config.enableAvatars) this.avatar.hide(); }); } private bindEvents(client: Client) { const { Events } = Constants as unknown as DQConstants; this.nameLabel.setText(__('CONNECTION_STATUS_CONNECTING')); this.discLabel.setText('#0000'); client.on(Events.CLIENT_READY, () => { this.updateData(); this.updateAvatar(); this.updatePresence(); }); client.on(Events.USER_UPDATE, (prev, cur) => { this.updateData(); if (prev.avatar !== cur.avatar) this.updateAvatar(); }); client.on(Events.PRESENCE_UPDATE, (_o, presence) => { if (presence.userID === client.user?.id) this.updatePresence(); }); client.on(Events.USER_SETTINGS_UPDATE, () => { this.updatePresence(); }); } private copiedTimer?: any; private copiedAmount = 0; private copyUserInfo() { const { clipboard, discLabel, statusText } = this; if (this.copiedTimer) clearTimeout(this.copiedTimer); this.copiedAmount += 1; clipboard.setText(app.client.user?.tag || '', QClipboardMode.Clipboard); [discLabel, statusText].forEach((w) => w.setText(__(`ACCOUNT_USERNAME_COPY_SUCCESS_${Math.min(this.copiedAmount, 11)}`))); this.copiedTimer = setTimeout(() => { this.updateData(); this.updatePresence(); this.copiedAmount = 0; }, 3000); } private initComponent() { const { avatar, nameLabel, discLabel, controls, statusCircle, statusIcon, statusText, } = this; this.setLayout(controls); this.setObjectName('UserPanel'); this.setMinimumSize(0, 52); this.setMaximumSize(MAX_QSIZE, 52); controls.setContentsMargins(8, 8, 8, 8); controls.setSpacing(0); avatar.setObjectName('UserAvatar'); avatar.setFixedSize(32, 32); avatar.setCursor(new QCursor(CursorShape.PointingHandCursor)); statusCircle.setObjectName('StatusCircle'); statusCircle.setFixedSize(16, 16); statusCircle.setProperty('tooltip', 'Offline'); statusCircle.move(18, 18); statusCircle.hide(); const layInfo = new QBoxLayout(Direction.TopToBottom); layInfo.setSpacing(0); layInfo.setContentsMargins(8, 0, 0, 0); nameLabel.setText(__('NO_ACCOUNT')); nameLabel.setObjectName('NameLabel'); const layStat = new QBoxLayout(Direction.LeftToRight); layStat.setSpacing(4); layStat.setContentsMargins(0, 0, 0, 0); discLabel.setText('#0000'); discLabel.setObjectName('DiscLabel'); statusText.setObjectName('DiscLabel'); [nameLabel, discLabel, statusText].forEach((f) => { f.setCursor(CursorShape.PointingHandCursor); f.addEventListener(WidgetEventTypes.MouseButtonPress, this.copyUserInfo.bind(this)); }); layStat.addWidget(discLabel, 1); layStat.addWidget(statusIcon); layStat.addWidget(statusText, 1); statusIcon.hide(); statusText.hide(); layInfo.addWidget(nameLabel); layInfo.addLayout(layStat); const userMenu = new UserMenu(this); userMenu.setObjectName('UserMenu'); avatar.addEventListener(WidgetEventTypes.MouseButtonPress, () => { userMenu.adjustSize(); const point = this.mapToGlobal(new QPoint(0, 0)); point.setY(point.y() - userMenu.size().height() - 10); point.setX(point.x() + 10); userMenu.popup(point); }); userMenu.addEventListener(WidgetEventTypes.Close, () => { setTimeout(this.updatePresence.bind(this), 500); }); const iBtn = new DIconButton({ iconPath: join(__dirname, './assets/icons/invite.png'), iconQSize: new QSize(20, 20), tooltipText: __('INSTANT_INVITE_INVITE_CODE'), }); iBtn.setFixedSize(32, 32); iBtn.addEventListener('clicked', () => app.window.dialogs.acceptInvite.show()); const setBtn = new DIconButton({ iconPath: join(__dirname, './assets/icons/cog.png'), iconQSize: new QSize(20, 20), tooltipText: __('USER_SETTINGS'), }); setBtn.setFixedSize(32, 32); setBtn.addEventListener('clicked', () => app.emit(AppEvents.SWITCH_VIEW, 'settings')); controls.addWidget(avatar, 0); controls.addLayout(layInfo, 1); controls.addWidget(iBtn, 0); controls.addWidget(setBtn, 0); } async updateData(): Promise { const { nameLabel, discLabel, statusCircle } = this; const { client } = app; if (!client.user) { nameLabel.setText(__('NO_ACCOUNT')); discLabel.setText('#0000'); statusCircle.hide(); return; } nameLabel.setText(client.user.username); discLabel.setText(`#${client.user.discriminator}`); } async updateAvatar(): Promise { const { avatar } = this; const { client } = app; if (!client.user) return; const path = await pictureWorker.loadImage( client.user.displayAvatarURL({ format: 'png', size: 256 }), ); avatar.setPixmap(new QPixmap(path).scaled(32, 32, 1, 1)); } async loadStatusEmoji(status: CustomStatus) { try { const emojiPath = await resolveEmoji(status); const pix = new QPixmap(emojiPath); this.statusIcon.setPixmap(pix.scaled(14, 14, 1, 1)); this.statusIcon.show(); } catch (e) { this.statusIcon.hide(); } } async updatePresence() { const { discLabel, statusCircle, statusIcon, statusText, } = this; if (!app.client?.user) return; const { customStatus, presence } = app.client.user; statusCircle.setInlineStyle(`background-color: ${PresenceStatusColor.get(presence.status)};`); statusCircle.setProperty('toolTip', presence.status); statusCircle.show(); if (!customStatus) { statusIcon.hide(); statusText.hide(); discLabel.show(); return; } this.loadStatusEmoji(customStatus); this.statusText.setText(customStatus.text || ''); statusText.show(); discLabel.hide(); } }