5180 fichiers 38.5 Mo 0 téléchargements 342 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / groups / views / forums / GroupForumThreadListView.tsx
GroupForumThreadListView.tsx 14.2 Ko Télécharger le fichier
import {
    ExtendedForumData,
    GetThreadsMessageComposer,
    GuildForumThread,
    GuildForumThreadsEvent,
    ModerateThreadMessageComposer,
    UpdateThreadMessageComposer,
    UpdateThreadMessageEvent
} from '@octane/renderer';
import { FC, useEffect, useState } from 'react';
import { LocalizeText, ReportType, SendMessageComposer } from '../../../../api';
import lockedIcon from '../../../../assets/images/groups/native/forum_forum_locked.png';
import unlockedIcon from '../../../../assets/images/groups/native/forum_forum_unlocked.png';
import hideIcon from '../../../../assets/images/groups/native/forum_forum_hide.png';
import pinnedIcon from '../../../../assets/images/groups/native/forum_forum_pinned.png';
import unpinnedIcon from '../../../../assets/images/groups/native/forum_forum_unpinned.png';
import reportIcon from '../../../../assets/images/groups/native/forum_forum_report.png';
import unhideIcon from '../../../../assets/images/groups/native/forum_forum_unhide.png';
import { ClassicScrollAreaView } from '../../../../common';
import { useHelp, useMessageEvent } from '../../../../hooks';
import { flatText, GroupText } from '../GroupNativeLayout';
import { FORUM_PAGE_SIZE, FORUM_SURFACE, stripTags, ForumButton, forumAge, ForumPager } from './GroupForumLayout';
import { BaseButton } from '../../../../common/BaseButton';

// Thread states: 1 visible, 10 hidden by a group administrator, 20 deleted by Hotel staff.
export const THREAD_HIDDEN_BY_ADMIN = 10;
export const THREAD_DELETED_BY_STAFF = 20;
export const THREAD_VISIBLE = 1;

/** ThreadListView.getThreadColor: hidden by an admin grey, deleted by staff red, the others alternate starting light. */
const threadColor = (state: number, rowIndex: number): number => {
    if (state === THREAD_HIDDEN_BY_ADMIN) return 0xaaaaaa;
    if (state === THREAD_DELETED_BY_STAFF) return 0xffb0a5;

    return rowIndex % 2 === 1 ? 0xb2e6fa : 0xeefeff;
};

/** ThreadListView.getModerationMessage. */
const moderationMessage = (thread: GuildForumThread): string => {
    if (thread.state === THREAD_HIDDEN_BY_ADMIN) return LocalizeText('groupforum.view.thread_hidden_by_admin', ['ADMIN_NAME', 'admin_name'], [thread.adminName, thread.adminName]);
    if (thread.state === THREAD_DELETED_BY_STAFF) return LocalizeText('groupforum.view.thread_hidden_by_staff', ['ADMIN_NAME', 'admin_name'], [thread.adminName, thread.adminName]);

    return '';
};

const ROW_WIDTH = 532;
const BUTTON_WIDTH = 25;

/** Maps the error code of a permission to the text of that refusal ("Only group owner can start new threads."). */
export const forumPermissionText = (code: string, operationKey: string): string => {
    const operation = LocalizeText(`groupforum.view.error.${operationKey}`);

    return LocalizeText(`groupforum.view.error.${code}`, ['operation', 'OPERATION'], [operation, operation]);
};

interface GroupForumThreadListViewProps {
    forumData: ExtendedForumData;
    groupId: number;
    pageIndex: number;
    /** Changes whenever the controller asks for the page again (requestThreadList), e.g. after a new thread was posted. */
    reloadKey?: number;
    /** GroupForumController.getThreadLastReadMessageIndex: the index of the last message of the thread the reader has seen. */
    getLastReadMessageIndex: (thread: GuildForumThread) => number;
    onLeave: () => void;
    onNewThread: () => void;
    onOpenThread: (thread: GuildForumThread) => void;
    onPageChange: (pageIndex: number) => void;
}


export const GroupForumThreadListView: FC<GroupForumThreadListViewProps> = ({ forumData, groupId, pageIndex, reloadKey = 0, getLastReadMessageIndex, onLeave, onNewThread, onOpenThread, onPageChange }) => {
    const [threads, setThreads] = useState<GuildForumThread[]>([]);
    const { report = null } = useHelp();
    const canModerate = forumData.canModerate;
    const isStaff = forumData.isStaff;
    // ThreadListView: moderators and Hotel staff get working lock, pin and hide buttons.
    const canManage = canModerate || isStaff;
    const canReport = forumData.canReport;
    const canPostThread = forumData.canPostThread;
    const pageCount = Math.ceil(forumData.totalThreads / FORUM_PAGE_SIZE);

    useMessageEvent<GuildForumThreadsEvent>(GuildForumThreadsEvent, (event) => {
        const parser = event.getParser();

        if (parser.groupId !== groupId || parser.startIndex !== pageIndex * FORUM_PAGE_SIZE) return;

        setThreads(parser.threads);
    });

    useMessageEvent<UpdateThreadMessageEvent>(UpdateThreadMessageEvent, (event) => {
        const parser = event.getParser();

        if (parser.groupId !== groupId) return;

        setThreads((previous) => previous.map((thread) => (thread.threadId === parser.thread.threadId ? parser.thread : thread)));
    });

    useEffect(() => {
        if (!groupId) return;

        SendMessageComposer(new GetThreadsMessageComposer(groupId, pageIndex * FORUM_PAGE_SIZE, FORUM_PAGE_SIZE));
    }, [groupId, pageIndex, reloadKey]);

    // handleButtonVisibility: a visible thread can be hidden, one hidden by an admin shown again, and one deleted by staff only shown again by staff.
    const hideActionOf = (thread: GuildForumThread): 'hide' | 'unhide' | null => {
        if (!canManage) return null;
        if (thread.state === THREAD_HIDDEN_BY_ADMIN) return 'unhide';
        if (thread.state === THREAD_DELETED_BY_STAFF) return isStaff ? 'unhide' : null;

        return 'hide';
    };

    // GroupForumController.deleteThread hides with 10 for a group admin and 20 for staff; unDeleteThread sends 1.
    const moderate = (thread: GuildForumThread, action: 'hide' | 'unhide') => {
        const hiddenState = isStaff ? THREAD_DELETED_BY_STAFF : canModerate ? THREAD_HIDDEN_BY_ADMIN : 0;

        SendMessageComposer(new ModerateThreadMessageComposer(groupId, thread.threadId, action === 'unhide' ? THREAD_VISIBLE : hiddenState));
    };

    const statusKey = !canPostThread ? forumData.postThreadPermissionError : '';

    return (
        <>
            <GroupText background={FORUM_SURFACE} height={25} overrides={flatText(16, { bold: true, color: 0xa6a6a2 })} text={LocalizeText('groupforum.view.all_threads')} width={541} x={0} y={115} />
            <div className="octane-forum__list">
                <ClassicScrollAreaView className="octane-forum__scroll" contentClassName="octane-forum__scroll-content" minThumbSize={26} scrollStep={41}>
                    {/* ThreadListView.update lists the threads in the order the server sends them. */}
                    {threads.map((thread, rowIndex) => {
                        // ThreadListView.updateListItem: the unread count follows what the reader has seen of the thread this session.
                        const unreadCount = thread.totalMessages - getLastReadMessageIndex(thread) - 1;
                        const isUnread = unreadCount > 0;
                        const background = threadColor(thread.state, rowIndex);
                        const rowColor = '#' + background.toString(16).padStart(6, '0');
                        const subject = thread.header === '' ? '(No Subject)' : thread.header;
                        const header = thread.state > 1 && !canModerate && !isStaff ? moderationMessage(thread) : subject;
                        const details = stripTags(
                            LocalizeText(
                                'groupforum.view.thread_details',
                                ['THREAD_AUTHOR_NAME', 'CREATION_TIME', 'LAST_AUTHOR_NAME', 'UPDATE_TIME'],
                                [thread.authorName, forumAge(thread.creationTimeAsSecondsAgo), thread.lastUserName, forumAge(thread.lastCommentTime)]
                            )
                        );
                        const hideAction = hideActionOf(thread);
                        const buttonCount = (hideAction ? 1 : 0) + (canReport ? 1 : 0);
                        const buttonsWidth = buttonCount * BUTTON_WIDTH;
                        const textsWidth = ROW_WIDTH - 20 - 1 - 1 - 140 - (buttonCount ? 1 + buttonsWidth : 0);

                        return (
                            <div key={thread.threadId} className="octane-forum__thread">
                                {/* handleLeftButtonsVisibility: others only see the lock and the pin of a locked or pinned thread, disabled; info_buttons is a
                                    vertical item list, so a pin without a lock moves up. */}
                                <div className="octane-forum__thread-flags" style={{ background: rowColor }}>
                                    {(canManage || thread.isLocked) && (
                                        <BaseButton
                                            className="octane-forum__flag"
                                            disabled={!canManage}
                                            style={{ top: 0 }}
                                            type="button"
                                            onClick={() => SendMessageComposer(new UpdateThreadMessageComposer(groupId, thread.threadId, !thread.isLocked, thread.isPinned))}
                                        >
                                            <img alt="" draggable={false} src={thread.isLocked ? lockedIcon : unlockedIcon} style={{ left: 3, top: 1 }} />
                                        </BaseButton>
                                    )}
                                    {(canManage || thread.isPinned) && (
                                        <BaseButton
                                            className="octane-forum__flag"
                                            disabled={!canManage}
                                            style={{ top: canManage || thread.isLocked ? 20 : 0 }}
                                            type="button"
                                            onClick={() => SendMessageComposer(new UpdateThreadMessageComposer(groupId, thread.threadId, thread.isLocked, !thread.isPinned))}
                                        >
                                            <img alt="" draggable={false} src={thread.isPinned ? pinnedIcon : unpinnedIcon} style={{ left: 3, top: 2 }} />
                                        </BaseButton>
                                    )}
                                </div>
                                <div className="octane-forum__thread-body" style={{ left: 21, width: textsWidth, background: rowColor }}>
                                    <GroupText background={background} overrides={isUnread ? { bold: true } : undefined} text={header} x={0} y={0} />
                                    <GroupText background={background} overrides={flatText(10)} text={details} x={0} y={16} />
                                    {/* header_region and unread_region go to the first unread message (onGoToFirstUnread). */}
                                    <BaseButton className="octane-forum__thread-header" type="button" onClick={() => onOpenThread(thread)} />
                                </div>
                                <BaseButton className="octane-forum__thread-counts" type="button" style={{ left: 21 + textsWidth + 1, background: rowColor }} onClick={() => onOpenThread(thread)}>
                                    <GroupText
                                        background={background}
                                        overrides={flatText(10, { bold: isUnread })}
                                        text={LocalizeText('groupforum.view.thread_details1', ['TOTAL_MESSAGES', 'total_messages'], [String(thread.totalMessages), String(thread.totalMessages)])}
                                        x={0}
                                        y={0}
                                    />
                                    <GroupText
                                        background={background}
                                        overrides={flatText(10, { bold: isUnread })}
                                        text={LocalizeText('groupforum.view.thread_details2', ['NEW_MESSAGES', 'new_messages'], [String(unreadCount), String(unreadCount)])}
                                        x={0}
                                        y={15}
                                    />
                                </BaseButton>
                                {buttonCount > 0 && (
                                    <div className="octane-forum__thread-actions" style={{ left: ROW_WIDTH - buttonsWidth, width: buttonsWidth }}>
                                        {hideAction && (
                                            <BaseButton className="octane-forum__action is-hide" type="button" onClick={() => moderate(thread, hideAction)}>
                                                <img alt="" draggable={false} src={hideAction === 'unhide' ? unhideIcon : hideIcon} style={{ left: 5, top: 11 }} />
                                            </BaseButton>
                                        )}
                                        {canReport && (
                                            <BaseButton className="octane-forum__action is-report" style={{ left: hideAction ? BUTTON_WIDTH : 0 }} type="button" onClick={() => report(ReportType.THREAD, { groupId, threadId: thread.threadId })}>
                                                <img alt="" draggable={false} src={reportIcon} style={{ left: 4, top: 12 }} />
                                            </BaseButton>
                                        )}
                                    </div>
                                )}
                            </div>
                        );
                    })}
                </ClassicScrollAreaView>
            </div>
            <div className="octane-forum__footer">
                <ForumButton label={LocalizeText('groupforum.view.mark_read')} width={95} x={10} onClick={onLeave} />
                <ForumButton disabled={!canPostThread} label={LocalizeText('groupforum.view.start_thread')} right={178} tint="blue" width={115} onClick={onNewThread} />
                <ForumPager pageCount={pageCount} pageIndex={pageIndex} onPage={onPageChange} />
            </div>
            {statusKey && (
                <div className="octane-forum__status">
                    <GroupText align="center" background={FORUM_SURFACE} overrides={flatText(11)} text={forumPermissionText(statusKey, 'operation_post_thread')} width={300} x={3} y={3} />
                </div>
            )}
        </>
    );
};
15Sujets
1Dépôts
20Membres
3En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser