From da8b401279974c65d12f0e09873aad140286f123 Mon Sep 17 00:00:00 2001 From: Bill Date: Wed, 2 Mar 2022 18:56:50 -0500 Subject: [PATCH] Overhaul groups --- src/App.scss | 2 +- src/api/groups/GetGroupMembers.ts | 4 +- src/components/groups/GroupView.scss | 60 +++++- src/components/groups/GroupsContext.tsx | 12 +- .../groups/GroupsMessageHandler.tsx | 141 -------------- src/components/groups/GroupsView.tsx | 90 ++++----- src/components/groups/common/CompareId.ts | 8 + .../groups/common/IGroupCustomize.ts | 8 + src/components/groups/common/IGroupData.ts | 13 ++ .../groups/reducers/GroupsReducer.ts | 148 --------------- .../groups/views/GroupBadgeCreatorView.tsx | 57 ++---- .../groups/views/GroupCreatorView.tsx | 174 ++++++++++++++++++ .../GroupInformationStandaloneView.tsx | 20 +- .../GroupInformationView.tsx | 28 +-- .../groups/views/GroupManagerView.tsx | 125 +++++++++++++ .../views/{members => }/GroupMembersView.tsx | 134 ++++++++------ .../groups/views/GroupRoomInformationView.tsx | 149 +++++++++++++++ .../GroupCreatorTabConfirmationView.tsx | 55 ------ .../views/creator/GroupCreatorView.scss | 52 ------ .../groups/views/creator/GroupCreatorView.tsx | 138 -------------- .../views/manager/GroupManagerView.scss | 4 - .../groups/views/manager/GroupManagerView.tsx | 101 ---------- .../GroupRoomInformationView.scss | 13 -- .../GroupRoomInformationView.tsx | 129 ------------- .../groups/views/tabs/GroupSharedTabs.scss | 7 - .../groups/views/tabs/GroupTabBadgeView.tsx | 114 +++++++----- .../groups/views/tabs/GroupTabColorsView.tsx | 113 ++++++++---- .../tabs/GroupTabCreatorConfirmationView.tsx | 69 +++++++ .../views/tabs/GroupTabIdentityView.tsx | 134 +++++++++----- .../views/tabs/GroupTabSettingsView.tsx | 89 ++++++--- src/components/right-side/RightSideView.tsx | 2 +- .../views/GroupsContainerView.tsx | 2 +- 32 files changed, 1073 insertions(+), 1122 deletions(-) delete mode 100644 src/components/groups/GroupsMessageHandler.tsx create mode 100644 src/components/groups/common/CompareId.ts create mode 100644 src/components/groups/common/IGroupCustomize.ts create mode 100644 src/components/groups/common/IGroupData.ts delete mode 100644 src/components/groups/reducers/GroupsReducer.ts create mode 100644 src/components/groups/views/GroupCreatorView.tsx rename src/components/groups/views/{information-standalone => }/GroupInformationStandaloneView.tsx (67%) rename src/components/groups/views/{information => }/GroupInformationView.tsx (86%) create mode 100644 src/components/groups/views/GroupManagerView.tsx rename src/components/groups/views/{members => }/GroupMembersView.tsx (69%) create mode 100644 src/components/groups/views/GroupRoomInformationView.tsx delete mode 100644 src/components/groups/views/creator/GroupCreatorTabConfirmationView.tsx delete mode 100644 src/components/groups/views/creator/GroupCreatorView.scss delete mode 100644 src/components/groups/views/creator/GroupCreatorView.tsx delete mode 100644 src/components/groups/views/manager/GroupManagerView.scss delete mode 100644 src/components/groups/views/manager/GroupManagerView.tsx delete mode 100644 src/components/groups/views/room-information/GroupRoomInformationView.scss delete mode 100644 src/components/groups/views/room-information/GroupRoomInformationView.tsx delete mode 100644 src/components/groups/views/tabs/GroupSharedTabs.scss create mode 100644 src/components/groups/views/tabs/GroupTabCreatorConfirmationView.tsx diff --git a/src/App.scss b/src/App.scss index 26af7747..1699a227 100644 --- a/src/App.scss +++ b/src/App.scss @@ -72,7 +72,7 @@ $room-info-width: 325px; $nitro-group-creator-width: 383px; $nitro-mod-tools-width: 175px; -$nitro-group-manager-width: 375px; +$nitro-group-manager-width: 390px; $nitro-group-manager-height: 355px; $nitro-chooser-width: 200px; diff --git a/src/api/groups/GetGroupMembers.ts b/src/api/groups/GetGroupMembers.ts index b6b0e522..27fb6d57 100644 --- a/src/api/groups/GetGroupMembers.ts +++ b/src/api/groups/GetGroupMembers.ts @@ -2,6 +2,6 @@ import { CreateLinkEvent } from '..'; export function GetGroupMembers(groupId: number, levelId?: number): void { - if(!levelId) CreateLinkEvent(`groups/members/${groupId}`); - else CreateLinkEvent(`groups/members/${groupId}/${levelId}`); + if(!levelId) CreateLinkEvent(`group-members/${ groupId }`); + else CreateLinkEvent(`group-members/${ groupId }/${ levelId }`); } diff --git a/src/components/groups/GroupView.scss b/src/components/groups/GroupView.scss index b787fd43..d57b0d0e 100644 --- a/src/components/groups/GroupView.scss +++ b/src/components/groups/GroupView.scss @@ -131,7 +131,59 @@ height: 40px; } -@import './views/creator/GroupCreatorView'; -@import './views/manager/GroupManagerView'; -@import './views/room-information/GroupRoomInformationView'; -@import './views/tabs/GroupSharedTabs'; +.nitro-group-manager { + height: $nitro-group-manager-height; + width: $nitro-group-manager-width; +} + +.nitro-group-creator { + height: $nitro-group-manager-height; + width: $nitro-group-manager-width; + + .creator-tabs { + + .tab { + position: relative; + margin-left: -6px; + background-image: url('../../assets/images/groups/creator_tabs.png'); + background-repeat: no-repeat; + + &:first-child { + margin-left: 0; + } + + &.tab-blue-flat { + width: 84px; + height: 24px; + background-position: 0px 0px; + + &.active { + height: 28px; + background-position: 0px -24px; + } + } + + &.tab-blue-arrow { + width: 83px; + height: 24px; + background-position: 0px -52px; + + &.active { + height: 28px; + background-position: 0px -76px; + } + } + + &.tab-yellow { + width: 133px; + height: 28px; + background-position: 0px -104px; + + &.active { + height: 33px; + background-position: 0px -132px; + } + } + } + } +} diff --git a/src/components/groups/GroupsContext.tsx b/src/components/groups/GroupsContext.tsx index 19e20b8a..7a19e688 100644 --- a/src/components/groups/GroupsContext.tsx +++ b/src/components/groups/GroupsContext.tsx @@ -1,15 +1,15 @@ -import { createContext, Dispatch, FC, ProviderProps, useContext } from 'react'; -import { IGroupsAction, IGroupsState } from './reducers/GroupsReducer'; +import { createContext, Dispatch, FC, ProviderProps, SetStateAction, useContext } from 'react'; +import { IGroupCustomize } from './common/IGroupCustomize'; interface IGroupsContext { - groupsState: IGroupsState; - dispatchGroupsState: Dispatch; + groupCustomize: IGroupCustomize; + setGroupCustomize: Dispatch>; } const GroupsContext = createContext({ - groupsState: null, - dispatchGroupsState: null + groupCustomize: null, + setGroupCustomize: null }); export const GroupsContextProvider: FC> = props => diff --git a/src/components/groups/GroupsMessageHandler.tsx b/src/components/groups/GroupsMessageHandler.tsx deleted file mode 100644 index 5e730da9..00000000 --- a/src/components/groups/GroupsMessageHandler.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { GroupBadgePartsEvent, GroupBuyDataEvent, GroupSettingsEvent, RoomCreatedEvent } from '@nitrots/nitro-renderer'; -import { FC, useCallback } from 'react'; -import { CreateMessageHook } from '../../hooks'; -import { GroupBadgePart } from './common/GroupBadgePart'; -import { useGroupsContext } from './GroupsContext'; -import { GroupsActions } from './reducers/GroupsReducer'; - -function compareId(a, b) -{ - if( a.id < b.id ) return -1; - if( a.id > b.id ) return 1; - return 0; -} - -export const GroupsMessageHandler: FC<{}> = props => -{ - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { availableRooms = null } = groupsState; - - const onGroupBuyDataEvent = useCallback((event: GroupBuyDataEvent) => - { - const parser = event.getParser(); - - const rooms: { id: number, name: string }[] = []; - - parser.availableRooms.forEach((name, id) => - { - rooms.push({ id, name }); - }); - - dispatchGroupsState({ - type: GroupsActions.SET_PURHCASE_SETTINGS, - payload: { - objectValues: rooms, - numberValues: [ parser.groupCost ] - } - }); - }, [ dispatchGroupsState ]); - - - const onRoomCreatedEvent = useCallback((event: RoomCreatedEvent) => - { - const parser = event.getParser(); - - const clonedRooms = Array.from(availableRooms); - clonedRooms.push({ - id: parser.roomId, - name: parser.roomName - }); - - dispatchGroupsState({ - type: GroupsActions.SET_PURHCASE_SETTINGS, - payload: { - objectValues: clonedRooms - } - }); - }, [ availableRooms, dispatchGroupsState ]); - - const onGroupBadgePartsEvent = useCallback((event: GroupBadgePartsEvent) => - { - const parser = event.getParser(); - - const bases: { id: number, images: string[] }[] = []; - const symbols: { id: number, images: string[] }[] = []; - const partColors: { id: number, color: string }[] = []; - const colorsA: { id: number, color: string }[] = []; - const colorsB: { id: number, color: string }[] = []; - - parser.bases.forEach((images, id) => - { - bases.push({ id, images }); - }); - - parser.symbols.forEach((images, id) => - { - symbols.push({ id, images }); - }); - - parser.partColors.forEach((color, id) => - { - partColors.push({ id, color }); - }); - - parser.colorsA.forEach((color, id) => - { - colorsA.push({ id, color }); - }); - - parser.colorsB.forEach((color, id) => - { - colorsB.push({ id, color }); - }); - - bases.sort(compareId); - symbols.sort(compareId); - partColors.sort(compareId); - colorsA.sort(compareId); - colorsB.sort(compareId); - - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_BADGE_PARTS_CONFIG, - payload: { - objectValues: [ bases, symbols, partColors, colorsA, colorsB ] - } - }); - }, [ dispatchGroupsState ]); - - const onGroupSettingsEvent = useCallback((event: GroupSettingsEvent) => - { - const parser = event.getParser(); - - const groupBadgeParts: GroupBadgePart[] = []; - - parser.badgeParts.forEach((part, id) => - { - groupBadgeParts.push(new GroupBadgePart( - part.isBase ? GroupBadgePart.BASE : GroupBadgePart.SYMBOL, - part.key, - part.color, - part.position - )); - }); - - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_SETTINGS, - payload: { - stringValues: [ parser.title, parser.description ], - numberValues: [ parser.id, parser.state, parser.colorA, parser.colorB ], - boolValues: [ parser.canMembersDecorate ], - objectValues: groupBadgeParts - } - }); - }, [ dispatchGroupsState ]); - - CreateMessageHook(GroupBuyDataEvent, onGroupBuyDataEvent); - CreateMessageHook(RoomCreatedEvent, onRoomCreatedEvent); - CreateMessageHook(GroupBadgePartsEvent, onGroupBadgePartsEvent); - CreateMessageHook(GroupSettingsEvent, onGroupSettingsEvent); - - return null; -}; diff --git a/src/components/groups/GroupsView.tsx b/src/components/groups/GroupsView.tsx index ac436811..eb0e2091 100644 --- a/src/components/groups/GroupsView.tsx +++ b/src/components/groups/GroupsView.tsx @@ -1,31 +1,19 @@ -import { GroupBadgePartsComposer, GroupPurchasedEvent, GroupSettingsComposer, ILinkEventTracker } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useEffect, useReducer, useState } from 'react'; +import { GroupBadgePartsComposer, GroupBadgePartsEvent, GroupPurchasedEvent, GroupSettingsComposer, ILinkEventTracker } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useState } from 'react'; import { AddEventLinkTracker, RemoveLinkEventTracker, TryVisitRoom } from '../../api'; -import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../hooks'; +import { CreateMessageHook, SendMessageHook } from '../../hooks'; +import { CompareId } from './common/CompareId'; +import { IGroupCustomize } from './common/IGroupCustomize'; import { GroupsContextProvider } from './GroupsContext'; -import { GroupsMessageHandler } from './GroupsMessageHandler'; -import { GroupsReducer, initialGroups } from './reducers/GroupsReducer'; -import { GroupCreatorView } from './views/creator/GroupCreatorView'; -import { GroupInformationStandaloneView } from './views/information-standalone/GroupInformationStandaloneView'; -import { GroupManagerView } from './views/manager/GroupManagerView'; -import { GroupMembersView } from './views/members/GroupMembersView'; +import { GroupCreatorView } from './views/GroupCreatorView'; +import { GroupInformationStandaloneView } from './views/GroupInformationStandaloneView'; +import { GroupManagerView } from './views/GroupManagerView'; +import { GroupMembersView } from './views/GroupMembersView'; export const GroupsView: FC<{}> = props => { - const [ currentGroupId, setCurrentGroupId ] = useState(null); - const [ currentGroupLevelId, setCurrentGroupLevelId ] = useState(null); - const [ isMembersVisible, setMembersVisible ] = useState(false); const [ isCreatorVisible, setCreatorVisible ] = useState(false); - const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups); - - const closeMembers = () => - { - BatchUpdates(() => - { - setCurrentGroupId(null); - setCurrentGroupLevelId(null); - }); - } + const [ groupCustomize, setGroupCustomize ] = useState(null); const onGroupPurchasedEvent = useCallback((event: GroupPurchasedEvent) => { @@ -37,6 +25,35 @@ export const GroupsView: FC<{}> = props => CreateMessageHook(GroupPurchasedEvent, onGroupPurchasedEvent); + const onGroupBadgePartsEvent = useCallback((event: GroupBadgePartsEvent) => + { + const parser = event.getParser(); + + const customize: IGroupCustomize = { + badgeBases: [], + badgeSymbols: [], + badgePartColors: [], + groupColorsA: [], + groupColorsB: [] + }; + + parser.bases.forEach((images, id) => customize.badgeBases.push({ id, images })); + parser.symbols.forEach((images, id) => customize.badgeSymbols.push({ id, images })); + parser.partColors.forEach((color, id) => customize.badgePartColors.push({ id, color })); + parser.colorsA.forEach((color, id) => customize.groupColorsA.push({ id, color })); + parser.colorsB.forEach((color, id) => customize.groupColorsB.push({ id, color })); + + customize.badgeBases.sort(CompareId); + customize.badgeSymbols.sort(CompareId); + customize.badgePartColors.sort(CompareId); + customize.groupColorsA.sort(CompareId); + customize.groupColorsB.sort(CompareId); + + setGroupCustomize(customize); + }, [ setGroupCustomize ]); + + CreateMessageHook(GroupBadgePartsEvent, onGroupBadgePartsEvent); + const linkReceived = useCallback((url: string) => { const parts = url.split('/'); @@ -50,22 +67,9 @@ export const GroupsView: FC<{}> = props => return; case 'manage': if(!parts[2]) return; - + + setCreatorVisible(false); SendMessageHook(new GroupSettingsComposer(Number(parts[2]))); - return; - case 'members': - if(!parts[2]) return; - - const groupId = (parseInt(parts[2]) || -1); - const levelId = (parseInt(parts[3]) || 3); - - BatchUpdates(() => - { - setCurrentGroupId(groupId); - setCurrentGroupLevelId(levelId); - setMembersVisible(true); - }); - return; } }, []); @@ -88,13 +92,13 @@ export const GroupsView: FC<{}> = props => }, []); return ( - - +
- setCreatorVisible(false) } /> - - { isMembersVisible && - } + { isCreatorVisible && + setCreatorVisible(false) } /> } + { !isCreatorVisible && + } +
diff --git a/src/components/groups/common/CompareId.ts b/src/components/groups/common/CompareId.ts new file mode 100644 index 00000000..c6fdda40 --- /dev/null +++ b/src/components/groups/common/CompareId.ts @@ -0,0 +1,8 @@ +export const CompareId = (a, b) => +{ + if(a.id < b.id) return -1; + + if(a.id > b.id) return 1; + + return 0; +} diff --git a/src/components/groups/common/IGroupCustomize.ts b/src/components/groups/common/IGroupCustomize.ts new file mode 100644 index 00000000..44fc4ff3 --- /dev/null +++ b/src/components/groups/common/IGroupCustomize.ts @@ -0,0 +1,8 @@ +export interface IGroupCustomize +{ + badgeBases: { id: number, images: string[] }[]; + badgeSymbols: { id: number, images: string[] }[]; + badgePartColors: { id: number, color: string }[]; + groupColorsA: { id: number, color: string }[]; + groupColorsB: { id: number, color: string }[]; +} diff --git a/src/components/groups/common/IGroupData.ts b/src/components/groups/common/IGroupData.ts new file mode 100644 index 00000000..bb65b491 --- /dev/null +++ b/src/components/groups/common/IGroupData.ts @@ -0,0 +1,13 @@ +import { GroupBadgePart } from './GroupBadgePart'; + +export interface IGroupData +{ + groupId: number; + groupName: string; + groupDescription: string; + groupHomeroomId: number; + groupState: number; + groupCanMembersDecorate: boolean; + groupColors: number[]; + groupBadgeParts: GroupBadgePart[]; +} diff --git a/src/components/groups/reducers/GroupsReducer.ts b/src/components/groups/reducers/GroupsReducer.ts deleted file mode 100644 index 1889bf37..00000000 --- a/src/components/groups/reducers/GroupsReducer.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { Reducer } from 'react'; -import { GroupBadgePart } from '../common/GroupBadgePart'; - -export interface IGroupsState -{ - availableRooms: { id: number, name: string }[]; - purchaseCost: number; - badgeBases: { id: number, images: string[] }[]; - badgeSymbols: { id: number, images: string[] }[]; - badgePartColors: { id: number, color: string }[]; - groupId: number; - groupColorsA: { id: number, color: string }[]; - groupColorsB: { id: number, color: string }[]; - groupName: string; - groupDescription: string; - groupHomeroomId: number; - groupBadgeParts: GroupBadgePart[]; - groupColors: number[]; - groupState: number; - groupCanMembersDecorate: boolean; -} - -export interface IGroupsAction -{ - type: string; - payload?: { - objectValues?: any[]; - stringValues?: string[]; - numberValues?: number[]; - boolValues?: boolean[]; - badgeParts?: GroupBadgePart[]; - } -} - -export class GroupsActions -{ - public static SET_PURHCASE_SETTINGS: string = 'GA_SET_PURHCASE_SETTINGS'; - public static SET_GROUP_BADGE_PARTS_CONFIG: string = 'GA_SET_GROUP_BADGE_PARTS_CONFIG'; - public static SET_GROUP_NAME: string = 'GA_SET_GROUP_NAME'; - public static SET_GROUP_DESCRIPTION: string = 'GA_SET_GROUP_DESCRIPTION'; - public static SET_GROUP_HOMEROOM_ID: string = 'GA_SET_GROUP_HOMEROOM_ID'; - public static SET_GROUP_BADGE_PARTS: string = 'GA_SET_BADGE_PARTS'; - public static SET_GROUP_COLORS: string = 'GA_SET_GROUP_COLORS'; - public static SET_GROUP_STATE: string = 'GA_SET_GROUP_STATE'; - public static SET_GROUP_CAN_MEMBERS_DECORATE: string = 'GA_SET_GROUP_CAN_MEMBERS_DECORATE'; - public static SET_GROUP_SETTINGS: string = 'GA_SET_GROUP_SETTINGS'; - public static RESET_GROUP_SETTINGS: string = 'GA_RESET_GROUP_SETTINGS'; -} - -export const initialGroups: IGroupsState = { - availableRooms: null, - purchaseCost: null, - badgeBases: null, - badgeSymbols: null, - badgePartColors: null, - groupId: null, - groupColorsA: null, - groupColorsB: null, - groupName: '', - groupDescription: '', - groupHomeroomId: 0, - groupBadgeParts: null, - groupColors: null, - groupState: null, - groupCanMembersDecorate: null -}; - -export const GroupsReducer: Reducer = (state, action) => -{ - switch(action.type) - { - case GroupsActions.SET_PURHCASE_SETTINGS: { - const availableRooms = action.payload.objectValues; - const purchaseCost = (action.payload.numberValues[0] || state.purchaseCost || 0); - - return { ...state, availableRooms, purchaseCost }; - } - case GroupsActions.SET_GROUP_BADGE_PARTS_CONFIG: { - const badgeBases = (action.payload.objectValues[0] || state.badgeBases || null); - const badgeSymbols = (action.payload.objectValues[1] || state.badgeSymbols || null); - const badgePartColors = (action.payload.objectValues[2] || state.badgePartColors || null); - const groupColorsA = (action.payload.objectValues[3] || state.groupColorsA || null); - const groupColorsB = (action.payload.objectValues[4] || state.groupColorsB || null); - - return { ...state, badgeBases, badgeSymbols, badgePartColors, groupColorsA, groupColorsB }; - } - case GroupsActions.SET_GROUP_NAME: { - const groupName = action.payload.stringValues[0]; - - return { ...state, groupName }; - } - case GroupsActions.SET_GROUP_DESCRIPTION: { - const groupDescription = action.payload.stringValues[0]; - - return { ...state, groupDescription }; - } - case GroupsActions.SET_GROUP_HOMEROOM_ID: { - const groupHomeroomId = action.payload.numberValues[0]; - - return { ...state, groupHomeroomId }; - } - case GroupsActions.SET_GROUP_BADGE_PARTS: { - const groupBadgeParts = action.payload.badgeParts; - - return { ...state, groupBadgeParts }; - } - case GroupsActions.SET_GROUP_COLORS: { - const groupColors = action.payload.objectValues; - - return { ...state, groupColors }; - } - case GroupsActions.SET_GROUP_STATE: { - const groupState = action.payload.numberValues[0]; - - return { ...state, groupState }; - } - case GroupsActions.SET_GROUP_CAN_MEMBERS_DECORATE: { - const groupCanMembersDecorate = action.payload.boolValues[0]; - - return { ...state, groupCanMembersDecorate }; - } - case GroupsActions.SET_GROUP_SETTINGS: { - const groupId = action.payload.numberValues[0]; - const groupName = action.payload.stringValues[0]; - const groupDescription = action.payload.stringValues[1]; - const groupBadgeParts = action.payload.objectValues; - const groupState = action.payload.numberValues[1]; - const groupColors = action.payload.numberValues.slice(2); - const groupCanMembersDecorate = action.payload.boolValues[0]; - - return { ...state, groupId, groupName, groupDescription, groupBadgeParts, groupColors, groupState, groupCanMembersDecorate }; - } - case GroupsActions.RESET_GROUP_SETTINGS: { - const groupId = null; - const groupName = ''; - const groupDescription = ''; - const groupHomeroomId = 0; - const groupBadgeParts = null; - const groupColors = null; - const groupState = null; - const groupCanMembersDecorate = null; - - return { ...state, groupId, groupName, groupDescription, groupHomeroomId, groupBadgeParts, groupColors, groupState, groupCanMembersDecorate }; - } - default: - return state; - } -} diff --git a/src/components/groups/views/GroupBadgeCreatorView.tsx b/src/components/groups/views/GroupBadgeCreatorView.tsx index acc6a22f..5f231d06 100644 --- a/src/components/groups/views/GroupBadgeCreatorView.tsx +++ b/src/components/groups/views/GroupBadgeCreatorView.tsx @@ -1,7 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Dispatch, FC, SetStateAction, useEffect, useRef, useState } from 'react'; +import { Dispatch, FC, SetStateAction, useState } from 'react'; import { Base, Column, Flex, Grid } from '../../../common'; -import { BatchUpdates } from '../../../hooks'; import { BadgeImageView } from '../../../views/shared/badge-image/BadgeImageView'; import { GroupBadgePart } from '../common/GroupBadgePart'; import { useGroupsContext } from '../GroupsContext'; @@ -18,53 +17,31 @@ export const GroupBadgeCreatorView: FC = props => { const { badgeParts = [], setBadgeParts = null } = props; const [ selectedIndex, setSelectedIndex ] = useState(-1); - const [ copiedBadgeParts, setCopiedBadgeParts ] = useState(null); - const { groupsState = null } = useGroupsContext(); - const { badgeBases = null, badgeSymbols = null, badgePartColors = null } = groupsState; - const willUnmount = useRef(false); + const { groupCustomize = null } = useGroupsContext(); const setPartProperty = (partIndex: number, property: string, value: number) => { - const newBadgeParts = [ ...copiedBadgeParts ]; + const newBadgeParts = [ ...badgeParts ]; newBadgeParts[partIndex][property] = value; - BatchUpdates(() => - { - setCopiedBadgeParts(newBadgeParts); - - if(property === 'key') setSelectedIndex(-1); - }); + setBadgeParts(newBadgeParts); + + if(property === 'key') setSelectedIndex(-1); } - useEffect(() => - { - BatchUpdates(() => - { - setCopiedBadgeParts(badgeParts); - setSelectedIndex(-1); - }); - }, [ badgeParts ]); - - useEffect(() => - { - if(!copiedBadgeParts || (copiedBadgeParts === badgeParts)) return; - - setBadgeParts(copiedBadgeParts); - }, [ copiedBadgeParts, badgeParts, setBadgeParts ]); - - if(!copiedBadgeParts || !copiedBadgeParts.length) return null; + if(!badgeParts || !badgeParts.length) return null; return ( <> - { ((selectedIndex < 0) && copiedBadgeParts && (copiedBadgeParts.length > 0)) && copiedBadgeParts.map((part, index) => + { ((selectedIndex < 0) && badgeParts && (badgeParts.length > 0)) && badgeParts.map((part, index) => { return ( setSelectedIndex(index) }> - { (copiedBadgeParts[index].code && (copiedBadgeParts[index].code.length > 0)) && - } - { (!copiedBadgeParts[index].code || !copiedBadgeParts[index].code.length) && + { (badgeParts[index].code && (badgeParts[index].code.length > 0)) && + } + { (!badgeParts[index].code || !badgeParts[index].code.length) && } @@ -73,13 +50,13 @@ export const GroupBadgeCreatorView: FC = props => { POSITIONS.map((position, posIndex) => { - return setPartProperty(index, 'position', position) }> + return setPartProperty(index, 'position', position) }> }) } } - { (badgePartColors.length > 0) && badgePartColors.map((item, colorIndex) => + { (groupCustomize.badgePartColors.length > 0) && groupCustomize.badgePartColors.map((item, colorIndex) => { - return setPartProperty(index, 'color', (colorIndex + 1)) }> + return setPartProperty(index, 'color', (colorIndex + 1)) }> }) } @@ -87,17 +64,17 @@ export const GroupBadgeCreatorView: FC = props => }) } { (selectedIndex >= 0) && - { (copiedBadgeParts[selectedIndex].type === GroupBadgePart.SYMBOL) && + { (badgeParts[selectedIndex].type === GroupBadgePart.SYMBOL) && setPartProperty(selectedIndex, 'key', 0) }> } - { ((copiedBadgeParts[selectedIndex].type === GroupBadgePart.BASE) ? badgeBases : badgeSymbols).map((item, index) => + { ((badgeParts[selectedIndex].type === GroupBadgePart.BASE) ? groupCustomize.badgeBases : groupCustomize.badgeSymbols).map((item, index) => { return ( setPartProperty(selectedIndex, 'key', item.id) }> - + ); }) } diff --git a/src/components/groups/views/GroupCreatorView.tsx b/src/components/groups/views/GroupCreatorView.tsx new file mode 100644 index 00000000..8c7706e6 --- /dev/null +++ b/src/components/groups/views/GroupCreatorView.tsx @@ -0,0 +1,174 @@ +import { GroupBuyComposer, GroupBuyDataComposer, GroupBuyDataEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useState } from 'react'; +import { HasHabboClub, LocalizeText } from '../../../api'; +import { Base, Button, Column, Flex, Text } from '../../../common'; +import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../layout'; +import { IGroupData } from '../common/IGroupData'; +import { GroupTabBadgeView } from './tabs/GroupTabBadgeView'; +import { GroupTabColorsView } from './tabs/GroupTabColorsView'; +import { GroupTabCreatorConfirmationView } from './tabs/GroupTabCreatorConfirmationView'; +import { GroupTabIdentityView } from './tabs/GroupTabIdentityView'; + +interface GroupCreatorViewProps +{ + onClose: () => void; +} + +const TABS: number[] = [ 1, 2, 3, 4 ]; + +export const GroupCreatorView: FC = props => +{ + const { onClose = null } = props; + const [ currentTab, setCurrentTab ] = useState(1); + const [ closeAction, setCloseAction ] = useState<{ action: () => boolean }>(null); + const [ groupData, setGroupData ] = useState(null); + const [ availableRooms, setAvailableRooms ] = useState<{ id: number, name: string }[]>(null); + const [ purchaseCost, setPurchaseCost ] = useState(0); + + const close = () => + { + setCloseAction(null); + setGroupData(null); + + if(onClose) onClose(); + } + + const buyGroup = () => + { + if(!groupData) return; + + const badge = []; + + groupData.groupBadgeParts.forEach(part => + { + if(part.code) + { + badge.push(part.key); + badge.push(part.color); + badge.push(part.position); + } + }); + + SendMessageHook(new GroupBuyComposer(groupData.groupName, groupData.groupDescription, groupData.groupHomeroomId, groupData.groupColors[0], groupData.groupColors[1], badge)); + } + + const previousStep = () => + { + if(closeAction && closeAction.action) + { + if(!closeAction.action()) return; + } + + if(currentTab === 1) + { + onClose(); + + return; + } + + setCurrentTab(value => value - 1); + } + + const nextStep = () => + { + if(closeAction && closeAction.action) + { + if(!closeAction.action()) return; + } + + if(currentTab === 4) + { + buyGroup(); + + return; + } + + setCurrentTab(value => (value === 4 ? value : value + 1)); + } + + const onGroupBuyDataEvent = useCallback((event: GroupBuyDataEvent) => + { + const parser = event.getParser(); + + const rooms: { id: number, name: string }[] = []; + + parser.availableRooms.forEach((name, id) => rooms.push({ id, name })); + + BatchUpdates(() => + { + setAvailableRooms(rooms); + setPurchaseCost(parser.groupCost); + }); + }, []); + + CreateMessageHook(GroupBuyDataEvent, onGroupBuyDataEvent); + + useEffect(() => + { + BatchUpdates(() => + { + setCurrentTab(1); + + setGroupData({ + groupId: -1, + groupName: null, + groupDescription: null, + groupHomeroomId: -1, + groupState: 1, + groupCanMembersDecorate: true, + groupColors: null, + groupBadgeParts: null + }); + }); + + SendMessageHook(new GroupBuyDataComposer()); + }, [ setGroupData ]); + + if(!groupData) return null; + + return ( + + + + + { TABS.map((tab, index) => + { + return ( + + { LocalizeText(`group.create.steplabel.${ tab }`) } + + ); + }) } + + + + + + { LocalizeText(`group.create.stepcaption.${ currentTab }`) } + { LocalizeText(`group.create.stepdesc.${ currentTab }`) } + + + + { (currentTab === 1) && + } + { (currentTab === 2) && + } + { (currentTab === 3) && + } + { (currentTab === 4) && + } + + + + + + + + + ); +}; diff --git a/src/components/groups/views/information-standalone/GroupInformationStandaloneView.tsx b/src/components/groups/views/GroupInformationStandaloneView.tsx similarity index 67% rename from src/components/groups/views/information-standalone/GroupInformationStandaloneView.tsx rename to src/components/groups/views/GroupInformationStandaloneView.tsx index f1e67980..2ca1ce66 100644 --- a/src/components/groups/views/information-standalone/GroupInformationStandaloneView.tsx +++ b/src/components/groups/views/GroupInformationStandaloneView.tsx @@ -1,9 +1,9 @@ import { GroupInformationEvent, GroupInformationParser } from '@nitrots/nitro-renderer'; import { FC, useCallback, useState } from 'react'; -import { LocalizeText } from '../../../../api'; -import { BatchUpdates, CreateMessageHook } from '../../../../hooks'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; -import { GroupInformationView } from '../information/GroupInformationView'; +import { LocalizeText } from '../../../api'; +import { CreateMessageHook } from '../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../layout'; +import { GroupInformationView } from './GroupInformationView'; export const GroupInformationStandaloneView: FC<{}> = props => { @@ -13,14 +13,8 @@ export const GroupInformationStandaloneView: FC<{}> = props => { const parser = event.getParser(); - if(!parser.flag) return; - - BatchUpdates(() => - { - setGroupInformation(null); - setGroupInformation(parser); - }); - }, []); + if((groupInformation && (groupInformation.id === parser.id)) || parser.flag) setGroupInformation(parser); + }, [ groupInformation ]); CreateMessageHook(GroupInformationEvent, onGroupInformationEvent); @@ -28,7 +22,7 @@ export const GroupInformationStandaloneView: FC<{}> = props => return ( - setGroupInformation(null) } /> + setGroupInformation(null) } /> setGroupInformation(null) } /> diff --git a/src/components/groups/views/information/GroupInformationView.tsx b/src/components/groups/views/GroupInformationView.tsx similarity index 86% rename from src/components/groups/views/information/GroupInformationView.tsx rename to src/components/groups/views/GroupInformationView.tsx index 1cd8547c..757acdb4 100644 --- a/src/components/groups/views/information/GroupInformationView.tsx +++ b/src/components/groups/views/GroupInformationView.tsx @@ -1,16 +1,18 @@ import { GroupInformationParser, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback } from 'react'; -import { CreateLinkEvent, GetSessionDataManager, LocalizeText, TryVisitRoom } from '../../../../api'; -import { GetGroupManager } from '../../../../api/groups/GetGroupManager'; -import { GetGroupMembers } from '../../../../api/groups/GetGroupMembers'; -import { TryJoinGroup } from '../../../../api/groups/TryJoinGroup'; -import { Button, Column, Flex, Grid, Text } from '../../../../common'; -import { SendMessageHook } from '../../../../hooks'; -import { NotificationUtilities } from '../../../../views/notification-center/common/NotificationUtilities'; -import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView'; -import { CatalogPageName } from '../../../catalog/common/CatalogPageName'; -import { GroupMembershipType } from '../../common/GroupMembershipType'; -import { GroupType } from '../../common/GroupType'; +import { CreateLinkEvent, GetSessionDataManager, LocalizeText, TryVisitRoom } from '../../../api'; +import { GetGroupManager } from '../../../api/groups/GetGroupManager'; +import { GetGroupMembers } from '../../../api/groups/GetGroupMembers'; +import { TryJoinGroup } from '../../../api/groups/TryJoinGroup'; +import { Button, Column, Flex, Grid, Text } from '../../../common'; +import { SendMessageHook } from '../../../hooks'; +import { NotificationUtilities } from '../../../views/notification-center/common/NotificationUtilities'; +import { BadgeImageView } from '../../../views/shared/badge-image/BadgeImageView'; +import { CatalogPageName } from '../../catalog/common/CatalogPageName'; +import { GroupMembershipType } from '../common/GroupMembershipType'; +import { GroupType } from '../common/GroupType'; + +const STATES: string[] = [ 'regular', 'exclusive', 'private' ]; interface GroupInformationViewProps { @@ -125,9 +127,9 @@ export const GroupInformationView: FC = props => { groupInformation.title } - + { groupInformation.canMembersDecorate && - } + } { LocalizeText('group.created', ['date', 'owner'], [groupInformation.createdAt, groupInformation.ownerName]) } diff --git a/src/components/groups/views/GroupManagerView.tsx b/src/components/groups/views/GroupManagerView.tsx new file mode 100644 index 00000000..28d532e7 --- /dev/null +++ b/src/components/groups/views/GroupManagerView.tsx @@ -0,0 +1,125 @@ +import { GroupBadgePart, GroupInformationEvent, GroupSettingsEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { LocalizeText } from '../../../api'; +import { Base, Column, Flex, Text } from '../../../common'; +import { CreateMessageHook } from '../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../layout'; +import { IGroupData } from '../common/IGroupData'; +import { GroupTabBadgeView } from './tabs/GroupTabBadgeView'; +import { GroupTabColorsView } from './tabs/GroupTabColorsView'; +import { GroupTabIdentityView } from './tabs/GroupTabIdentityView'; +import { GroupTabSettingsView } from './tabs/GroupTabSettingsView'; + +const TABS: number[] = [ 1, 2, 3, 5 ]; + +export const GroupManagerView: FC<{}> = props => +{ + const [ currentTab, setCurrentTab ] = useState(1); + const [ closeAction, setCloseAction ] = useState<{ action: () => boolean }>(null); + const [ groupData, setGroupData ] = useState(null); + + const close = () => + { + setCloseAction(prevValue => + { + if(prevValue && prevValue.action) prevValue.action(); + + return null; + }); + + setGroupData(null); + } + + const changeTab = (tab: number) => + { + if(closeAction && closeAction.action) closeAction.action(); + + setCurrentTab(tab); + } + + const onGroupInformationEvent = useCallback((event: GroupInformationEvent) => + { + const parser = event.getParser(); + + if(!groupData || (groupData.groupId !== parser.id)) return; + + setGroupData(prevValue => + { + const newValue = { ...prevValue }; + + newValue.groupName = parser.title; + newValue.groupDescription = parser.description; + newValue.groupState = parser.type; + newValue.groupCanMembersDecorate = parser.canMembersDecorate; + + return newValue; + }); + }, [ groupData ]); + + CreateMessageHook(GroupInformationEvent, onGroupInformationEvent); + + const onGroupSettingsEvent = useCallback((event: GroupSettingsEvent) => + { + const parser = event.getParser(); + + const groupBadgeParts: GroupBadgePart[] = []; + + parser.badgeParts.forEach((part, id) => + { + groupBadgeParts.push(new GroupBadgePart( + part.isBase ? GroupBadgePart.BASE : GroupBadgePart.SYMBOL, + part.key, + part.color, + part.position + )); + }); + + setGroupData({ + groupId: parser.id, + groupName: parser.title, + groupDescription: parser.description, + groupHomeroomId: parser.roomId, + groupState: parser.state, + groupCanMembersDecorate: parser.canMembersDecorate, + groupColors: [ parser.colorA, parser.colorB ], + groupBadgeParts + }); + }, [ setGroupData ]); + + CreateMessageHook(GroupSettingsEvent, onGroupSettingsEvent); + + if(!groupData || (groupData.groupId <= 0)) return null; + + return ( + + + + { TABS.map(tab => + { + return ( changeTab(tab) }> + { LocalizeText(`group.edit.tab.${tab}`) } + ); + }) } + + + + + + { LocalizeText(`group.edit.tabcaption.${ currentTab }`) } + { LocalizeText(`group.edit.tabdesc.${ currentTab }`) } + + + + { currentTab === 1 && + } + { currentTab === 2 && + } + { currentTab === 3 && + } + { currentTab === 5 && + } + + + + ); +}; diff --git a/src/components/groups/views/members/GroupMembersView.tsx b/src/components/groups/views/GroupMembersView.tsx similarity index 69% rename from src/components/groups/views/members/GroupMembersView.tsx rename to src/components/groups/views/GroupMembersView.tsx index 7de661be..a4064cdc 100644 --- a/src/components/groups/views/members/GroupMembersView.tsx +++ b/src/components/groups/views/GroupMembersView.tsx @@ -1,39 +1,25 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { GroupAdminGiveComposer, GroupAdminTakeComposer, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembersParser, GroupRank, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer'; +import { GroupAdminGiveComposer, GroupAdminTakeComposer, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembersParser, GroupRank, GroupRemoveMemberComposer, ILinkEventTracker } from '@nitrots/nitro-renderer'; import classNames from 'classnames'; -import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; -import { GetSessionDataManager, GetUserProfile, LocalizeText } from '../../../../api'; -import { Base, Button, Column, Flex, Grid, Text } from '../../../../common'; -import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../hooks'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; -import { NotificationUtilities } from '../../../../views/notification-center/common/NotificationUtilities'; -import { AvatarImageView } from '../../../../views/shared/avatar-image/AvatarImageView'; -import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView'; +import { FC, useCallback, useEffect, useState } from 'react'; +import { AddEventLinkTracker, GetSessionDataManager, GetUserProfile, LocalizeText, RemoveLinkEventTracker } from '../../../api'; +import { Base, Button, Column, Flex, Grid, Text } from '../../../common'; +import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../layout'; +import { NotificationUtilities } from '../../../views/notification-center/common/NotificationUtilities'; +import { AvatarImageView } from '../../../views/shared/avatar-image/AvatarImageView'; +import { BadgeImageView } from '../../../views/shared/badge-image/BadgeImageView'; -interface GroupMembersViewProps +export const GroupMembersView: FC<{}> = props => { - groupId: number; - levelId: number; - setLevelId: Dispatch>; - onClose: () => void; -} - -export const GroupMembersView: FC = props => -{ - const { groupId = -1, levelId = -1, setLevelId = null, onClose = null } = props; - const [ pageData, setPageData ] = useState(null); + const [ groupId, setGroupId ] = useState(-1); + const [ levelId, setLevelId ] = useState(-1); + const [ membersData, setMembersData ] = useState(null); const [ pageId, setPageId ] = useState(-1); - const [ searchQuery, setSearchQuery ] = useState(''); const [ totalPages, setTotalPages ] = useState(0); + const [ searchQuery, setSearchQuery ] = useState(''); const [ removingMemberName, setRemovingMemberName ] = useState(null); - const refreshMembers = useCallback(() => - { - if((groupId === -1) || (levelId === -1) || (pageId === -1)) return; - - SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, levelId)); - }, [ groupId, levelId, pageId, searchQuery ]); - const previousPage = () => setPageId(prevValue => (prevValue - 1)); const nextPage = () => setPageId(prevValue => (prevValue + 1)); @@ -42,7 +28,7 @@ export const GroupMembersView: FC = props => { if(member.rank === GroupRank.OWNER) return 'group.members.owner'; - if(pageData.admin) + if(membersData.admin) { if(member.rank === GroupRank.ADMIN) return 'group.members.removerights'; @@ -52,12 +38,19 @@ export const GroupMembersView: FC = props => return ''; } + const refreshMembers = useCallback(() => + { + if((groupId === -1) || (levelId === -1) || (pageId === -1)) return; + + SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, levelId)); + }, [ groupId, levelId, pageId, searchQuery ]); + const toggleAdmin = (member: GroupMemberParser) => { if(member.rank === GroupRank.OWNER) return; - if(member.rank !== GroupRank.ADMIN) SendMessageHook(new GroupAdminGiveComposer(pageData.groupId, member.id)); - else SendMessageHook(new GroupAdminTakeComposer(pageData.groupId, member.id)); + if(member.rank !== GroupRank.ADMIN) SendMessageHook(new GroupAdminGiveComposer(membersData.groupId, member.id)); + else SendMessageHook(new GroupAdminTakeComposer(membersData.groupId, member.id)); refreshMembers(); } @@ -66,7 +59,7 @@ export const GroupMembersView: FC = props => { if(member.rank !== GroupRank.REQUESTED) return; - SendMessageHook(new GroupMembershipAcceptComposer(pageData.groupId, member.id)); + SendMessageHook(new GroupMembershipAcceptComposer(membersData.groupId, member.id)); refreshMembers(); } @@ -75,7 +68,7 @@ export const GroupMembersView: FC = props => { if(member.rank === GroupRank.REQUESTED) { - SendMessageHook(new GroupMembershipDeclineComposer(pageData.groupId, member.id)); + SendMessageHook(new GroupMembershipDeclineComposer(membersData.groupId, member.id)); refreshMembers(); @@ -83,7 +76,7 @@ export const GroupMembersView: FC = props => } setRemovingMemberName(member.name); - SendMessageHook(new GroupConfirmRemoveMemberComposer(pageData.groupId, member.id)); + SendMessageHook(new GroupConfirmRemoveMemberComposer(membersData.groupId, member.id)); } const onGroupMembersEvent = useCallback((event: GroupMembersEvent) => @@ -92,8 +85,8 @@ export const GroupMembersView: FC = props => BatchUpdates(() => { - setPageData(parser); - //setSearchLevelId(parser.level); + setMembersData(parser); + setLevelId(parser.level); setTotalPages(Math.ceil(parser.totalMembersCount / parser.pageSize)); }); }, []); @@ -106,26 +99,49 @@ export const GroupMembersView: FC = props => NotificationUtilities.confirm(LocalizeText(((parser.furnitureCount > 0) ? 'group.kickconfirm.desc' : 'group.kickconfirm_nofurni.desc'), [ 'user', 'amount' ], [ removingMemberName, parser.furnitureCount.toString() ]), () => { - SendMessageHook(new GroupRemoveMemberComposer(pageData.groupId, parser.userId)); + SendMessageHook(new GroupRemoveMemberComposer(membersData.groupId, parser.userId)); refreshMembers(); }, null); setRemovingMemberName(null); - }, [ pageData, removingMemberName, refreshMembers ]); + }, [ membersData, removingMemberName, refreshMembers ]); CreateMessageHook(GroupConfirmMemberRemoveEvent, onGroupConfirmMemberRemoveEvent); + const linkReceived = useCallback((url: string) => + { + const parts = url.split('/'); + + if(parts.length < 2) return; + + const groupId = (parseInt(parts[1]) || -1); + const levelId = (parseInt(parts[2]) || 3); + + BatchUpdates(() => + { + setGroupId(groupId); + setLevelId(levelId); + }); + }, []); + + useEffect(() => + { + const linkTracker: ILinkEventTracker = { + linkReceived, + eventUrlPrefix: 'group-members/' + }; + + AddEventLinkTracker(linkTracker); + + return () => RemoveLinkEventTracker(linkTracker); + }, [ linkReceived ]); + useEffect(() => { setPageId(0); }, [ groupId, levelId, searchQuery ]); - useEffect(() => - { - - }) - useEffect(() => { if((groupId === -1) || (levelId === -1) || (pageId === -1)) return; @@ -133,15 +149,29 @@ export const GroupMembersView: FC = props => SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, levelId)); }, [ groupId, levelId, pageId, searchQuery ]); - if(!groupId || !pageData) return null; + useEffect(() => + { + if(groupId === -1) return; + + BatchUpdates(() => + { + setLevelId(-1); + setMembersData(null); + setTotalPages(0); + setSearchQuery(''); + setRemovingMemberName(null); + }) + }, [ groupId ]); + + if((groupId === -1) || !membersData) return null; return ( - + setGroupId(-1) } /> - + setSearchQuery(event.target.value) } /> @@ -153,7 +183,7 @@ export const GroupMembersView: FC = props => - { pageData.result.map((member, index) => + { membersData.result.map((member, index) => { return ( @@ -168,13 +198,13 @@ export const GroupMembersView: FC = props => { (member.rank !== GroupRank.REQUESTED) && - toggleAdmin(member) } /> + toggleAdmin(member) } /> } { (member.rank === GroupRank.REQUESTED) && acceptMembership(member) }> } - { (member.rank !== GroupRank.OWNER) && pageData.admin && (member.id !== GetSessionDataManager().userId) && + { (member.rank !== GroupRank.OWNER) && membersData.admin && (member.id !== GetSessionDataManager().userId) && removeMemberOrDeclineMembership(member) }> } @@ -184,13 +214,13 @@ export const GroupMembersView: FC = props => }) } - - { LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [pageData.totalMembersCount.toString(), (pageData.pageIndex + 1).toString(), totalPages.toString()]) } + { LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [membersData.totalMembersCount.toString(), (membersData.pageIndex + 1).toString(), totalPages.toString()]) } - diff --git a/src/components/groups/views/GroupRoomInformationView.tsx b/src/components/groups/views/GroupRoomInformationView.tsx new file mode 100644 index 00000000..789cfb69 --- /dev/null +++ b/src/components/groups/views/GroupRoomInformationView.tsx @@ -0,0 +1,149 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { DesktopViewEvent, GetGuestRoomResultEvent, GroupInformationComposer, GroupInformationEvent, GroupInformationParser, GroupRemoveMemberComposer, HabboGroupDeactivatedMessageEvent, RoomEntryInfoMessageEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { GetGroupInformation, GetSessionDataManager, LocalizeText, TryJoinGroup } from '../../../api'; +import { GetGroupManager } from '../../../api/groups/GetGroupManager'; +import { Base, Button, Column, Flex, Text } from '../../../common'; +import { CreateMessageHook, SendMessageHook } from '../../../hooks'; +import { NotificationBubbleView } from '../../../layout'; +import { NotificationUtilities } from '../../../views/notification-center/common/NotificationUtilities'; +import { BadgeImageView } from '../../../views/shared/badge-image/BadgeImageView'; +import { GroupMembershipType } from '../common/GroupMembershipType'; +import { GroupType } from '../common/GroupType'; + +export const GroupRoomInformationView: FC<{}> = props => +{ + const [ expectedGroupId, setExpectedGroupId ] = useState(0); + const [ groupInformation, setGroupInformation ] = useState(null); + const [ isOpen, setIsOpen ] = useState(true); + + const onDesktopViewEvent = useCallback((event: DesktopViewEvent) => + { + setExpectedGroupId(0); + setGroupInformation(null); + }, []); + + CreateMessageHook(DesktopViewEvent, onDesktopViewEvent); + + const onRoomEntryInfoMessageEvent = useCallback((event: RoomEntryInfoMessageEvent) => + { + setExpectedGroupId(0); + setGroupInformation(null); + }, []); + + CreateMessageHook(RoomEntryInfoMessageEvent, onRoomEntryInfoMessageEvent); + + const onGetGuestRoomResultEvent = useCallback((event: GetGuestRoomResultEvent) => + { + const parser = event.getParser(); + + if(!parser.roomEnter) return; + + if(parser.data.habboGroupId > 0) + { + setExpectedGroupId(parser.data.habboGroupId); + SendMessageHook(new GroupInformationComposer(parser.data.habboGroupId, false)); + } + else + { + setExpectedGroupId(0); + setGroupInformation(null); + } + }, []); + + CreateMessageHook(GetGuestRoomResultEvent, onGetGuestRoomResultEvent); + + const onHabboGroupDeactivatedMessageEvent = useCallback((event: HabboGroupDeactivatedMessageEvent) => + { + const parser = event.getParser(); + + if(!groupInformation || ((parser.groupId !== groupInformation.id) && (parser.groupId !== expectedGroupId))) return; + + setExpectedGroupId(0); + setGroupInformation(null); + }, [ expectedGroupId, groupInformation ]); + + CreateMessageHook(HabboGroupDeactivatedMessageEvent, onHabboGroupDeactivatedMessageEvent); + + const onGroupInformationEvent = useCallback((event: GroupInformationEvent) => + { + const parser = event.getParser(); + + if(parser.id !== expectedGroupId) return; + + setGroupInformation(parser); + }, [ expectedGroupId ]); + + CreateMessageHook(GroupInformationEvent, onGroupInformationEvent); + + const leaveGroup = () => + { + NotificationUtilities.confirm(LocalizeText('group.leaveconfirm.desc'), () => + { + SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId)); + }, null); + } + + const isRealOwner = (groupInformation && (groupInformation.ownerName === GetSessionDataManager().userName)); + + const getButtonText = () => + { + if(isRealOwner) return 'group.manage'; + + if(groupInformation.type === GroupType.PRIVATE) return ''; + + if(groupInformation.membershipType === GroupMembershipType.MEMBER) return 'group.leave'; + + if(groupInformation.membershipType === GroupMembershipType.NOT_MEMBER && groupInformation.type === GroupType.REGULAR) return 'group.join'; + + if(groupInformation.type === GroupType.EXCLUSIVE) + { + if(groupInformation.membershipType === GroupMembershipType.NOT_MEMBER) return 'group.requestmembership'; + + if(groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return 'group.membershippending'; + } + } + + const handleButtonClick = () => + { + if(isRealOwner) return GetGroupManager(groupInformation.id); + + if((groupInformation.type === GroupType.PRIVATE) && (groupInformation.membershipType === GroupMembershipType.NOT_MEMBER)) return; + + if(groupInformation.membershipType === GroupMembershipType.MEMBER) + { + leaveGroup(); + + return; + } + + TryJoinGroup(groupInformation.id); + } + + if(!groupInformation) return null; + + return ( + + + setIsOpen(value => !value) }> + { LocalizeText('group.homeroominfo.title') } + + + { isOpen && + <> + GetGroupInformation(groupInformation.id) }> + + + + { groupInformation.title } + + { (groupInformation.type !== GroupType.PRIVATE || isRealOwner) && + + } + } + + + ); +}; diff --git a/src/components/groups/views/creator/GroupCreatorTabConfirmationView.tsx b/src/components/groups/views/creator/GroupCreatorTabConfirmationView.tsx deleted file mode 100644 index 8b18520a..00000000 --- a/src/components/groups/views/creator/GroupCreatorTabConfirmationView.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { FC, useCallback } from 'react'; -import { LocalizeText } from '../../../../api'; -import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView'; -import { useGroupsContext } from '../../GroupsContext'; - -export const GroupCreatorTabConfirmationView: FC<{}> = props => -{ - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { groupName = '', groupDescription = '', groupBadgeParts = null, groupColors = null, groupColorsA = null, groupColorsB = null, purchaseCost = 0 } = groupsState; - - const getCompleteBadgeCode = useCallback(() => - { - let code = ''; - - if(!groupBadgeParts) return code; - - groupBadgeParts.forEach((badgePart) => - { - if(badgePart.code) code = code + badgePart.code; - }); - - return code; - }, [ groupBadgeParts ]); - - const getGroupColor = useCallback((colorIndex: number) => - { - if(colorIndex === 0) return groupColorsA.find(c => c.id === groupColors[colorIndex]).color; - - return groupColorsB.find(c => c.id === groupColors[colorIndex]).color; - }, [ groupColors, groupColorsA, groupColorsB ]); - - return ( -
-
-
{ LocalizeText('group.create.confirm.guildbadge') }
-
- -
-
-
{ LocalizeText('group.edit.color.guild.color') }
- { groupColors &&
-
-
-
} -
-
-
-
{ groupName }
-
{ groupDescription }
-
-
{ LocalizeText('group.create.confirm.buyinfo', ['amount'], [purchaseCost.toString()]) }
-
-
- ); -}; diff --git a/src/components/groups/views/creator/GroupCreatorView.scss b/src/components/groups/views/creator/GroupCreatorView.scss deleted file mode 100644 index 14f1c45d..00000000 --- a/src/components/groups/views/creator/GroupCreatorView.scss +++ /dev/null @@ -1,52 +0,0 @@ -.nitro-group-creator { - width: $nitro-group-creator-width; - - .creator-tabs { - display: flex; - align-items: center; - - .tab { - position: relative; - margin-left: -6px; - background-image: url('../../../../assets/images/groups/creator_tabs.png'); - background-repeat: no-repeat; - - &:first-child { - margin-left: 0; - } - - &.tab-blue-flat { - width: 84px; - height: 24px; - background-position: 0px 0px; - - &.active { - height: 28px; - background-position: 0px -24px; - } - } - - &.tab-blue-arrow { - width: 83px; - height: 24px; - background-position: 0px -52px; - - &.active { - height: 28px; - background-position: 0px -76px; - } - } - - &.tab-yellow { - width: 133px; - height: 28px; - background-position: 0px -104px; - - &.active { - height: 33px; - background-position: 0px -132px; - } - } - } - } -} diff --git a/src/components/groups/views/creator/GroupCreatorView.tsx b/src/components/groups/views/creator/GroupCreatorView.tsx deleted file mode 100644 index 1f076448..00000000 --- a/src/components/groups/views/creator/GroupCreatorView.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { GroupBuyComposer, GroupBuyDataComposer } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useEffect, useState } from 'react'; -import { HasHabboClub, LocalizeText } from '../../../../api'; -import { Base, Button, Column, Flex, Text } from '../../../../common'; -import { SendMessageHook } from '../../../../hooks'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; -import { NotificationUtilities } from '../../../../views/notification-center/common/NotificationUtilities'; -import { useGroupsContext } from '../../GroupsContext'; -import { GroupsActions } from '../../reducers/GroupsReducer'; -import { GroupTabBadgeView } from '../tabs/GroupTabBadgeView'; -import { GroupTabColorsView } from '../tabs/GroupTabColorsView'; -import { GroupTabIdentityView } from '../tabs/GroupTabIdentityView'; -import { GroupCreatorTabConfirmationView } from './GroupCreatorTabConfirmationView'; - -const TABS: number[] = [1, 2, 3, 4]; - -interface GroupCreatorViewProps -{ - isVisible: boolean; - onClose: () => void; -} - -export const GroupCreatorView: FC = props => -{ - const { isVisible = false, onClose = null } = props; - const [ currentTab, setCurrentTab ] = useState(1); - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { groupName = null, groupDescription = null, groupHomeroomId = null, groupColors = null, groupBadgeParts = null } = groupsState; - - const buyGroup = useCallback(() => - { - if(!groupBadgeParts) return; - - const badge = []; - - groupBadgeParts.forEach((part) => - { - if(part.code) - { - badge.push(part.key); - badge.push(part.color); - badge.push(part.position); - } - }); - - SendMessageHook(new GroupBuyComposer(groupName, groupDescription, groupHomeroomId, groupColors[0], groupColors[1], badge)); - }, [ groupName, groupDescription, groupHomeroomId, groupColors, groupBadgeParts ]); - - const previousStep = useCallback(() => - { - if(currentTab === 1) return onClose(); - - setCurrentTab(value => value - 1); - }, [ currentTab, onClose ]); - - const nextStep = useCallback(() => - { - switch(currentTab) - { - case 1: { - if(!groupName || groupName.length === 0 || !groupHomeroomId) - { - NotificationUtilities.simpleAlert(LocalizeText('group.edit.error.no.name.or.room.selected')); - - return; - } - - break; - } - case 4: - buyGroup(); - break; - } - - setCurrentTab(value => (value === 4 ? value : value + 1)); - }, [ currentTab, groupName, groupHomeroomId, buyGroup ]); - - useEffect(() => - { - if(!isVisible) - { - setCurrentTab(1); - - dispatchGroupsState({ - type: GroupsActions.RESET_GROUP_SETTINGS - }); - - return; - } - - SendMessageHook(new GroupBuyDataComposer()); - }, [ isVisible, dispatchGroupsState ]); - - if(!isVisible) return null; - - return ( - - - - - { TABS.map((tab, index) => - { - return ( - - { LocalizeText(`group.create.steplabel.${ tab }`) } - - ); - }) } - - - - - { LocalizeText(`group.create.stepcaption.${ currentTab }`) } - { LocalizeText(`group.create.stepdesc.${ currentTab }`) } - - - - { (currentTab === 1) && - } - { (currentTab === 2) && - } - { (currentTab === 3) && - } - { (currentTab === 4) && - } - - - - - - - - ); -}; diff --git a/src/components/groups/views/manager/GroupManagerView.scss b/src/components/groups/views/manager/GroupManagerView.scss deleted file mode 100644 index 2dd3baa5..00000000 --- a/src/components/groups/views/manager/GroupManagerView.scss +++ /dev/null @@ -1,4 +0,0 @@ -.nitro-group-manager { - height: $nitro-group-manager-height; - width: $nitro-group-manager-width; -} diff --git a/src/components/groups/views/manager/GroupManagerView.tsx b/src/components/groups/views/manager/GroupManagerView.tsx deleted file mode 100644 index 986a9d99..00000000 --- a/src/components/groups/views/manager/GroupManagerView.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { GroupDeleteComposer, GroupSaveColorsComposer, GroupSaveInformationComposer, GroupSavePreferencesComposer } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useState } from 'react'; -import { LocalizeText } from '../../../../api'; -import { Base, Button, Column, Flex, Text } from '../../../../common'; -import { SendMessageHook } from '../../../../hooks'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout'; -import { useGroupsContext } from '../../GroupsContext'; -import { GroupsActions } from '../../reducers/GroupsReducer'; -import { GroupTabBadgeView } from '../tabs/GroupTabBadgeView'; -import { GroupTabColorsView } from '../tabs/GroupTabColorsView'; -import { GroupTabIdentityView } from '../tabs/GroupTabIdentityView'; -import { GroupTabSettingsView } from '../tabs/GroupTabSettingsView'; - -const TABS: number[] = [1, 2, 3, 5]; - -export const GroupManagerView: FC<{}> = props => -{ - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { groupId = null, groupName = null, groupDescription = null, groupColors = null, groupBadgeParts = null, groupState = null, groupCanMembersDecorate = null } = groupsState; - - const [ currentTab, setCurrentTab ] = useState(1); - - const onClose = useCallback(() => - { - dispatchGroupsState({ - type: GroupsActions.RESET_GROUP_SETTINGS - }); - }, [ dispatchGroupsState ]); - - const saveGroup = useCallback(() => - { - const badge = []; - - if(!groupBadgeParts) return; - - groupBadgeParts.forEach((part) => - { - if(part.code) - { - badge.push(part.key); - badge.push(part.color); - badge.push(part.position); - } - }); - - SendMessageHook(new GroupSaveInformationComposer(groupId, groupName, groupDescription)); - //SendMessageHook(new GroupSaveBadgeComposer(groupId, badge)); - SendMessageHook(new GroupSaveColorsComposer(groupId, groupColors[0], groupColors[1])); - SendMessageHook(new GroupSavePreferencesComposer(groupId, groupState, groupCanMembersDecorate ? 0 : 1)); - - onClose(); - }, [ groupBadgeParts, groupId, groupName, groupDescription, groupColors, groupState, groupCanMembersDecorate, onClose ]); - - const deleteGroup = useCallback(() => - { - if(window.confirm(LocalizeText('group.deleteconfirm.title') + ' - ' + LocalizeText('group.deleteconfirm.desc'))) - { - SendMessageHook(new GroupDeleteComposer(groupId)); - onClose(); - } - }, [ groupId, onClose ]); - - if(!groupId) return null; - - return ( - - - - { TABS.map(tab => - { - return ( setCurrentTab(tab) }> - { LocalizeText(`group.edit.tab.${tab}`) } - ); - }) } - - - - - - { LocalizeText(`group.edit.tabcaption.${ currentTab }`) } - { LocalizeText(`group.edit.tabdesc.${ currentTab }`) } - - - - { currentTab === 1 && - } - { currentTab === 2 && - } - { currentTab === 3 && - } - { currentTab === 5 && - } - - - - - - - - ); -}; diff --git a/src/components/groups/views/room-information/GroupRoomInformationView.scss b/src/components/groups/views/room-information/GroupRoomInformationView.scss deleted file mode 100644 index 8c921539..00000000 --- a/src/components/groups/views/room-information/GroupRoomInformationView.scss +++ /dev/null @@ -1,13 +0,0 @@ -.nitro-group-room-information { - pointer-events: all; - padding: 6px 5px; - background-color: rgba($dark,.95); - box-shadow: inset 0px 5px lighten(rgba($dark,.6),2.5), inset 0 -4px darken(rgba($dark,.6),4); - font-size: $font-size-sm; - margin-bottom: 5px; - - .group-badge { - width: 50px; - height: 50px; - } -} diff --git a/src/components/groups/views/room-information/GroupRoomInformationView.tsx b/src/components/groups/views/room-information/GroupRoomInformationView.tsx deleted file mode 100644 index 7763528e..00000000 --- a/src/components/groups/views/room-information/GroupRoomInformationView.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { DesktopViewEvent, GetGuestRoomResultEvent, GroupInformationComposer, GroupInformationEvent, GroupInformationParser, GroupJoinComposer, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useState } from 'react'; -import { GetGroupInformation, GetSessionDataManager, LocalizeText } from '../../../../api'; -import { GetGroupManager } from '../../../../api/groups/GetGroupManager'; -import { CreateMessageHook, SendMessageHook } from '../../../../hooks'; -import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView'; -import { GroupMembershipType } from '../../common/GroupMembershipType'; -import { GroupType } from '../../common/GroupType'; - -export const GroupRoomInformationView: FC<{}> = props => -{ - const [ groupId, setGroupId ] = useState(null); - const [ groupInformation, setGroupInformation ] = useState(null); - const [ isExpended, setIsExpended ] = useState(true); - - const onGetGuestRoomResultEvent = useCallback((event: GetGuestRoomResultEvent) => - { - const parser = event.getParser(); - - setGroupInformation(null); - - if(parser.data.habboGroupId) - { - setGroupId(parser.data.habboGroupId); - SendMessageHook(new GroupInformationComposer(parser.data.habboGroupId, false)); - } - }, []); - - CreateMessageHook(GetGuestRoomResultEvent, onGetGuestRoomResultEvent); - - const onGroupInformationEvent = useCallback((event: GroupInformationEvent) => - { - const parser = event.getParser(); - - if(parser.flag || groupId !== parser.id) return; - console.log(parser); - setGroupInformation(null); - setGroupInformation(parser); - }, [ groupId ]); - - CreateMessageHook(GroupInformationEvent, onGroupInformationEvent); - - const onDesktopViewEvent = useCallback((event: DesktopViewEvent) => - { - setGroupId(0); - setGroupInformation(null); - }, []); - - CreateMessageHook(DesktopViewEvent, onDesktopViewEvent); - - const isRealOwner = useCallback(() => - { - if(!groupInformation) return false; - - return (groupInformation.ownerName === GetSessionDataManager().userName); - }, [ groupInformation ]); - - const tryJoinGroup = useCallback(() => - { - if(!groupInformation) return; - - SendMessageHook(new GroupJoinComposer(groupInformation.id)); - SendMessageHook(new GroupInformationComposer(groupInformation.id, false)); - }, [ groupInformation ]); - - const tryLeaveGroup = useCallback(() => - { - if(window.confirm(LocalizeText('group.leaveconfirm.desc'))) - { - SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId)); - SendMessageHook(new GroupInformationComposer(groupInformation.id, false)); - } - }, [ groupInformation ]); - - const getButtonText = useCallback(() => - { - if(isRealOwner()) return 'group.manage'; - - if(groupInformation.type === GroupType.PRIVATE) return ''; - - if(groupInformation.membershipType === GroupMembershipType.MEMBER) return 'group.leave'; - - if(groupInformation.membershipType === GroupMembershipType.NOT_MEMBER && groupInformation.type === GroupType.REGULAR) return 'group.join'; - - if(groupInformation.type === GroupType.EXCLUSIVE) - { - if(groupInformation.membershipType === GroupMembershipType.NOT_MEMBER) return 'group.requestmembership'; - - if(groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return 'group.membershippending'; - } - }, [ groupInformation, isRealOwner ]); - - const handleButtonClick = useCallback(() => - { - if(isRealOwner()) return GetGroupManager(groupInformation.id); - - if(groupInformation.type === GroupType.PRIVATE && groupInformation.membershipType === GroupMembershipType.NOT_MEMBER) return; - - if(groupInformation.membershipType === GroupMembershipType.MEMBER) return tryLeaveGroup(); - - return tryJoinGroup(); - }, [ groupInformation, tryLeaveGroup, tryJoinGroup, isRealOwner ]); - - if(!groupInformation) return null; - - return ( -
-
setIsExpended(value => !value) }> -
{ LocalizeText('group.homeroominfo.title') }
- -
- { isExpended && <> -
GetGroupInformation(groupId) }> -
- -
-
- { groupInformation.title } -
-
- { (groupInformation.type !== GroupType.PRIVATE || isRealOwner()) && - - } - } -
- ); -}; diff --git a/src/components/groups/views/tabs/GroupSharedTabs.scss b/src/components/groups/views/tabs/GroupSharedTabs.scss deleted file mode 100644 index c1657df1..00000000 --- a/src/components/groups/views/tabs/GroupSharedTabs.scss +++ /dev/null @@ -1,7 +0,0 @@ -.nitro-groups { - - .group-color-swatch { - width: 30px; - height: 40px; - } -} diff --git a/src/components/groups/views/tabs/GroupTabBadgeView.tsx b/src/components/groups/views/tabs/GroupTabBadgeView.tsx index 7b790fba..9b70f702 100644 --- a/src/components/groups/views/tabs/GroupTabBadgeView.tsx +++ b/src/components/groups/views/tabs/GroupTabBadgeView.tsx @@ -1,26 +1,26 @@ import { GroupSaveBadgeComposer } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useState } from 'react'; +import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; import { Column, Flex, Grid } from '../../../../common'; import { SendMessageHook } from '../../../../hooks'; import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView'; import { GroupBadgePart } from '../../common/GroupBadgePart'; +import { IGroupData } from '../../common/IGroupData'; import { useGroupsContext } from '../../GroupsContext'; -import { GroupsActions } from '../../reducers/GroupsReducer'; import { GroupBadgeCreatorView } from '../GroupBadgeCreatorView'; -const POSITIONS: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - interface GroupTabBadgeViewProps { skipDefault?: boolean; + setCloseAction: Dispatch boolean }>>; + groupData: IGroupData; + setGroupData: Dispatch>; } export const GroupTabBadgeView: FC = props => { - const { skipDefault = null } = props; + const { groupData = null, setGroupData = null, setCloseAction = null, skipDefault = null } = props; const [ badgeParts, setBadgeParts ] = useState(null); - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { badgeBases = null, badgeSymbols = null, badgePartColors = null, groupId = -1, groupBadgeParts = null } = groupsState; + const { groupCustomize = null } = useGroupsContext(); const getModifiedBadgeCode = () => { @@ -33,36 +33,29 @@ export const GroupTabBadgeView: FC = props => return badgeCode; } - useEffect(() => + const saveBadge = useCallback(() => { - if(groupBadgeParts && groupBadgeParts.length) return; - - const badgeParts = [ - new GroupBadgePart(GroupBadgePart.BASE, badgeBases[0].id, badgePartColors[0].id), - new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id), - new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id), - new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id), - new GroupBadgePart(GroupBadgePart.SYMBOL, 0, badgePartColors[0].id) - ]; + if(!groupData || !badgeParts || !badgeParts.length) return false; - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_BADGE_PARTS, - payload: { badgeParts } - }); - }, [ groupBadgeParts, badgeBases, badgePartColors, dispatchGroupsState ]); + if((groupData.groupBadgeParts === badgeParts)) return true; - useEffect(() => - { - setBadgeParts(groupBadgeParts); - }, [ groupBadgeParts ]); + if(groupData.groupId <= 0) + { + setGroupData(prevValue => + { + const newValue = { ...prevValue }; - useEffect(() => - { - if((groupId <= 0) || !badgeParts || !badgeParts.length || !groupBadgeParts || !groupBadgeParts.length || (badgeParts === groupBadgeParts)) return; + newValue.groupBadgeParts = badgeParts; + + return newValue; + }); + + return true; + } const badge = []; - badgeParts.forEach((part) => + badgeParts.forEach(part => { if(!part.code) return; @@ -70,31 +63,50 @@ export const GroupTabBadgeView: FC = props => badge.push(part.color); badge.push(part.position); }); - - console.log('send') - SendMessageHook(new GroupSaveBadgeComposer(groupId, badge)); - }, [ groupId, badgeParts, groupBadgeParts ]); + SendMessageHook(new GroupSaveBadgeComposer(groupData.groupId, badge)); - // useEffect(() => - // { - // if((groupId <= 0) || !badgeParts || !badgeParts.length || (badgeParts === groupBadgeParts)) return; + return true; + }, [ groupData, badgeParts, setGroupData ]); - // const badge = []; - - // badgeParts.forEach((part) => - // { - // if(!part.code) return; - - // badge.push(part.key); - // badge.push(part.color); - // badge.push(part.position); - // }); - - // console.log('send') + useEffect(() => + { + if(groupData.groupBadgeParts) return; - // SendMessageHook(new GroupSaveBadgeComposer(groupId, badge)); - // }, [ groupId, groupBadgeParts, badgeParts ]); + const badgeParts = [ + new GroupBadgePart(GroupBadgePart.BASE, groupCustomize.badgeBases[0].id, groupCustomize.badgePartColors[0].id), + new GroupBadgePart(GroupBadgePart.SYMBOL, 0, groupCustomize.badgePartColors[0].id), + new GroupBadgePart(GroupBadgePart.SYMBOL, 0, groupCustomize.badgePartColors[0].id), + new GroupBadgePart(GroupBadgePart.SYMBOL, 0, groupCustomize.badgePartColors[0].id), + new GroupBadgePart(GroupBadgePart.SYMBOL, 0, groupCustomize.badgePartColors[0].id) + ]; + + setGroupData(prevValue => + { + const groupBadgeParts = badgeParts; + + return { ...prevValue, groupBadgeParts }; + }); + }, [ groupData.groupBadgeParts, groupCustomize, setGroupData ]); + + useEffect(() => + { + if(groupData.groupId <= 0) + { + setBadgeParts(groupData.groupBadgeParts ? [ ...groupData.groupBadgeParts ] : null); + + return; + } + + setBadgeParts(groupData.groupBadgeParts); + }, [ groupData ]); + + useEffect(() => + { + setCloseAction({ action: saveBadge }); + + return () => setCloseAction(null); + }, [ setCloseAction, saveBadge ]); return ( diff --git a/src/components/groups/views/tabs/GroupTabColorsView.tsx b/src/components/groups/views/tabs/GroupTabColorsView.tsx index 364da6b0..2dcf4220 100644 --- a/src/components/groups/views/tabs/GroupTabColorsView.tsx +++ b/src/components/groups/views/tabs/GroupTabColorsView.tsx @@ -1,58 +1,107 @@ +import { GroupSaveColorsComposer } from '@nitrots/nitro-renderer'; import classNames from 'classnames'; -import { FC, useEffect } from 'react'; +import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; import { LocalizeText } from '../../../../api'; import { AutoGrid, Base, Column, Flex, Grid, Text } from '../../../../common'; +import { SendMessageHook } from '../../../../hooks'; +import { IGroupData } from '../../common/IGroupData'; import { useGroupsContext } from '../../GroupsContext'; -import { GroupsActions } from '../../reducers/GroupsReducer'; -export const GroupTabColorsView: FC<{}> = props => +interface GroupTabColorsViewProps { - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { groupColors = null, groupColorsA = null, groupColorsB = null } = groupsState; + groupData: IGroupData; + setGroupData: Dispatch>; + setCloseAction: Dispatch boolean }>>; +} + +export const GroupTabColorsView: FC = props => +{ + const { groupData = null, setGroupData = null, setCloseAction = null } = props; + const [ colors, setColors ] = useState(null); + const { groupCustomize = null } = useGroupsContext(); const getGroupColor = (colorIndex: number) => { - if(colorIndex === 0) return groupColorsA.find(color => (color.id === groupColors[colorIndex])).color; + if(colorIndex === 0) return groupCustomize.groupColorsA.find(color => (color.id === colors[colorIndex])).color; - return groupColorsB.find(color => (color.id === groupColors[colorIndex])).color; + return groupCustomize.groupColorsB.find(color => (color.id === colors[colorIndex])).color; } const selectColor = (colorIndex: number, colorId: number) => { - const clonedGroupColors = Array.from(groupColors); + setColors(prevValue => + { + const newColors = [ ...prevValue ]; - clonedGroupColors[colorIndex] = colorId; + newColors[colorIndex] = colorId; - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_COLORS, - payload: { - objectValues: clonedGroupColors - } - }); + return newColors; + }); } + const saveColors = useCallback(() => + { + if(!groupData || !colors || !colors.length) return false; + + if(groupData.groupColors === colors) return true; + + if(groupData.groupId <= 0) + { + setGroupData(prevValue => + { + const newValue = { ...prevValue }; + + newValue.groupColors = [ ...colors ]; + + return newValue; + }); + + return true; + } + + SendMessageHook(new GroupSaveColorsComposer(groupData.groupId, colors[0], colors[1])); + + return true; + }, [ groupData, colors, setGroupData ]); + useEffect(() => { - if(!groupColorsA || !groupColorsB || groupColors) return; + if(!groupCustomize.groupColorsA || !groupCustomize.groupColorsB || groupData.groupColors) return; - const colors: number[] = [ - groupColorsA[0].id, - groupColorsB[0].id - ]; + const groupColors = [ groupCustomize.groupColorsA[0].id, groupCustomize.groupColorsB[0].id ]; - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_COLORS, - payload: { - objectValues: colors - } - }); - }, [ dispatchGroupsState, groupColors, groupColorsA, groupColorsB ]); + setGroupData(prevValue => + { + return { ...prevValue, groupColors }; + }); + }, [ groupCustomize, groupData.groupColors, setGroupData ]); + + useEffect(() => + { + if(groupData.groupId <= 0) + { + setColors(groupData.groupColors ? [ ...groupData.groupColors ] : null); + + return; + } + + setColors(groupData.groupColors); + }, [ groupData ]); + + useEffect(() => + { + setCloseAction({ action: saveColors }); + + return () => setCloseAction(null); + }, [ setCloseAction, saveColors ]); + + if(!colors) return null; return ( { LocalizeText('group.edit.color.guild.color') } - { groupColors && (groupColors.length > 0) && + { groupData.groupColors && (groupData.groupColors.length > 0) && @@ -61,18 +110,18 @@ export const GroupTabColorsView: FC<{}> = props => { LocalizeText('group.edit.color.primary.color') } - { groupColors && groupColorsA && groupColorsA.map((item, index) => + { groupData.groupColors && groupCustomize.groupColorsA && groupCustomize.groupColorsA.map((item, index) => { - return
selectColor(0, item.id) }>
+ return
selectColor(0, item.id) }>
}) }
{ LocalizeText('group.edit.color.secondary.color') } - { groupColorsB && groupColorsB.map((item, index) => + { groupData.groupColors && groupCustomize.groupColorsB && groupCustomize.groupColorsB.map((item, index) => { - return
selectColor(1, item.id) }>
+ return
selectColor(1, item.id) }>
}) }
diff --git a/src/components/groups/views/tabs/GroupTabCreatorConfirmationView.tsx b/src/components/groups/views/tabs/GroupTabCreatorConfirmationView.tsx new file mode 100644 index 00000000..07792208 --- /dev/null +++ b/src/components/groups/views/tabs/GroupTabCreatorConfirmationView.tsx @@ -0,0 +1,69 @@ +import { Dispatch, FC, SetStateAction } from 'react'; +import { LocalizeText } from '../../../../api'; +import { Base, Column, Flex, Grid, Text } from '../../../../common'; +import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView'; +import { IGroupData } from '../../common/IGroupData'; +import { useGroupsContext } from '../../GroupsContext'; + +interface GroupTabCreatorConfirmationViewProps +{ + groupData: IGroupData; + setGroupData: Dispatch>; + purchaseCost: number; +} + +export const GroupTabCreatorConfirmationView: FC = props => +{ + const { groupData = null, setGroupData = null, purchaseCost = 0 } = props; + const { groupCustomize = null } = useGroupsContext(); + + const getCompleteBadgeCode = () => + { + if(!groupData || !groupData.groupBadgeParts || !groupData.groupBadgeParts.length) return ''; + + let badgeCode = ''; + + groupData.groupBadgeParts.forEach(part => (part.code && (badgeCode += part.code))); + + return badgeCode; + } + + const getGroupColor = (colorIndex: number) => + { + if(colorIndex === 0) return groupCustomize.groupColorsA.find(c => c.id === groupData.groupColors[colorIndex]).color; + + return groupCustomize.groupColorsB.find(c => c.id === groupData.groupColors[colorIndex]).color; + } + + if(!groupData) return null; + + return ( + + + + { LocalizeText('group.create.confirm.guildbadge') } + + + + { LocalizeText('group.edit.color.guild.color') } + + + + + + + + + + { groupData.groupName } + { groupData.groupDescription } + + { LocalizeText('group.create.confirm.info') } + + + { LocalizeText('group.create.confirm.buyinfo', [ 'amount' ], [ purchaseCost.toString() ]) } + + + + ); +}; diff --git a/src/components/groups/views/tabs/GroupTabIdentityView.tsx b/src/components/groups/views/tabs/GroupTabIdentityView.tsx index 1ed74d7e..974f66a9 100644 --- a/src/components/groups/views/tabs/GroupTabIdentityView.tsx +++ b/src/components/groups/views/tabs/GroupTabIdentityView.tsx @@ -1,75 +1,117 @@ -import { FC } from 'react'; +import { GroupDeleteComposer, GroupSaveInformationComposer } from '@nitrots/nitro-renderer'; +import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; import { CreateLinkEvent, LocalizeText } from '../../../../api'; -import { Column, Flex, Text } from '../../../../common'; -import { useGroupsContext } from '../../GroupsContext'; -import { GroupsActions } from '../../reducers/GroupsReducer'; +import { Base, Button, Column, Flex, Text } from '../../../../common'; +import { BatchUpdates, SendMessageHook } from '../../../../hooks'; +import { NotificationUtilities } from '../../../../views/notification-center/common/NotificationUtilities'; +import { IGroupData } from '../../common/IGroupData'; interface GroupTabIdentityViewProps { + groupData: IGroupData; + setGroupData: Dispatch>; + setCloseAction: Dispatch boolean }>>; isCreator?: boolean; + availableRooms?: { id: number, name: string }[]; } export const GroupTabIdentityView: FC = props => { - const { isCreator = false } = props; - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { groupName = '', groupDescription = '', groupHomeroomId = 0, availableRooms = null } = groupsState; - - const setName = (name: string) => + const { groupData = null, setGroupData = null, setCloseAction = null, isCreator = false, availableRooms = [] } = props; + const [ groupName, setGroupName ] = useState(''); + const [ groupDescription, setGroupDescription ] = useState(''); + const [ groupHomeroomId, setGroupHomeroomId ] = useState(-1); + + const deleteGroup = () => { - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_NAME, - payload: { - stringValues: [ name ] - } - }) + if(!groupData || (groupData.groupId <= 0)) return; + + NotificationUtilities.confirm(LocalizeText('group.deleteconfirm.desc'), () => + { + SendMessageHook(new GroupDeleteComposer(groupData.groupId)); + }, null, null, null, LocalizeText('group.deleteconfirm.title')); } - const setDescription = (description: string) => + const saveIdentity = useCallback(() => { - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_DESCRIPTION, - payload: { - stringValues: [ description ] - } - }) - } + if(!groupData || !groupName || !groupName.length) return false; - const setHomeroomId = (id: number) => + if((groupName === groupData.groupName) && (groupDescription === groupData.groupDescription)) return true; + + if(groupData.groupId <= 0) + { + if(groupHomeroomId <= 0) return false; + + setGroupData(prevValue => + { + const newValue = { ...prevValue }; + + newValue.groupName = groupName; + newValue.groupDescription = groupDescription; + newValue.groupHomeroomId = groupHomeroomId; + + return newValue; + }); + + return true; + } + + SendMessageHook(new GroupSaveInformationComposer(groupData.groupId, groupName, (groupDescription || ''))); + + return true; + }, [ groupData, groupName, groupDescription, groupHomeroomId, setGroupData ]); + + useEffect(() => { - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_HOMEROOM_ID, - payload: { - numberValues: [ id ] - } - }) - } + BatchUpdates(() => + { + setGroupName(groupData.groupName || ''); + setGroupDescription(groupData.groupDescription || ''); + setGroupHomeroomId(groupData.groupHomeroomId); + }); + }, [ groupData ]); + + useEffect(() => + { + setCloseAction({ action: saveIdentity }); + + return () => setCloseAction(null); + }, [ setCloseAction, saveIdentity ]); + + if(!groupData) return null; return ( - <> + - { LocalizeText('group.edit.name') } - setName(event.target.value) } /> + { LocalizeText('group.edit.name') } + setGroupName(event.target.value) } /> - { LocalizeText('group.edit.desc') } -