diff --git a/src/App.scss b/src/App.scss index 5bc604ea..26af7747 100644 --- a/src/App.scss +++ b/src/App.scss @@ -69,11 +69,17 @@ $camera-checkout-width: 350px; $room-info-width: 325px; +$nitro-group-creator-width: 383px; $nitro-mod-tools-width: 175px; +$nitro-group-manager-width: 375px; +$nitro-group-manager-height: 355px; + $nitro-chooser-width: 200px; $nitro-chooser-height: 200px; +$nitro-guide-tool-width: 250px; + .nitro-app { width: 100%; height: 100%; diff --git a/src/api/groups/TryJoinGroup.ts b/src/api/groups/TryJoinGroup.ts index 607a8a27..1b5ee7ff 100644 --- a/src/api/groups/TryJoinGroup.ts +++ b/src/api/groups/TryJoinGroup.ts @@ -1,7 +1,4 @@ import { GroupJoinComposer } from '@nitrots/nitro-renderer'; import { SendMessageHook } from '../../hooks'; -export function TryJoinGroup(groupId: number): void -{ - SendMessageHook(new GroupJoinComposer(groupId)); -} +export const TryJoinGroup = (groupId: number) => SendMessageHook(new GroupJoinComposer(groupId)); diff --git a/src/common/HorizontalRule.tsx b/src/common/HorizontalRule.tsx new file mode 100644 index 00000000..25aeaf99 --- /dev/null +++ b/src/common/HorizontalRule.tsx @@ -0,0 +1,38 @@ +import { CSSProperties, FC, useMemo } from 'react'; +import { Base, BaseProps } from './Base'; +import { ColorVariantType } from './types'; + +export interface HorizontalRuleProps extends BaseProps +{ + variant?: ColorVariantType; + height?: number; +} + +export const HorizontalRule: FC = props => +{ + const { variant = 'black', height = 1, classNames = [], style = {}, ...rest } = props; + + const getClassNames = useMemo(() => + { + const newClassNames: string[] = []; + + if(variant) newClassNames.push('bg-' + variant); + + if(classNames.length) newClassNames.push(...classNames); + + return newClassNames; + }, [ variant, classNames ]); + + const getStyle = useMemo(() => + { + let newStyle: CSSProperties = { display: 'list-item' }; + + if(height > 0) newStyle.height = height; + + if(Object.keys(style).length) newStyle = { ...newStyle, ...style }; + + return newStyle; + }, [ height, style ]); + + return ; +} diff --git a/src/common/index.ts b/src/common/index.ts index c1380a04..763ce538 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -7,6 +7,7 @@ export * from './Flex'; export * from './FormGroup'; export * from './Grid'; export * from './GridContext'; +export * from './HorizontalRule'; export * from './layout'; export * from './Text'; export * from './types'; diff --git a/src/components/groups/GroupView.scss b/src/components/groups/GroupView.scss index 934c8a05..b787fd43 100644 --- a/src/components/groups/GroupView.scss +++ b/src/components/groups/GroupView.scss @@ -1,54 +1,137 @@ -.nitro-groups { - .tab-image { - width: 122px; - height: 68px; - display: flex; - align-items: center; - justify-content: center; +.nitro-group-tab-image { + width: 122px; + height: 68px; + background: url('../../assets/images/groups/creator_images.png') no-repeat; - div { - background-image: url('../../assets/images/groups/creator_images.png'); - background-repeat: no-repeat; + &.tab-1 { + background-position: 0px 0px; + width: 99px; + height: 50px; + } + + &.tab-2 { + background-position: -99px 0px; + width: 98px; + height: 62px; + } + + &.tab-3 { + background-position: 0px -50px; + width: 96px; + height: 45px; + } + + &.tab-4, + &.tab-5 { + background-position: 0px -95px; + width: 114px; + height: 61px; + } +} + +.group-information { + width: 100%; + height: 100%; + display: flex; + + .group-badge { + width: 78px; + height: 78px; + + .badge-image { + background-size: contain; } + } - &.tab-1 { - div { - background-position: 0px 0px; - width: 99px; + .group-description { + height: 55px; + } +} + +.nitro-group-information-standalone { + width: 500px; +} + +.nitro-group-members { + width: 400px; + + .nitro-group-members-list-grid { + + .member-list-item { + height: 50px; + max-height: 50px; + + .avatar-head { + position: relative; + overflow: hidden; + width: 40px; height: 50px; - } - } - - &.tab-2 { - div { - background-position: -99px 0px; - width: 98px; - height: 62px; - } - } - - &.tab-3 { - div { - background-position: 0px -50px; - width: 96px; - height: 45px; - } - } - - &.tab-4, &.tab-5 { - div { - background-position: 0px -95px; - width: 114px; - height: 61px; + + .avatar-image { + position: absolute; + left: -25px; + top: -20px; + } } } } } +.group-badge-preview { + width: 42px; + height: 42px; + background-color: $grid-bg-color; + + &.active { + border-color: $grid-active-border-color !important; + background-color: $grid-active-bg-color; + + &:before { + position: absolute; + content: ' '; + width: 0; + height: 0; + border-top: 10px solid white; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + bottom: -10px; + } + } +} + +.group-badge-color-swatch, +.group-badge-position-swatch { + position: relative; + border-radius: $border-radius; + width: 16px; + height: 16px; + background: $white; + border: 2px solid $white; + box-shadow: inset 3px 3px rgba(0, 0, 0, .1); + + &.active { + box-shadow: none; + } +} + +.group-badge-position-swatch { + box-shadow: inset 3px 3px rgba(0, 0, 0, .1); + + &.active { + background: $primary; + } +} + +.group-badge-color-swatch { + box-shadow: inset 2px 2px rgba(0, 0, 0, .2); +} + +.group-color-swatch { + width: 30px; + height: 40px; +} + @import './views/creator/GroupCreatorView'; -@import './views/information/GroupInformationView'; -@import './views/information-standalone/GroupInformationStandaloneView'; @import './views/manager/GroupManagerView'; -@import './views/members/GroupMembersView'; @import './views/room-information/GroupRoomInformationView'; -@import './views/shared-tabs/GroupSharedTabs'; +@import './views/tabs/GroupSharedTabs'; diff --git a/src/components/groups/GroupsView.tsx b/src/components/groups/GroupsView.tsx index 2a2ec8b6..ac436811 100644 --- a/src/components/groups/GroupsView.tsx +++ b/src/components/groups/GroupsView.tsx @@ -12,30 +12,31 @@ import { GroupMembersView } from './views/members/GroupMembersView'; export const GroupsView: FC<{}> = props => { - const [ isCreatorVisible, setIsCreatorVisible ] = useState(false); - const [ groupMembersId, setGroupMembersId ] = useState(null); - const [ groupMembersLevel, setGroupMembersLevel ] = useState(null); + 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 onGroupPurchasedEvent = useCallback((event: GroupPurchasedEvent) => - { - const parser = event.getParser(); - - setIsCreatorVisible(false); - TryVisitRoom(parser.roomId); - }, []); - - CreateMessageHook(GroupPurchasedEvent, onGroupPurchasedEvent); - const closeMembers = () => { BatchUpdates(() => { - setGroupMembersId(null); - setGroupMembersLevel(null); + setCurrentGroupId(null); + setCurrentGroupLevelId(null); }); } + const onGroupPurchasedEvent = useCallback((event: GroupPurchasedEvent) => + { + const parser = event.getParser(); + + setCreatorVisible(false); + TryVisitRoom(parser.roomId); + }, []); + + CreateMessageHook(GroupPurchasedEvent, onGroupPurchasedEvent); + const linkReceived = useCallback((url: string) => { const parts = url.split('/'); @@ -45,7 +46,7 @@ export const GroupsView: FC<{}> = props => switch(parts[1]) { case 'create': - setIsCreatorVisible(true); + setCreatorVisible(true); return; case 'manage': if(!parts[2]) return; @@ -55,12 +56,16 @@ export const GroupsView: FC<{}> = props => case 'members': if(!parts[2]) return; + const groupId = (parseInt(parts[2]) || -1); + const levelId = (parseInt(parts[3]) || 3); + BatchUpdates(() => { - setGroupMembersId(Number(parts[2])); - - if(parts[3]) setGroupMembersLevel(Number(parts[3])); + setCurrentGroupId(groupId); + setCurrentGroupLevelId(levelId); + setMembersVisible(true); }); + return; } }, []); @@ -86,9 +91,10 @@ export const GroupsView: FC<{}> = props =>
- setIsCreatorVisible(false) } /> + setCreatorVisible(false) } /> - + { isMembersVisible && + }
diff --git a/src/components/groups/views/GroupBadgeCreatorView.tsx b/src/components/groups/views/GroupBadgeCreatorView.tsx new file mode 100644 index 00000000..acc6a22f --- /dev/null +++ b/src/components/groups/views/GroupBadgeCreatorView.tsx @@ -0,0 +1,107 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Dispatch, FC, SetStateAction, useEffect, useRef, 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'; + +interface GroupBadgeCreatorViewProps +{ + badgeParts: GroupBadgePart[]; + setBadgeParts: Dispatch>; +} + +const POSITIONS: number[] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]; + +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 setPartProperty = (partIndex: number, property: string, value: number) => + { + const newBadgeParts = [ ...copiedBadgeParts ]; + + newBadgeParts[partIndex][property] = value; + + BatchUpdates(() => + { + setCopiedBadgeParts(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; + + return ( + <> + { ((selectedIndex < 0) && copiedBadgeParts && (copiedBadgeParts.length > 0)) && copiedBadgeParts.map((part, index) => + { + return ( + + setSelectedIndex(index) }> + { (copiedBadgeParts[index].code && (copiedBadgeParts[index].code.length > 0)) && + } + { (!copiedBadgeParts[index].code || !copiedBadgeParts[index].code.length) && + + + } + + { (part.type !== GroupBadgePart.BASE) && + + { POSITIONS.map((position, posIndex) => + { + return setPartProperty(index, 'position', position) }> + }) } + } + + { (badgePartColors.length > 0) && badgePartColors.map((item, colorIndex) => + { + return setPartProperty(index, 'color', (colorIndex + 1)) }> + }) } + + + ); + }) } + { (selectedIndex >= 0) && + + { (copiedBadgeParts[selectedIndex].type === GroupBadgePart.SYMBOL) && + setPartProperty(selectedIndex, 'key', 0) }> + + + + } + { ((copiedBadgeParts[selectedIndex].type === GroupBadgePart.BASE) ? badgeBases : badgeSymbols).map((item, index) => + { + return ( + setPartProperty(selectedIndex, 'key', item.id) }> + + + ); + }) } + } + + ); +} diff --git a/src/components/groups/views/creator/GroupCreatorView.scss b/src/components/groups/views/creator/GroupCreatorView.scss index 565be07f..14f1c45d 100644 --- a/src/components/groups/views/creator/GroupCreatorView.scss +++ b/src/components/groups/views/creator/GroupCreatorView.scss @@ -1,5 +1,5 @@ .nitro-group-creator { - width: 433px; + width: $nitro-group-creator-width; .creator-tabs { display: flex; @@ -11,14 +11,8 @@ background-image: url('../../../../assets/images/groups/creator_tabs.png'); background-repeat: no-repeat; - div { - position: absolute; - left: 50%; - transform: translateX(-50%); - } - &:first-child { - margin-left: 0px; + margin-left: 0; } &.tab-blue-flat { @@ -26,10 +20,6 @@ height: 24px; background-position: 0px 0px; - div { - margin-top: 1px; - } - &.active { height: 28px; background-position: 0px -24px; @@ -41,10 +31,6 @@ height: 24px; background-position: 0px -52px; - div { - margin-top: 1px; - } - &.active { height: 28px; background-position: 0px -76px; @@ -56,10 +42,6 @@ height: 28px; background-position: 0px -104px; - div { - margin-top: 3px; - } - &.active { height: 33px; background-position: 0px -132px; @@ -67,10 +49,4 @@ } } } - - .creator-tab { - height: 260px; - min-height: 260px; - max-height: 260px; - } } diff --git a/src/components/groups/views/creator/GroupCreatorView.tsx b/src/components/groups/views/creator/GroupCreatorView.tsx index 53d68ae1..1f076448 100644 --- a/src/components/groups/views/creator/GroupCreatorView.tsx +++ b/src/components/groups/views/creator/GroupCreatorView.tsx @@ -1,15 +1,15 @@ import { GroupBuyComposer, GroupBuyDataComposer } from '@nitrots/nitro-renderer'; -import classNames from 'classnames'; import { FC, useCallback, useEffect, useState } from 'react'; -import { Button } from 'react-bootstrap'; 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 { GroupSharedTabBadgeView } from '../shared-tabs/GroupSharedTabBadgeView'; -import { GroupSharedTabColorsView } from '../shared-tabs/GroupSharedTabColorsView'; -import { GroupSharedTabIdentityView } from '../shared-tabs/GroupSharedTabIdentityView'; +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]; @@ -22,34 +22,17 @@ interface GroupCreatorViewProps 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 { isVisible = false, onClose = null } = props; - - const [ currentTab, setCurrentTab ] = useState(1); - - useEffect(() => - { - if(!isVisible) - { - setCurrentTab(1); - dispatchGroupsState({ - type: GroupsActions.RESET_GROUP_SETTINGS - }); - } - else - { - SendMessageHook(new GroupBuyDataComposer()); - } - }, [ isVisible ]); - const buyGroup = useCallback(() => { - const badge = []; - if(!groupBadgeParts) return; + const badge = []; + groupBadgeParts.forEach((part) => { if(part.code) @@ -77,56 +60,78 @@ export const GroupCreatorView: FC = props => case 1: { if(!groupName || groupName.length === 0 || !groupHomeroomId) { - alert(LocalizeText('group.edit.error.no.name.or.room.selected')); + NotificationUtilities.simpleAlert(LocalizeText('group.edit.error.no.name.or.room.selected')); + return; } + break; } - case 4: { + case 4: buyGroup(); break; - } } - setCurrentTab(value => - { - return (value === 4 ? value : value + 1) + setCurrentTab(value => (value === 4 ? value : value + 1)); + }, [ currentTab, groupName, groupHomeroomId, buyGroup ]); + + useEffect(() => + { + if(!isVisible) + { + setCurrentTab(1); + + dispatchGroupsState({ + type: GroupsActions.RESET_GROUP_SETTINGS }); - }, [ currentTab, groupName, groupHomeroomId ]); + + 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 && } -
-
- - -
+ { + 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.scss b/src/components/groups/views/information-standalone/GroupInformationStandaloneView.scss deleted file mode 100644 index c431fe7c..00000000 --- a/src/components/groups/views/information-standalone/GroupInformationStandaloneView.scss +++ /dev/null @@ -1,3 +0,0 @@ -.nitro-group-information-standalone { - width: 500px; -} diff --git a/src/components/groups/views/information-standalone/GroupInformationStandaloneView.tsx b/src/components/groups/views/information-standalone/GroupInformationStandaloneView.tsx index 73666c57..f1e67980 100644 --- a/src/components/groups/views/information-standalone/GroupInformationStandaloneView.tsx +++ b/src/components/groups/views/information-standalone/GroupInformationStandaloneView.tsx @@ -1,7 +1,7 @@ import { GroupInformationEvent, GroupInformationParser } from '@nitrots/nitro-renderer'; import { FC, useCallback, useState } from 'react'; import { LocalizeText } from '../../../../api'; -import { CreateMessageHook } from '../../../../hooks'; +import { BatchUpdates, CreateMessageHook } from '../../../../hooks'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; import { GroupInformationView } from '../information/GroupInformationView'; @@ -15,8 +15,11 @@ export const GroupInformationStandaloneView: FC<{}> = props => if(!parser.flag) return; - setGroupInformation(null); - setGroupInformation(parser); + BatchUpdates(() => + { + setGroupInformation(null); + setGroupInformation(parser); + }); }, []); CreateMessageHook(GroupInformationEvent, onGroupInformationEvent); @@ -24,9 +27,9 @@ export const GroupInformationStandaloneView: FC<{}> = props => if(!groupInformation) return null; return ( - + setGroupInformation(null) } /> - + setGroupInformation(null) } /> diff --git a/src/components/groups/views/information/GroupInformationView.scss b/src/components/groups/views/information/GroupInformationView.scss deleted file mode 100644 index dded1dbe..00000000 --- a/src/components/groups/views/information/GroupInformationView.scss +++ /dev/null @@ -1,18 +0,0 @@ -.group-information { - width: 100%; - height: 100%; - display: flex; - - .group-badge { - width: 78px; - height: 78px; - - .badge-image { - background-size: contain; - } - } - - .group-description { - height: 55px; - } -} diff --git a/src/components/groups/views/information/GroupInformationView.tsx b/src/components/groups/views/information/GroupInformationView.tsx index 00bc7272..1cd8547c 100644 --- a/src/components/groups/views/information/GroupInformationView.tsx +++ b/src/components/groups/views/information/GroupInformationView.tsx @@ -4,7 +4,9 @@ import { CreateLinkEvent, GetSessionDataManager, LocalizeText, TryVisitRoom } fr 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'; @@ -21,30 +23,21 @@ export const GroupInformationView: FC = props => { const { groupInformation = null, onClose = null } = props; - const joinGroup = useCallback(() => + const isRealOwner = () => (groupInformation && (groupInformation.ownerName === GetSessionDataManager().userName)); + + const joinGroup = () => (groupInformation && TryJoinGroup(groupInformation.id)); + + const leaveGroup = () => { - if(!groupInformation) return; + NotificationUtilities.confirm(LocalizeText('group.leaveconfirm.desc'), () => + { + SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId)); - TryJoinGroup(groupInformation.id); - }, [ groupInformation ]); + if(onClose) onClose(); + }, null); + } - const leaveGroup = useCallback(() => - { - if(window.confirm(LocalizeText('group.leaveconfirm.desc'))) - { - SendMessageHook(new GroupRemoveMemberComposer(groupInformation.id, GetSessionDataManager().userId)); - if(onClose) onClose(); - } - }, [ groupInformation, onClose ]); - - const isRealOwner = useCallback(() => - { - if(!groupInformation) return false; - - return (groupInformation.ownerName === GetSessionDataManager().userName); - }, [ groupInformation ]); - - const getRoleIcon = useCallback(() => + const getRoleIcon = () => { if(groupInformation.membershipType === GroupMembershipType.NOT_MEMBER || groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return null; @@ -53,9 +46,9 @@ export const GroupInformationView: FC = props => if(groupInformation.isAdmin) return ; return ; - }, [ groupInformation, isRealOwner ]); + } - const getButtonText = useCallback(() => + const getButtonText = () => { if(groupInformation.type === GroupType.PRIVATE) return ''; @@ -71,16 +64,21 @@ export const GroupInformationView: FC = props => if(groupInformation.membershipType === GroupMembershipType.REQUEST_PENDING) return 'group.membershippending'; } - }, [ groupInformation, isRealOwner ]); + } - const handleButtonClick = useCallback(() => + const handleButtonClick = () => { - if(groupInformation.type === GroupType.PRIVATE && groupInformation.membershipType === GroupMembershipType.NOT_MEMBER) return; + if((groupInformation.type === GroupType.PRIVATE) && (groupInformation.membershipType === GroupMembershipType.NOT_MEMBER)) return; - if(groupInformation.membershipType === GroupMembershipType.MEMBER) return leaveGroup(); + if(groupInformation.membershipType === GroupMembershipType.MEMBER) + { + leaveGroup(); - return joinGroup(); - }, [ groupInformation, leaveGroup, joinGroup ]); + return; + } + + joinGroup(); + } const handleAction = useCallback((action: string) => { @@ -102,56 +100,52 @@ export const GroupInformationView: FC = props => CreateLinkEvent('catalog/open/' + CatalogPageName.GUILD_CUSTOM_FURNI); break; } - }, [ groupInformation, onClose ]); + }, [ groupInformation ]); if(!groupInformation) return null; return ( -
-
-
-
- -
-
handleAction('members') }> - { LocalizeText('group.membercount', ['totalMembers'], [groupInformation.membersCount.toString()]) } -
- { groupInformation.pendingRequestsCount > 0 &&
handleAction('members_pending') }> - { LocalizeText('group.pendingmembercount', ['amount'], [groupInformation.pendingRequestsCount.toString()]) } -
} - { groupInformation.isOwner &&
handleAction('manage') }> - { LocalizeText('group.manage') } -
} -
- { getRoleIcon() } -
-
-
-
-
- - { groupInformation.canMembersDecorate && } -
{ groupInformation.title }
-
-
{ LocalizeText('group.created', ['date', 'owner'], [groupInformation.createdAt, groupInformation.ownerName]) }
-
{ groupInformation.description }
-
-
handleAction('homeroom') }> - { LocalizeText('group.linktobase') } -
-
handleAction('furniture') }> - { LocalizeText('group.buyfurni') } -
-
- { LocalizeText('group.showgroups') } -
-
- { groupInformation.type !== GroupType.PRIVATE && - - } -
-
+ + + + + + + handleAction('members') }>{ LocalizeText('group.membercount', [ 'totalMembers' ], [ groupInformation.membersCount.toString() ]) } + { (groupInformation.pendingRequestsCount > 0) && + handleAction('members_pending') }>{ LocalizeText('group.pendingmembercount', [ 'amount' ], [ groupInformation.pendingRequestsCount.toString() ]) } } + { groupInformation.isOwner && + handleAction('manage') }>{ LocalizeText('group.manage') } } + + { getRoleIcon() } + + + + + + { groupInformation.title } + + + { groupInformation.canMembersDecorate && + } + + + { LocalizeText('group.created', ['date', 'owner'], [groupInformation.createdAt, groupInformation.ownerName]) } + + { groupInformation.description } + + + + handleAction('homeroom') }>{ LocalizeText('group.linktobase') } + handleAction('furniture') }>{ LocalizeText('group.buyfurni') } + { LocalizeText('group.showgroups') } + + { (groupInformation.type !== GroupType.PRIVATE) && + } + + + ); }; diff --git a/src/components/groups/views/manager/GroupManagerView.scss b/src/components/groups/views/manager/GroupManagerView.scss index 10c3159e..2dd3baa5 100644 --- a/src/components/groups/views/manager/GroupManagerView.scss +++ b/src/components/groups/views/manager/GroupManagerView.scss @@ -1,9 +1,4 @@ .nitro-group-manager { - width: 385px; - - .manager-tab { - height: 260px; - min-height: 260px; - max-height: 260px; - } + 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 index 11c21e4e..986a9d99 100644 --- a/src/components/groups/views/manager/GroupManagerView.tsx +++ b/src/components/groups/views/manager/GroupManagerView.tsx @@ -1,14 +1,15 @@ -import { GroupDeleteComposer, GroupSaveBadgeComposer, GroupSaveColorsComposer, GroupSaveInformationComposer, GroupSavePreferencesComposer } from '@nitrots/nitro-renderer'; +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 { GroupSharedTabBadgeView } from '../shared-tabs/GroupSharedTabBadgeView'; -import { GroupSharedTabColorsView } from '../shared-tabs/GroupSharedTabColorsView'; -import { GroupSharedTabIdentityView } from '../shared-tabs/GroupSharedTabIdentityView'; -import { GroupManagerTabSettingsView } from './tab-settings/GroupManagerTabSettingsView'; +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]; @@ -43,7 +44,7 @@ export const GroupManagerView: FC<{}> = props => }); SendMessageHook(new GroupSaveInformationComposer(groupId, groupName, groupDescription)); - SendMessageHook(new GroupSaveBadgeComposer(groupId, badge)); + //SendMessageHook(new GroupSaveBadgeComposer(groupId, badge)); SendMessageHook(new GroupSaveColorsComposer(groupId, groupColors[0], groupColors[1])); SendMessageHook(new GroupSavePreferencesComposer(groupId, groupState, groupCanMembersDecorate ? 0 : 1)); @@ -64,36 +65,36 @@ export const GroupManagerView: FC<{}> = props => 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 && } -
-
- - -
-
+ + { 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/manager/tab-settings/GroupManagerTabSettingsView.tsx b/src/components/groups/views/manager/tab-settings/GroupManagerTabSettingsView.tsx deleted file mode 100644 index 8a2c7244..00000000 --- a/src/components/groups/views/manager/tab-settings/GroupManagerTabSettingsView.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { FC, useCallback } from 'react'; -import { LocalizeText } from '../../../../../api/utils/LocalizeText'; -import { useGroupsContext } from '../../../GroupsContext'; -import { GroupsActions } from '../../../reducers/GroupsReducer'; - -const STATES: string[] = ['regular', 'exclusive', 'private']; - -export const GroupManagerTabSettingsView: FC<{}> = props => -{ - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { groupState = null, groupCanMembersDecorate = false } = groupsState; - - const setState = useCallback((state: number) => - { - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_STATE, - payload: { - numberValues: [ state ] - } - }) - }, [ dispatchGroupsState ]); - - const toggleCanMembersDecorate = useCallback(() => - { - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_CAN_MEMBERS_DECORATE, - payload: { - boolValues: [ !groupCanMembersDecorate ] - } - }) - }, [ dispatchGroupsState, groupCanMembersDecorate ]); - - return ( - <> - { STATES.map((state, index) => - { - return
- setState(index) } /> - -
{ LocalizeText(`group.edit.settings.type.${state}.help`) }
-
- }) } -
-
- toggleCanMembersDecorate() } /> - -
{ LocalizeText('group.edit.settings.rights.members.help') }
-
- - ); -}; diff --git a/src/components/groups/views/members/GroupMembersView.scss b/src/components/groups/views/members/GroupMembersView.scss deleted file mode 100644 index e8d4215d..00000000 --- a/src/components/groups/views/members/GroupMembersView.scss +++ /dev/null @@ -1,27 +0,0 @@ -.nitro-group-members { - width: 400px; - - .group-badge { - width: 50px; - height: 50px; - } - - .members-list { - .list-member { - border: 1px solid #ced4da; - - .avatar-head { - position: relative; - overflow: hidden; - width: 40px; - height: 50px; - - .avatar-image { - position: absolute; - left: -25px; - top: -20px; - } - } - } - } -} diff --git a/src/components/groups/views/members/GroupMembersView.tsx b/src/components/groups/views/members/GroupMembersView.tsx index 69272784..7de661be 100644 --- a/src/components/groups/views/members/GroupMembersView.tsx +++ b/src/components/groups/views/members/GroupMembersView.tsx @@ -1,9 +1,12 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GroupAdminGiveComposer, GroupAdminTakeComposer, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupMemberParser, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembersParser, GroupRank, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer'; import classNames from 'classnames'; -import { FC, KeyboardEvent, useCallback, useEffect, useState } from 'react'; +import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; import { GetSessionDataManager, GetUserProfile, LocalizeText } from '../../../../api'; -import { CreateMessageHook, SendMessageHook } from '../../../../hooks'; +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'; @@ -11,90 +14,31 @@ interface GroupMembersViewProps { groupId: number; levelId: number; + setLevelId: Dispatch>; onClose: () => void; } - export const GroupMembersView: FC = props => { - const { groupId = null, levelId = null, onClose = null } = props; - + const { groupId = -1, levelId = -1, setLevelId = null, onClose = null } = props; const [ pageData, setPageData ] = useState(null); + const [ pageId, setPageId ] = useState(-1); const [ searchQuery, setSearchQuery ] = useState(''); - const [ searchLevelId, setSearchLevelId ] = useState(3); const [ totalPages, setTotalPages ] = useState(0); const [ removingMemberName, setRemovingMemberName ] = useState(null); - const searchMembers = useCallback((pageId: number, newLevelId?: number) => + const refreshMembers = useCallback(() => { - if(!groupId) return; - - SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, newLevelId !== undefined ? newLevelId : searchLevelId)); - }, [ groupId, searchQuery, searchLevelId ]); + if((groupId === -1) || (levelId === -1) || (pageId === -1)) return; - const onGroupMembersEvent = useCallback((event: GroupMembersEvent) => - { - const parser = event.getParser(); - - setPageData(null); - setPageData(parser); - setSearchLevelId(parser.level); - setTotalPages(Math.ceil(parser.totalMembersCount / parser.pageSize)); - }, []); + SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, levelId)); + }, [ groupId, levelId, pageId, searchQuery ]); - const onGroupConfirmMemberRemoveEvent = useCallback((event: GroupConfirmMemberRemoveEvent) => - { - const parser = event.getParser(); + const previousPage = () => setPageId(prevValue => (prevValue - 1)); - if(window.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)); - searchMembers(pageData.pageIndex); - } + const nextPage = () => setPageId(prevValue => (prevValue + 1)); - setRemovingMemberName(null); - }, [ pageData, removingMemberName, searchMembers ]); - - CreateMessageHook(GroupMembersEvent, onGroupMembersEvent); - CreateMessageHook(GroupConfirmMemberRemoveEvent, onGroupConfirmMemberRemoveEvent); - - useEffect(() => - { - setPageData(null); - setSearchQuery(''); - setSearchLevelId(0); - - if(!groupId) return; - - if(levelId !== null) setSearchLevelId(levelId); - - searchMembers(0, levelId); - }, [ groupId, levelId ]); - - const selectSearchLevelId = useCallback((level: number) => - { - setSearchLevelId(level); - searchMembers(0, level); - }, [ searchMembers ]); - - const previousPage = useCallback(() => - { - searchMembers(pageData.pageIndex - 1); - }, [ searchMembers, pageData ]); - - const nextPage = useCallback(() => - { - searchMembers(pageData.pageIndex + 1); - }, [ searchMembers, pageData ]); - - const onKeyDown = useCallback((event: KeyboardEvent) => - { - if(event.key !== 'Enter') return; - - searchMembers(pageData.pageIndex); - }, [ searchMembers, pageData ]); - - const getRankDescription = useCallback((member: GroupMemberParser) => + const getRankDescription = (member: GroupMemberParser) => { if(member.rank === GroupRank.OWNER) return 'group.members.owner'; @@ -106,107 +50,151 @@ export const GroupMembersView: FC = props => } return ''; - }, [ pageData ]); + } - const toggleAdmin = useCallback((member: GroupMemberParser) => + 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(pageData.groupId, member.id)); + else SendMessageHook(new GroupAdminTakeComposer(pageData.groupId, member.id)); - searchMembers(pageData.pageIndex); - }, [ pageData, searchMembers ]); + refreshMembers(); + } - const acceptMembership = useCallback((member) => + const acceptMembership = (member: GroupMemberParser) => { - if(member.rank === GroupRank.REQUESTED) - { - SendMessageHook(new GroupMembershipAcceptComposer(pageData.groupId, member.id)); - searchMembers(pageData.pageIndex); - } - }, [ pageData, searchMembers ]); + if(member.rank !== GroupRank.REQUESTED) return; + + SendMessageHook(new GroupMembershipAcceptComposer(pageData.groupId, member.id)); - const removeMemberOrDeclineMembership = useCallback((member) => + refreshMembers(); + } + + const removeMemberOrDeclineMembership = (member: GroupMemberParser) => { if(member.rank === GroupRank.REQUESTED) { SendMessageHook(new GroupMembershipDeclineComposer(pageData.groupId, member.id)); - searchMembers(pageData.pageIndex); - } - else - { - setRemovingMemberName(member.name); - SendMessageHook(new GroupConfirmRemoveMemberComposer(pageData.groupId, member.id)); - } - }, [ pageData, searchMembers ]); - if(!groupId) return null; + refreshMembers(); + + return; + } + + setRemovingMemberName(member.name); + SendMessageHook(new GroupConfirmRemoveMemberComposer(pageData.groupId, member.id)); + } + + const onGroupMembersEvent = useCallback((event: GroupMembersEvent) => + { + const parser = event.getParser(); + + BatchUpdates(() => + { + setPageData(parser); + //setSearchLevelId(parser.level); + setTotalPages(Math.ceil(parser.totalMembersCount / parser.pageSize)); + }); + }, []); + + CreateMessageHook(GroupMembersEvent, onGroupMembersEvent); + + const onGroupConfirmMemberRemoveEvent = useCallback((event: GroupConfirmMemberRemoveEvent) => + { + const parser = event.getParser(); + + 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)); + + refreshMembers(); + }, null); + + setRemovingMemberName(null); + }, [ pageData, removingMemberName, refreshMembers ]); + + CreateMessageHook(GroupConfirmMemberRemoveEvent, onGroupConfirmMemberRemoveEvent); + + useEffect(() => + { + setPageId(0); + }, [ groupId, levelId, searchQuery ]); + + useEffect(() => + { + + }) + + useEffect(() => + { + if((groupId === -1) || (levelId === -1) || (pageId === -1)) return; + + SendMessageHook(new GroupMembersComposer(groupId, pageId, searchQuery, levelId)); + }, [ groupId, levelId, pageId, searchQuery ]); + + if(!groupId || !pageData) return null; return ( - + - { pageData && <> - -
-
- -
-
- setSearchQuery(e.target.value) } onBlur={ () => searchMembers(pageData.pageIndex) } onKeyDown={ onKeyDown } /> - -
-
-
- { pageData.result.map((member, index) => + + + + + + + setSearchQuery(event.target.value) } /> + + + + + { pageData.result.map((member, index) => { return ( -
-
-
{ GetUserProfile(member.id) } }> - -
-
-
{ GetUserProfile(member.id) } }>{ member.name }
- { member.rank !== GroupRank.REQUESTED &&
{ LocalizeText('group.members.since', ['date'], [member.joinedAt]) }
} -
-
-
- toggleAdmin(member) } /> -
- { member.rank === GroupRank.REQUESTED &&
- acceptMembership(member) } /> -
} - { member.rank !== GroupRank.OWNER && pageData.admin && member.id !== GetSessionDataManager().userId &&
- removeMemberOrDeclineMembership(member) } /> -
} -
+ +
GetUserProfile(member.id) }> +
-
+ + GetUserProfile(member.id) }>{ member.name } + { (member.rank !== GroupRank.REQUESTED) && + { LocalizeText('group.members.since', ['date'], [member.joinedAt]) } } + + + { (member.rank !== GroupRank.REQUESTED) && + + toggleAdmin(member) } /> + } + { (member.rank === GroupRank.REQUESTED) && + + acceptMembership(member) }> + } + { (member.rank !== GroupRank.OWNER) && pageData.admin && (member.id !== GetSessionDataManager().userId) && + + removeMemberOrDeclineMembership(member) }> + } + + ); }) } -
-
-
- -
-
{ LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [pageData.totalMembersCount.toString(), (pageData.pageIndex + 1).toString(), totalPages.toString()]) }
-
- -
-
-
- } + + + + + { LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [pageData.totalMembersCount.toString(), (pageData.pageIndex + 1).toString(), totalPages.toString()]) } + + + + ); }; diff --git a/src/components/groups/views/shared-tabs/GroupSharedTabBadgeView.tsx b/src/components/groups/views/shared-tabs/GroupSharedTabBadgeView.tsx deleted file mode 100644 index 5e9fc7a5..00000000 --- a/src/components/groups/views/shared-tabs/GroupSharedTabBadgeView.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import classNames from 'classnames'; -import { FC, useCallback, useEffect, useState } from 'react'; -import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView'; -import { GroupBadgePart } from '../../common/GroupBadgePart'; -import { useGroupsContext } from '../../GroupsContext'; -import { GroupsActions } from '../../reducers/GroupsReducer'; - -const POSITIONS: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - -interface GroupSharedTabBadgeViewProps -{ - skipDefault?: boolean; -} - -export const GroupSharedTabBadgeView: FC = props => -{ - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { badgeBases = null, badgeSymbols = null, badgePartColors = null, groupBadgeParts = null } = groupsState; - - const { skipDefault = null } = props; - - const [ editingIndex, setEditingIndex ] = useState(0); - const [ isSelectingModel, setIsSelectingModel ] = useState(false); - - useEffect(() => - { - if(skipDefault || !badgeBases || !badgePartColors || groupBadgeParts) return; - - const badgeParts: GroupBadgePart[] = [ - 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) - ]; - - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_BADGE_PARTS, - payload: { badgeParts } - }); - - }, [ badgeBases, badgePartColors, groupBadgeParts ]); - - const switchIndex = useCallback((index: number) => - { - setIsSelectingModel(false); - setEditingIndex(index); - }, []); - - const selectPartProperty = useCallback((property: string, key: number) => - { - const clonedBadgeParts = Array.from(groupBadgeParts); - - clonedBadgeParts[editingIndex][property] = key; - - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_BADGE_PARTS, - payload: { - badgeParts: clonedBadgeParts - } - }); - - if(property === 'key') setIsSelectingModel(false); - }, [ editingIndex, groupBadgeParts, dispatchGroupsState ]); - - const getCompleteBadgeCode = useCallback(() => - { - let code = ''; - - if(!groupBadgeParts) return code; - - groupBadgeParts.forEach((badgePart) => - { - if(badgePart.code) code = code + badgePart.code; - }); - - return code; - }, [ groupBadgeParts ]); - - const getCurrentPart = useCallback((property: string) => - { - return groupBadgeParts[editingIndex][property]; - }, [ groupBadgeParts, editingIndex ]); - - return ( -
-
-
- -
-
-
- { groupBadgeParts && groupBadgeParts.map((badgePart, partIndex) => - { - return ( -
switchIndex(partIndex) }> - { badgePart.code && } - { !badgePart.code && } -
- ) - }) } -
- { !isSelectingModel && groupBadgeParts &&
-
setIsSelectingModel(true) }> - { getCurrentPart('code') && } - { !getCurrentPart('code') && } -
-
-
- { POSITIONS.map((position) => - { - return
selectPartProperty('position', position) }>
- }) } -
-
-
-
- { badgePartColors && badgePartColors.map((item, index) => - { - return
selectPartProperty('color', item.id) }>
- }) } -
-
-
} - { isSelectingModel &&
- { groupBadgeParts[editingIndex].type !== GroupBadgePart.BASE && <> -
selectPartProperty('key', 0) }> - -
- } - { (groupBadgeParts[editingIndex].type === GroupBadgePart.BASE ? badgeBases : badgeSymbols).map((item, index) => - { - return
selectPartProperty('key', item.id) }> - -
- }) } -
} -
-
-
); -}; diff --git a/src/components/groups/views/shared-tabs/GroupSharedTabColorsView.tsx b/src/components/groups/views/shared-tabs/GroupSharedTabColorsView.tsx deleted file mode 100644 index 490e8079..00000000 --- a/src/components/groups/views/shared-tabs/GroupSharedTabColorsView.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import classNames from 'classnames'; -import { FC, useCallback, useEffect, useState } from 'react'; -import { LocalizeText } from '../../../../api'; -import { useGroupsContext } from '../../GroupsContext'; -import { GroupsActions } from '../../reducers/GroupsReducer'; - -export const GroupSharedTabColorsView: FC<{}> = props => -{ - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { groupColors = null, groupColorsA = null, groupColorsB = null } = groupsState; - - const [ selectingColorIndex, setSelectingColorIndex ] = useState(0); - - useEffect(() => - { - if(!groupColorsA || !groupColorsB || groupColors) return; - - const colors: number[] = [ - groupColorsA[0].id, - groupColorsB[0].id - ]; - - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_COLORS, - payload: { - objectValues: colors - } - }); - - }, [dispatchGroupsState, groupColors, groupColorsA, groupColorsB]); - - 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 ]); - - const selectColor = useCallback((colorId: number) => - { - const clonedGroupColors = Array.from(groupColors); - - clonedGroupColors[selectingColorIndex] = colorId; - - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_COLORS, - payload: { - objectValues: clonedGroupColors - } - }); - }, [ selectingColorIndex, groupColors, dispatchGroupsState ]); - - return ( -
-
-
{ LocalizeText('group.edit.color.guild.color') }
- { groupColors &&
-
-
-
} -
- { selectingColorIndex === 0 &&
-
{ LocalizeText('group.edit.color.primary.color') }
-
-
- { groupColors && groupColorsA && groupColorsA.map((item, index) => - { - return
selectColor(item.id) }>
- }) } -
-
setSelectingColorIndex(1) } />
-
-
} - { selectingColorIndex === 1 &&
-
{ LocalizeText('group.edit.color.secondary.color') }
-
-
setSelectingColorIndex(0) } />
-
- { groupColorsB && groupColorsB.map((item, index) => - { - return
selectColor(item.id) }>
- }) } -
-
-
} -
- ); -}; diff --git a/src/components/groups/views/shared-tabs/GroupSharedTabIdentityView.tsx b/src/components/groups/views/shared-tabs/GroupSharedTabIdentityView.tsx deleted file mode 100644 index 7181a7d0..00000000 --- a/src/components/groups/views/shared-tabs/GroupSharedTabIdentityView.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { FC, useCallback } from 'react'; -import { CreateLinkEvent, LocalizeText } from '../../../../api'; -import { useGroupsContext } from '../../GroupsContext'; -import { GroupsActions } from '../../reducers/GroupsReducer'; - -interface GroupSharedTabIdentityViewProps -{ - isCreator?: boolean; -} - -export const GroupSharedTabIdentityView: FC = props => -{ - const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); - const { groupName = '', groupDescription = '', groupHomeroomId = 0, availableRooms = null } = groupsState; - - const { isCreator = false } = props; - - const setName = useCallback((name: string) => - { - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_NAME, - payload: { - stringValues: [ name ] - } - }) - }, [ dispatchGroupsState ]); - - const setDescription = useCallback((description: string) => - { - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_DESCRIPTION, - payload: { - stringValues: [ description ] - } - }) - }, [ dispatchGroupsState ]); - - const setHomeroomId = useCallback((id: number) => - { - dispatchGroupsState({ - type: GroupsActions.SET_GROUP_HOMEROOM_ID, - payload: { - numberValues: [ id ] - } - }) - }, [ dispatchGroupsState ]); - - return (
-
- - setName(e.target.value) } /> -
-
- - setDescription(e.target.value) } /> -
- { isCreator && <> -
- - -
-
{ LocalizeText('group.edit.base.warning') }
-
CreateLinkEvent('navigator/create') }>{ LocalizeText('group.createroom') }
- } -
); -}; diff --git a/src/components/groups/views/shared-tabs/GroupSharedTabs.scss b/src/components/groups/views/shared-tabs/GroupSharedTabs.scss deleted file mode 100644 index 6c13623c..00000000 --- a/src/components/groups/views/shared-tabs/GroupSharedTabs.scss +++ /dev/null @@ -1,79 +0,0 @@ -.nitro-groups { - .badge-preview { - position: relative; - width: 50px; - height: 50px; - background: $white; - border-radius: $border-radius; - - &.active { - &:before { - position: absolute; - content: ' '; - width: 0; - height: 0; - border-top: 10px solid white; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - bottom: -10px; - } - } - } - - .group-color-swatch { - width: 30px; - height: 40px; - } -} - -.shared-tab-badge { - .position-swatch { - position: relative; - border-radius: $border-radius; - width: 16px; - height: 16px; - background: $white; - border: 2px solid $white; - box-shadow: inset 3px 3px rgba(0, 0, 0, .1); - - &.active { - background: $primary; - box-shadow: none; - } - } - - .color-swatch { - position: relative; - border-radius: $border-radius; - width: 16px; - height: 16px; - border: 2px solid $white; - box-shadow: inset 3px 3px rgba(0, 0, 0, .1); - - &.active { - box-shadow: none; - } - } - - .selection-list { - height: 160px; - min-height: 160px; - max-height: 160px; - } -} - -.shared-tab-colors { - - .color-swatch { - position: relative; - border-radius: $border-radius; - width: 15px; - height: 15px; - border: 2px solid $white; - box-shadow: inset 2px 2px rgba(0, 0, 0, .2); - - &.active { - box-shadow: none; - } - } -} diff --git a/src/components/groups/views/tabs/GroupSharedTabs.scss b/src/components/groups/views/tabs/GroupSharedTabs.scss new file mode 100644 index 00000000..c1657df1 --- /dev/null +++ b/src/components/groups/views/tabs/GroupSharedTabs.scss @@ -0,0 +1,7 @@ +.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 new file mode 100644 index 00000000..7b790fba --- /dev/null +++ b/src/components/groups/views/tabs/GroupTabBadgeView.tsx @@ -0,0 +1,111 @@ +import { GroupSaveBadgeComposer } from '@nitrots/nitro-renderer'; +import { FC, 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 { 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; +} + +export const GroupTabBadgeView: FC = props => +{ + const { 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 getModifiedBadgeCode = () => + { + if(!badgeParts || !badgeParts.length) return ''; + + let badgeCode = ''; + + badgeParts.forEach(part => (part.code && (badgeCode += part.code))); + + return badgeCode; + } + + useEffect(() => + { + 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) + ]; + + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_BADGE_PARTS, + payload: { badgeParts } + }); + }, [ groupBadgeParts, badgeBases, badgePartColors, dispatchGroupsState ]); + + useEffect(() => + { + setBadgeParts(groupBadgeParts); + }, [ groupBadgeParts ]); + + useEffect(() => + { + if((groupId <= 0) || !badgeParts || !badgeParts.length || !groupBadgeParts || !groupBadgeParts.length || (badgeParts === groupBadgeParts)) return; + + const badge = []; + + badgeParts.forEach((part) => + { + if(!part.code) return; + + badge.push(part.key); + badge.push(part.color); + badge.push(part.position); + }); + + console.log('send') + + SendMessageHook(new GroupSaveBadgeComposer(groupId, badge)); + }, [ groupId, badgeParts, groupBadgeParts ]); + + // useEffect(() => + // { + // if((groupId <= 0) || !badgeParts || !badgeParts.length || (badgeParts === groupBadgeParts)) return; + + // const badge = []; + + // badgeParts.forEach((part) => + // { + // if(!part.code) return; + + // badge.push(part.key); + // badge.push(part.color); + // badge.push(part.position); + // }); + + // console.log('send') + + // SendMessageHook(new GroupSaveBadgeComposer(groupId, badge)); + // }, [ groupId, groupBadgeParts, badgeParts ]); + + return ( + + + + + + + + + + + ); +}; diff --git a/src/components/groups/views/tabs/GroupTabColorsView.tsx b/src/components/groups/views/tabs/GroupTabColorsView.tsx new file mode 100644 index 00000000..364da6b0 --- /dev/null +++ b/src/components/groups/views/tabs/GroupTabColorsView.tsx @@ -0,0 +1,81 @@ +import classNames from 'classnames'; +import { FC, useEffect } from 'react'; +import { LocalizeText } from '../../../../api'; +import { AutoGrid, Base, Column, Flex, Grid, Text } from '../../../../common'; +import { useGroupsContext } from '../../GroupsContext'; +import { GroupsActions } from '../../reducers/GroupsReducer'; + +export const GroupTabColorsView: FC<{}> = props => +{ + const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); + const { groupColors = null, groupColorsA = null, groupColorsB = null } = groupsState; + + const getGroupColor = (colorIndex: number) => + { + if(colorIndex === 0) return groupColorsA.find(color => (color.id === groupColors[colorIndex])).color; + + return groupColorsB.find(color => (color.id === groupColors[colorIndex])).color; + } + + const selectColor = (colorIndex: number, colorId: number) => + { + const clonedGroupColors = Array.from(groupColors); + + clonedGroupColors[colorIndex] = colorId; + + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_COLORS, + payload: { + objectValues: clonedGroupColors + } + }); + } + + useEffect(() => + { + if(!groupColorsA || !groupColorsB || groupColors) return; + + const colors: number[] = [ + groupColorsA[0].id, + groupColorsB[0].id + ]; + + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_COLORS, + payload: { + objectValues: colors + } + }); + }, [ dispatchGroupsState, groupColors, groupColorsA, groupColorsB ]); + + return ( + + + { LocalizeText('group.edit.color.guild.color') } + { groupColors && (groupColors.length > 0) && + + + + } + + + { LocalizeText('group.edit.color.primary.color') } + + { groupColors && groupColorsA && groupColorsA.map((item, index) => + { + return
selectColor(0, item.id) }>
+ }) } +
+
+ + { LocalizeText('group.edit.color.secondary.color') } + + { groupColorsB && groupColorsB.map((item, index) => + { + return
selectColor(1, item.id) }>
+ }) } +
+
+
+ ); +}; diff --git a/src/components/groups/views/tabs/GroupTabIdentityView.tsx b/src/components/groups/views/tabs/GroupTabIdentityView.tsx new file mode 100644 index 00000000..1ed74d7e --- /dev/null +++ b/src/components/groups/views/tabs/GroupTabIdentityView.tsx @@ -0,0 +1,75 @@ +import { FC } from 'react'; +import { CreateLinkEvent, LocalizeText } from '../../../../api'; +import { Column, Flex, Text } from '../../../../common'; +import { useGroupsContext } from '../../GroupsContext'; +import { GroupsActions } from '../../reducers/GroupsReducer'; + +interface GroupTabIdentityViewProps +{ + isCreator?: boolean; +} + +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) => + { + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_NAME, + payload: { + stringValues: [ name ] + } + }) + } + + const setDescription = (description: string) => + { + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_DESCRIPTION, + payload: { + stringValues: [ description ] + } + }) + } + + const setHomeroomId = (id: number) => + { + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_HOMEROOM_ID, + payload: { + numberValues: [ id ] + } + }) + } + + return ( + <> + + + { LocalizeText('group.edit.name') } + setName(event.target.value) } /> + + + { LocalizeText('group.edit.desc') } +