diff --git a/src/assets/images/groups/creator_images.png b/src/assets/images/groups/creator_images.png new file mode 100644 index 00000000..b39b0d7b Binary files /dev/null and b/src/assets/images/groups/creator_images.png differ diff --git a/src/assets/images/groups/creator_tabs.png b/src/assets/images/groups/creator_tabs.png new file mode 100644 index 00000000..e95b9f0c Binary files /dev/null and b/src/assets/images/groups/creator_tabs.png differ diff --git a/src/assets/images/groups/group_decorate_icon.png b/src/assets/images/groups/icons/group_decorate_icon.png similarity index 100% rename from src/assets/images/groups/group_decorate_icon.png rename to src/assets/images/groups/icons/group_decorate_icon.png diff --git a/src/assets/images/groups/group_icon_big_admin.png b/src/assets/images/groups/icons/group_icon_big_admin.png similarity index 100% rename from src/assets/images/groups/group_icon_big_admin.png rename to src/assets/images/groups/icons/group_icon_big_admin.png diff --git a/src/assets/images/groups/group_icon_big_member.png b/src/assets/images/groups/icons/group_icon_big_member.png similarity index 100% rename from src/assets/images/groups/group_icon_big_member.png rename to src/assets/images/groups/icons/group_icon_big_member.png diff --git a/src/assets/images/groups/group_icon_big_owner.png b/src/assets/images/groups/icons/group_icon_big_owner.png similarity index 100% rename from src/assets/images/groups/group_icon_big_owner.png rename to src/assets/images/groups/icons/group_icon_big_owner.png diff --git a/src/assets/images/groups/grouptype_icon_0.png b/src/assets/images/groups/icons/grouptype_icon_0.png similarity index 100% rename from src/assets/images/groups/grouptype_icon_0.png rename to src/assets/images/groups/icons/grouptype_icon_0.png diff --git a/src/assets/images/groups/grouptype_icon_1.png b/src/assets/images/groups/icons/grouptype_icon_1.png similarity index 100% rename from src/assets/images/groups/grouptype_icon_1.png rename to src/assets/images/groups/icons/grouptype_icon_1.png diff --git a/src/assets/images/groups/grouptype_icon_2.png b/src/assets/images/groups/icons/grouptype_icon_2.png similarity index 100% rename from src/assets/images/groups/grouptype_icon_2.png rename to src/assets/images/groups/icons/grouptype_icon_2.png diff --git a/src/assets/styles/icons.scss b/src/assets/styles/icons.scss index fb6146f6..889f6c82 100644 --- a/src/assets/styles/icons.scss +++ b/src/assets/styles/icons.scss @@ -550,43 +550,43 @@ } &.icon-group-type-0 { - background: url('../images/groups/grouptype_icon_0.png'); + background: url('../images/groups/icons/grouptype_icon_0.png'); width: 16px; height: 16px; } &.icon-group-type-1 { - background: url('../images/groups/grouptype_icon_1.png'); + background: url('../images/groups/icons/grouptype_icon_1.png'); width: 16px; height: 16px; } &.icon-group-type-2 { - background: url('../images/groups/grouptype_icon_2.png'); + background: url('../images/groups/icons/grouptype_icon_2.png'); width: 16px; height: 16px; } &.icon-group-decorate { - background: url('../images/groups/group_decorate_icon.png'); + background: url('../images/groups/icons/group_decorate_icon.png'); width: 15px; height: 15px; } &.icon-group-member { - background: url('../images/groups/group_icon_big_member.png'); + background: url('../images/groups/icons/group_icon_big_member.png'); width: 20px; height: 20px; } &.icon-group-admin { - background: url('../images/groups/group_icon_big_admin.png'); + background: url('../images/groups/icons/group_icon_big_admin.png'); width: 20px; height: 20px; } &.icon-group-owner { - background: url('../images/groups/group_icon_big_owner.png'); + background: url('../images/groups/icons/group_icon_big_owner.png'); width: 20px; height: 20px; } diff --git a/src/views/groups/GroupView.scss b/src/views/groups/GroupView.scss index 44eafba0..acfe8f77 100644 --- a/src/views/groups/GroupView.scss +++ b/src/views/groups/GroupView.scss @@ -1,3 +1,4 @@ +@import './views/creator/GroupCreatorView'; @import './views/information/GroupInformationView'; @import './views/information-standalone/GroupInformationStandaloneView'; @import './views/room-information/GroupRoomInformationView'; diff --git a/src/views/groups/GroupsView.tsx b/src/views/groups/GroupsView.tsx index 85cc0cb7..bdfc443a 100644 --- a/src/views/groups/GroupsView.tsx +++ b/src/views/groups/GroupsView.tsx @@ -1,16 +1,19 @@ import { GroupBadgePartsComposer, ILinkEventTracker } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useEffect, useReducer } from 'react'; +import { FC, useCallback, useEffect, useReducer, useState } from 'react'; import { AddEventLinkTracker, RemoveLinkEventTracker } from '../../api'; import { SendMessageHook } from '../../hooks'; import { GroupsContextProvider } from './context/GroupsContext'; import { GroupsReducer, initialGroups } from './context/GroupsContext.types'; import { GroupsMessageHandler } from './GroupsMessageHandler'; +import { GroupCreatorView } from './views/creator/GroupCreatorView'; import { GroupInformationStandaloneView } from './views/information-standalone/GroupInformationStandaloneView'; export const GroupsView: FC<{}> = props => { const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups); + const [ isCreatorVisible, setIsCreatorVisible ] = useState(false); + useEffect(() => { SendMessageHook(new GroupBadgePartsComposer()); @@ -25,6 +28,7 @@ export const GroupsView: FC<{}> = props => switch(parts[1]) { case 'create': + setIsCreatorVisible(true); return; } }, []); @@ -44,6 +48,7 @@ export const GroupsView: FC<{}> = props => return ( + setIsCreatorVisible(false) } /> ); diff --git a/src/views/groups/common/GroupBadgePart.ts b/src/views/groups/common/GroupBadgePart.ts new file mode 100644 index 00000000..03338223 --- /dev/null +++ b/src/views/groups/common/GroupBadgePart.ts @@ -0,0 +1,26 @@ +export class GroupBadgePart +{ + public static BASE: string = 'b'; + public static SYMBOL: string = 's'; + public static SYMBOL_ALT: string = 't'; + + public type: string; + public key: number; + public color: number; + public position: number; + + constructor(type: string) + { + this.type = type; + this.key = 0; + this.color = 0; + this.position = 4; + } + + public get code(): string + { + if(this.key === 0) return null; + + return this.type + (this.key < 10 ? '0' : '') + this.key + (this.color < 10 ? '0' : '') + this.color + (this.type === GroupBadgePart.BASE ? '' : this.position); + } +} diff --git a/src/views/groups/common/GroupSettings.ts b/src/views/groups/common/GroupSettings.ts new file mode 100644 index 00000000..cf1e4ae3 --- /dev/null +++ b/src/views/groups/common/GroupSettings.ts @@ -0,0 +1,172 @@ +import { GroupBadgePart } from './GroupBadgePart'; + +export class GroupSettings +{ + private _id: number; + private _name: string; + private _description: string; + private _roomId: string; + + private _badgeParts: GroupBadgePart[]; + private _colorA: number; + private _colorB: number; + + private _state: number; + private _canMembersDecorate: boolean; + + constructor() + { + this._id = 0; + this._name = ''; + this._description = ''; + this._roomId = '0'; + + this._badgeParts = []; + this._colorA = 0; + this.colorB = 0; + + this._state = 0; + this._canMembersDecorate = false; + + this._badgeParts.push(new GroupBadgePart(GroupBadgePart.BASE)); + this._badgeParts.push(new GroupBadgePart(GroupBadgePart.SYMBOL)); + this._badgeParts.push(new GroupBadgePart(GroupBadgePart.SYMBOL)); + this._badgeParts.push(new GroupBadgePart(GroupBadgePart.SYMBOL)); + this._badgeParts.push(new GroupBadgePart(GroupBadgePart.SYMBOL)); + } + + public getBadgePart(index: number): GroupBadgePart + { + return this._badgeParts[index]; + } + + public setPartsColor(color: number): void + { + this._badgeParts.forEach((symbol) => + { + symbol.color = color; + }); + } + + public get id(): number + { + return this._id; + } + + public set id(id: number) + { + this._id = id; + } + + public get name(): string + { + return this._name; + } + + public set name(name: string) + { + this._name = name; + } + + public get description(): string + { + return this._description; + } + + public set description(description: string) + { + this._description = description; + } + + public get roomId(): string + { + return this._roomId; + } + + public set roomId(id: string) + { + this._roomId = id; + } + + public get badgeParts(): GroupBadgePart[] + { + return this._badgeParts; + } + + public set badgeParts(parts: GroupBadgePart[]) + { + this._badgeParts = parts; + } + + public get colorA(): number + { + return this._colorA; + } + + public set colorA(id: number) + { + this._colorA = id; + } + + public get colorB(): number + { + return this._colorB; + } + + public set colorB(id: number) + { + this._colorB = id; + } + + public get currentBadgeCode(): string + { + let code = ''; + + this._badgeParts.forEach((part) => + { + if(part.code) + { + code = code + part.code; + } + }); + + return code; + } + + public get currentBadgeArray(): number[] + { + const badge = []; + + this._badgeParts.forEach((part) => + { + if(part.code) + { + badge.push(part.key); + badge.push(part.color); + badge.push(part.position); + } + }); + + return badge; + } + + public get state(): number + { + return this._state; + } + + public set state(state: number) + { + this._state = state; + } + + public get canMembersDecorate(): boolean + { + return this._canMembersDecorate; + } + + public set canMembersDecorate(value: boolean) + { + this._canMembersDecorate = value; + } +} diff --git a/src/views/groups/context/GroupsContext.types.ts b/src/views/groups/context/GroupsContext.types.ts index 1bc4b089..dcef1768 100644 --- a/src/views/groups/context/GroupsContext.types.ts +++ b/src/views/groups/context/GroupsContext.types.ts @@ -18,20 +18,29 @@ export interface IGroupsState badgePartColors: Map; groupColorsA: Map; groupColorsB: Map; + groupName: string; + groupDescription: string; + groupHomeroomId: number; } export interface IGroupsAction { type: string; - payload: { + payload?: { arrayMaps?: Map[]; stringMaps?: Map[]; + stringValue?: string; + numberValue?: number; } } export class GroupsActions { public static SET_BADGE_PARTS: string = 'GA_SET_BADGE_PARTS'; + 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 RESET_GROUP_SETTINGS: string = 'GA_RESET_GROUP_SETTINGS'; } export const initialGroups: IGroupsState = { @@ -39,7 +48,10 @@ export const initialGroups: IGroupsState = { badgeSymbols: null, badgePartColors: null, groupColorsA: null, - groupColorsB: null + groupColorsB: null, + groupName: '', + groupDescription: '', + groupHomeroomId: 0 }; export const GroupsReducer: Reducer = (state, action) => @@ -55,6 +67,28 @@ export const GroupsReducer: Reducer = (state, actio return { ...state, badgeBases, badgeSymbols, badgePartColors, groupColorsA, groupColorsB }; } + case GroupsActions.SET_GROUP_NAME: { + const groupName = action.payload.stringValue; + + return { ...state, groupName }; + } + case GroupsActions.SET_GROUP_DESCRIPTION: { + const groupDescription = action.payload.stringValue; + + return { ...state, groupDescription }; + } + case GroupsActions.SET_GROUP_HOMEROOM_ID: { + const groupHomeroomId = action.payload.numberValue; + + return { ...state, groupHomeroomId }; + } + case GroupsActions.RESET_GROUP_SETTINGS: { + const groupName = ''; + const groupDescription = ''; + const groupHomeroomId = 0; + + return { ...state, groupName, groupDescription, groupHomeroomId }; + } default: return state; } diff --git a/src/views/groups/views/creator/GroupCreatorView.scss b/src/views/groups/views/creator/GroupCreatorView.scss new file mode 100644 index 00000000..e1c376ab --- /dev/null +++ b/src/views/groups/views/creator/GroupCreatorView.scss @@ -0,0 +1,115 @@ +.nitro-group-creator { + width: 433px; + + .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; + + div { + position: absolute; + left: 50%; + transform: translateX(-50%); + } + + &:first-child { + margin-left: 0px; + } + + &.tab-blue-flat { + width: 84px; + height: 24px; + background-position: 0px 0px; + + div { + margin-top: 1px; + } + + &.active { + height: 28px; + background-position: 0px -24px; + } + } + + &.tab-blue-arrow { + width: 83px; + height: 24px; + background-position: 0px -52px; + + div { + margin-top: 1px; + } + + &.active { + height: 28px; + background-position: 0px -76px; + } + } + + &.tab-yellow { + width: 133px; + height: 28px; + background-position: 0px -104px; + + div { + margin-top: 3px; + } + + &.active { + height: 33px; + background-position: 0px -132px; + } + } + } + } + + .tab-image { + width: 122px; + height: 68px; + display: flex; + align-items: center; + justify-content: center; + + div { + background-image: url('../../../../assets/images/groups/creator_images.png'); + background-repeat: no-repeat; + } + + &.tab-1 { + div { + background-position: 0px 0px; + width: 99px; + 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 { + div { + background-position: 0px -95px; + width: 114px; + height: 61px; + } + } + } +} diff --git a/src/views/groups/views/creator/GroupCreatorView.tsx b/src/views/groups/views/creator/GroupCreatorView.tsx new file mode 100644 index 00000000..a62a3969 --- /dev/null +++ b/src/views/groups/views/creator/GroupCreatorView.tsx @@ -0,0 +1,111 @@ +import { GroupBuyDataComposer, GroupBuyDataEvent } from '@nitrots/nitro-renderer'; +import classNames from 'classnames'; +import { FC, useCallback, useEffect, useState } from 'react'; +import { Button } from 'react-bootstrap'; +import { LocalizeText } from '../../../../api'; +import { CreateMessageHook, SendMessageHook } from '../../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { useGroupsContext } from '../../context/GroupsContext'; +import { GroupsActions } from '../../context/GroupsContext.types'; +import { GroupCreatorViewProps } from './GroupCreatorView.types'; +import { GroupCreatorTabIdentityView } from './views/creator-tab-identity/GroupCreatorTabIdentityView'; + +const TABS: number[] = [1, 2, 3, 4]; + +export const GroupCreatorView: FC = props => +{ + const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); + const { groupName = null, groupHomeroomId = null } = groupsState; + + const { isVisible = false, onClose = null } = props; + + const [ currentTab, setCurrentTab ] = useState(1); + const [ availableRooms, setAvailableRooms ] = useState>(null); + const [ cost, setCost ] = useState(0); + + useEffect(() => + { + if(!isVisible) + { + setCurrentTab(1); + dispatchGroupsState({ + type: GroupsActions.RESET_GROUP_SETTINGS + }); + } + else + { + SendMessageHook(new GroupBuyDataComposer()); + } + }, [ isVisible ]); + + const onGroupBuyDataEvent = useCallback((event: GroupBuyDataEvent) => + { + const parser = event.getParser(); + + setAvailableRooms(parser.availableRooms); + setCost(parser.groupCost); + }, []); + + CreateMessageHook(GroupBuyDataEvent, onGroupBuyDataEvent); + + 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) + { + alert(LocalizeText('group.edit.error.no.name.or.room.selected')); + return; + } + break; + } + } + + setCurrentTab(value => + { + return (value === 4 ? value : value + 1) + }); + }, [ currentTab, groupName, groupHomeroomId ]); + + 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 && } +
+
+ + +
+
+
+ ); +}; diff --git a/src/views/groups/views/creator/GroupCreatorView.types.ts b/src/views/groups/views/creator/GroupCreatorView.types.ts new file mode 100644 index 00000000..249f5dbf --- /dev/null +++ b/src/views/groups/views/creator/GroupCreatorView.types.ts @@ -0,0 +1,5 @@ +export interface GroupCreatorViewProps +{ + isVisible: boolean; + onClose: () => void; +} diff --git a/src/views/groups/views/creator/views/creator-tab-identity/GroupCreatorTabIdentityView.tsx b/src/views/groups/views/creator/views/creator-tab-identity/GroupCreatorTabIdentityView.tsx new file mode 100644 index 00000000..b19421b6 --- /dev/null +++ b/src/views/groups/views/creator/views/creator-tab-identity/GroupCreatorTabIdentityView.tsx @@ -0,0 +1,66 @@ +import { FC, useCallback } from 'react'; +import { CreateLinkEvent, LocalizeText } from '../../../../../../api'; +import { useGroupsContext } from '../../../../context/GroupsContext'; +import { GroupsActions } from '../../../../context/GroupsContext.types'; +import { GroupCreatorTabIdentityViewProps } from './GroupCreatorTabIdentityView.types'; + +export const GroupCreatorTabIdentityView: FC = props => +{ + const { groupsState = null, dispatchGroupsState = null } = useGroupsContext(); + const { groupName = '', groupDescription = '', groupHomeroomId = 0 } = groupsState; + + const { availableRooms = null } = props; + + const setName = useCallback((name: string) => + { + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_NAME, + payload: { + stringValue: name + } + }) + }, [ dispatchGroupsState ]); + + const setDescription = useCallback((description: string) => + { + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_DESCRIPTION, + payload: { + stringValue: description + } + }) + }, [ dispatchGroupsState ]); + + const setHomeroomId = useCallback((id: number) => + { + dispatchGroupsState({ + type: GroupsActions.SET_GROUP_HOMEROOM_ID, + payload: { + numberValue: id + } + }) + }, [ dispatchGroupsState ]); + + return (<> +
+ + setName(e.target.value) } /> +
+
+ + setDescription(e.target.value) } /> +
+
+ + +
+
{ LocalizeText('group.edit.base.warning') }
+
CreateLinkEvent('navigator/create') }>{ LocalizeText('group.createroom') }
+ ); +}; diff --git a/src/views/groups/views/creator/views/creator-tab-identity/GroupCreatorTabIdentityView.types.ts b/src/views/groups/views/creator/views/creator-tab-identity/GroupCreatorTabIdentityView.types.ts new file mode 100644 index 00000000..f53468af --- /dev/null +++ b/src/views/groups/views/creator/views/creator-tab-identity/GroupCreatorTabIdentityView.types.ts @@ -0,0 +1,4 @@ +export interface GroupCreatorTabIdentityViewProps +{ + availableRooms: Map; +} diff --git a/src/views/navigator/NavigatorView.tsx b/src/views/navigator/NavigatorView.tsx index f31fc246..49c05fae 100644 --- a/src/views/navigator/NavigatorView.tsx +++ b/src/views/navigator/NavigatorView.tsx @@ -143,6 +143,10 @@ export const NavigatorView: FC = props => } } return; + case 'create': + setIsVisible(true); + setCreatorOpen(true); + return; } }, []);