diff --git a/public/UITexts.example b/public/UITexts.example index b12e768..9ad6f72 100644 --- a/public/UITexts.example +++ b/public/UITexts.example @@ -38,5 +38,19 @@ "wiredfurni.params.texts.placeholder_type": "Tipo di segnaposto:", "wiredfurni.params.texts.placeholder_type.1": "Singolo", "wiredfurni.params.texts.placeholder_type.2": "Multiplo", - "wiredfurni.params.texts.select_delimiter": "Seleziona il delimitatore:" + "wiredfurni.params.texts.select_delimiter": "Seleziona il delimitatore:", + "groupforum.list.tab.most_active": "Meest active threads", + "groupforum.list.tab.my_forums": "Mijn group forums", + "groupforum.list.no_forums": "Er zijn geen forums", + "groupforum.view.threads": "Aantal threads", + "groupforum.thread.pin": "Pin hem vast", + "groupforum.thread.unpin": "Unpin bericht", + "groupforum.thread.lock": "Lock de thread", + "groupforum.thread.unlock": "Unlock de thread", + "groupforum.thread.hide": "Verberg thread", + "groupforum.thread.restore": "Maak thread weer zichtbaar", + "groupforum.thread.delete": "Verwijder thread + posts", + "groupforum.message.hide": "Verberg bericht", + "group.forum.enable.caption": "Enable / Disable Group forum", + "group.forum.enable.help": "Als je de group forum disabled dan verwijderen ook alle posts!" } diff --git a/src/api/groups/IGroupData.ts b/src/api/groups/IGroupData.ts index bb65b49..b5d43b3 100644 --- a/src/api/groups/IGroupData.ts +++ b/src/api/groups/IGroupData.ts @@ -8,6 +8,7 @@ export interface IGroupData groupHomeroomId: number; groupState: number; groupCanMembersDecorate: boolean; + groupHasForum: boolean; groupColors: number[]; groupBadgeParts: GroupBadgePart[]; } diff --git a/src/components/groups/views/GroupManagerView.tsx b/src/components/groups/views/GroupManagerView.tsx index b8336ab..c24b547 100644 --- a/src/components/groups/views/GroupManagerView.tsx +++ b/src/components/groups/views/GroupManagerView.tsx @@ -77,6 +77,7 @@ export const GroupManagerView: FC<{}> = props => groupHomeroomId: parser.roomId, groupState: parser.state, groupCanMembersDecorate: parser.canMembersDecorate, + groupHasForum: parser.hasForum, groupColors: [ parser.colorA, parser.colorB ], groupBadgeParts }); @@ -85,7 +86,7 @@ export const GroupManagerView: FC<{}> = props => if(!groupData || (groupData.groupId <= 0)) return null; return ( - + { TABS.map(tab => diff --git a/src/components/groups/views/forums/GroupForumListView.tsx b/src/components/groups/views/forums/GroupForumListView.tsx index 7156274..9ac2c5e 100644 --- a/src/components/groups/views/forums/GroupForumListView.tsx +++ b/src/components/groups/views/forums/GroupForumListView.tsx @@ -16,7 +16,7 @@ export const GroupForumListView: FC = props => { const { onOpenForum = null } = props; const [ forums, setForums ] = useState([]); - const [ listMode, setListMode ] = useState(2); // 2 = most active + const [ listMode, setListMode ] = useState(0); // 0 = most active const [ startIndex, setStartIndex ] = useState(0); const [ totalForums, setTotalForums ] = useState(0); @@ -59,8 +59,8 @@ export const GroupForumListView: FC = props => className="form-select form-select-sm" value={ listMode } onChange={ e => { setListMode(parseInt(e.target.value)); setStartIndex(0); } }> - - + + diff --git a/src/components/groups/views/forums/GroupForumThreadListView.tsx b/src/components/groups/views/forums/GroupForumThreadListView.tsx index 5c24ae2..99eda9e 100644 --- a/src/components/groups/views/forums/GroupForumThreadListView.tsx +++ b/src/components/groups/views/forums/GroupForumThreadListView.tsx @@ -101,7 +101,7 @@ export const GroupForumThreadListView: FC = props } - { forumData && forumData.hasPostThreadPermissionError && + { forumData && forumData.hasReadPermissionError && forumData.hasPostThreadPermissionError && } @@ -119,6 +119,16 @@ export const GroupForumThreadListView: FC = props { forumData.totalMessages } { LocalizeText('messageboard.messages') } } + { forumData && !forumData.hasReadPermissionError && + + + { LocalizeText('groupforum.view.error.operation_read') } + + { LocalizeText('groupforum.view.error.' + forumData.readPermissionError) } + + + } + { (!forumData || forumData.hasReadPermissionError) && { sortedThreads.map((thread, index) => { @@ -195,7 +205,7 @@ export const GroupForumThreadListView: FC = props { LocalizeText('groupforum.list.load_more') } } - + } ); }; diff --git a/src/components/groups/views/forums/GroupForumThreadView.tsx b/src/components/groups/views/forums/GroupForumThreadView.tsx index 8d9ee8c..eabbf60 100644 --- a/src/components/groups/views/forums/GroupForumThreadView.tsx +++ b/src/components/groups/views/forums/GroupForumThreadView.tsx @@ -1,4 +1,4 @@ -import { GetMessagesMessageComposer, ModerateMessageMessageComposer, ModerateThreadMessageComposer, PostMessageMessageComposer, PostMessageMessageEvent, PostThreadMessageEvent, ThreadMessagesMessageEvent, UpdateMessageMessageEvent, UpdateThreadMessageComposer, UpdateThreadMessageEvent } from '@nitrots/nitro-renderer'; +import { GetMessagesMessageComposer, ModerateMessageMessageComposer, ModerateThreadMessageComposer, PostMessageMessageComposer, PostMessageMessageEvent, PostThreadMessageEvent, ThreadMessagesMessageEvent, UpdateForumReadMarkerMessageComposer, UpdateForumReadMarkerEntry, UpdateMessageMessageEvent, UpdateThreadMessageComposer, UpdateThreadMessageEvent } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { LocalizeText, SendMessageComposer, GetUserProfile } from '../../../../api'; import { Button, Column, Flex, LayoutAvatarImageView, Text } from '../../../../common'; @@ -49,6 +49,15 @@ export const GroupForumThreadView: FC = props => { setMessages(prev => [ ...prev, ...parser.messages ]); } + + // Mark messages as read + if(parser.messages.length > 0) + { + const lastMessage = parser.messages[parser.messages.length - 1]; + SendMessageComposer(new UpdateForumReadMarkerMessageComposer( + new UpdateForumReadMarkerEntry(effectiveGroupId, lastMessage.messageId, true) + )); + } }); useMessageEvent(PostMessageMessageEvent, event => @@ -153,6 +162,12 @@ export const GroupForumThreadView: FC = props => onBack(); }, [ effectiveGroupId, threadId, onBack ]); + const deleteThread = useCallback(() => + { + SendMessageComposer(new ModerateThreadMessageComposer(effectiveGroupId, threadId, STATE_DELETED_BY_MODERATOR)); + onBack(); + }, [ effectiveGroupId, threadId, onBack ]); + const formatTimeAgo = (seconds: number): string => { if(seconds < 60) return `${ seconds }s ${ LocalizeText('messageboard.time.ago') }`; @@ -203,6 +218,9 @@ export const GroupForumThreadView: FC = props => + } @@ -292,6 +310,12 @@ export const GroupForumThreadView: FC = props => { LocalizeText('groupforum.thread.locked') } } + { !canPost && !isLocked && forumData && + + + { LocalizeText('groupforum.view.error.' + forumData.postMessagePermissionError) } + + } ); }; diff --git a/src/components/groups/views/tabs/GroupTabSettingsView.tsx b/src/components/groups/views/tabs/GroupTabSettingsView.tsx index be92446..d65567f 100644 --- a/src/components/groups/views/tabs/GroupTabSettingsView.tsx +++ b/src/components/groups/views/tabs/GroupTabSettingsView.tsx @@ -2,6 +2,7 @@ import { GroupSavePreferencesComposer } from '@nitrots/nitro-renderer'; import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; import { IGroupData, LocalizeText, SendMessageComposer } from '../../../../api'; import { Flex, HorizontalRule, Text } from '../../../../common'; +import { useNotification } from '../../../../hooks'; const STATES: string[] = [ 'regular', 'exclusive', 'private' ]; @@ -17,12 +18,30 @@ export const GroupTabSettingsView: FC = props => const { groupData = null, setGroupData = null, setCloseAction = null } = props; const [ groupState, setGroupState ] = useState(groupData.groupState); const [ groupDecorate, setGroupDecorate ] = useState(groupData.groupCanMembersDecorate); + const [ groupForum, setGroupForum ] = useState(groupData.groupHasForum ?? false); + const { showConfirm = null } = useNotification(); + + const handleForumToggle = useCallback(() => + { + if(groupForum) + { + // Disabling forum - show confirmation + showConfirm(LocalizeText('group.forum.disable.confirm'), () => + { + setGroupForum(false); + }, null); + } + else + { + setGroupForum(true); + } + }, [ groupForum, showConfirm ]); const saveSettings = useCallback(() => { if(!groupData) return false; - if((groupState === groupData.groupState) && (groupDecorate === groupData.groupCanMembersDecorate)) return true; + if((groupState === groupData.groupState) && (groupDecorate === groupData.groupCanMembersDecorate) && (groupForum === (groupData.groupHasForum ?? false))) return true; if(groupData.groupId <= 0) { @@ -32,6 +51,7 @@ export const GroupTabSettingsView: FC = props => newValue.groupState = groupState; newValue.groupCanMembersDecorate = groupDecorate; + newValue.groupHasForum = groupForum; return newValue; }); @@ -39,15 +59,16 @@ export const GroupTabSettingsView: FC = props => return true; } - SendMessageComposer(new GroupSavePreferencesComposer(groupData.groupId, groupState, groupDecorate ? 0 : 1)); + SendMessageComposer(new GroupSavePreferencesComposer(groupData.groupId, groupState, groupDecorate ? 0 : 1, groupForum)); return true; - }, [ groupData, groupState, groupDecorate, setGroupData ]); + }, [ groupData, groupState, groupDecorate, groupForum, setGroupData ]); useEffect(() => { setGroupState(groupData.groupState); setGroupDecorate(groupData.groupCanMembersDecorate); + setGroupForum(groupData.groupHasForum ?? false); }, [ groupData ]); useEffect(() => @@ -84,6 +105,14 @@ export const GroupTabSettingsView: FC = props => { LocalizeText('group.edit.settings.rights.members.help') } + +
+ +
+ { LocalizeText('group.forum.enable.caption') } + { LocalizeText('group.forum.enable.help') } +
+
); };