Groups updates

This commit is contained in:
MyNameIsBatman 2021-09-01 01:02:34 -03:00
parent 71aa8d5db0
commit e517c3afbb
21 changed files with 553 additions and 10 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 311 B

After

Width:  |  Height:  |  Size: 311 B

View File

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 429 B

View File

Before

Width:  |  Height:  |  Size: 447 B

After

Width:  |  Height:  |  Size: 447 B

View File

Before

Width:  |  Height:  |  Size: 460 B

After

Width:  |  Height:  |  Size: 460 B

View File

Before

Width:  |  Height:  |  Size: 542 B

After

Width:  |  Height:  |  Size: 542 B

View File

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

View File

Before

Width:  |  Height:  |  Size: 373 B

After

Width:  |  Height:  |  Size: 373 B

View File

@ -550,43 +550,43 @@
} }
&.icon-group-type-0 { &.icon-group-type-0 {
background: url('../images/groups/grouptype_icon_0.png'); background: url('../images/groups/icons/grouptype_icon_0.png');
width: 16px; width: 16px;
height: 16px; height: 16px;
} }
&.icon-group-type-1 { &.icon-group-type-1 {
background: url('../images/groups/grouptype_icon_1.png'); background: url('../images/groups/icons/grouptype_icon_1.png');
width: 16px; width: 16px;
height: 16px; height: 16px;
} }
&.icon-group-type-2 { &.icon-group-type-2 {
background: url('../images/groups/grouptype_icon_2.png'); background: url('../images/groups/icons/grouptype_icon_2.png');
width: 16px; width: 16px;
height: 16px; height: 16px;
} }
&.icon-group-decorate { &.icon-group-decorate {
background: url('../images/groups/group_decorate_icon.png'); background: url('../images/groups/icons/group_decorate_icon.png');
width: 15px; width: 15px;
height: 15px; height: 15px;
} }
&.icon-group-member { &.icon-group-member {
background: url('../images/groups/group_icon_big_member.png'); background: url('../images/groups/icons/group_icon_big_member.png');
width: 20px; width: 20px;
height: 20px; height: 20px;
} }
&.icon-group-admin { &.icon-group-admin {
background: url('../images/groups/group_icon_big_admin.png'); background: url('../images/groups/icons/group_icon_big_admin.png');
width: 20px; width: 20px;
height: 20px; height: 20px;
} }
&.icon-group-owner { &.icon-group-owner {
background: url('../images/groups/group_icon_big_owner.png'); background: url('../images/groups/icons/group_icon_big_owner.png');
width: 20px; width: 20px;
height: 20px; height: 20px;
} }

View File

@ -1,3 +1,4 @@
@import './views/creator/GroupCreatorView';
@import './views/information/GroupInformationView'; @import './views/information/GroupInformationView';
@import './views/information-standalone/GroupInformationStandaloneView'; @import './views/information-standalone/GroupInformationStandaloneView';
@import './views/room-information/GroupRoomInformationView'; @import './views/room-information/GroupRoomInformationView';

View File

@ -1,16 +1,19 @@
import { GroupBadgePartsComposer, ILinkEventTracker } from '@nitrots/nitro-renderer'; 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 { AddEventLinkTracker, RemoveLinkEventTracker } from '../../api';
import { SendMessageHook } from '../../hooks'; import { SendMessageHook } from '../../hooks';
import { GroupsContextProvider } from './context/GroupsContext'; import { GroupsContextProvider } from './context/GroupsContext';
import { GroupsReducer, initialGroups } from './context/GroupsContext.types'; import { GroupsReducer, initialGroups } from './context/GroupsContext.types';
import { GroupsMessageHandler } from './GroupsMessageHandler'; import { GroupsMessageHandler } from './GroupsMessageHandler';
import { GroupCreatorView } from './views/creator/GroupCreatorView';
import { GroupInformationStandaloneView } from './views/information-standalone/GroupInformationStandaloneView'; import { GroupInformationStandaloneView } from './views/information-standalone/GroupInformationStandaloneView';
export const GroupsView: FC<{}> = props => export const GroupsView: FC<{}> = props =>
{ {
const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups); const [ groupsState, dispatchGroupsState ] = useReducer(GroupsReducer, initialGroups);
const [ isCreatorVisible, setIsCreatorVisible ] = useState<boolean>(false);
useEffect(() => useEffect(() =>
{ {
SendMessageHook(new GroupBadgePartsComposer()); SendMessageHook(new GroupBadgePartsComposer());
@ -25,6 +28,7 @@ export const GroupsView: FC<{}> = props =>
switch(parts[1]) switch(parts[1])
{ {
case 'create': case 'create':
setIsCreatorVisible(true);
return; return;
} }
}, []); }, []);
@ -44,6 +48,7 @@ export const GroupsView: FC<{}> = props =>
return ( return (
<GroupsContextProvider value={ { groupsState, dispatchGroupsState } }> <GroupsContextProvider value={ { groupsState, dispatchGroupsState } }>
<GroupsMessageHandler /> <GroupsMessageHandler />
<GroupCreatorView isVisible={ isCreatorVisible } onClose={ () => setIsCreatorVisible(false) } />
<GroupInformationStandaloneView /> <GroupInformationStandaloneView />
</GroupsContextProvider> </GroupsContextProvider>
); );

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -18,20 +18,29 @@ export interface IGroupsState
badgePartColors: Map<number, string>; badgePartColors: Map<number, string>;
groupColorsA: Map<number, string>; groupColorsA: Map<number, string>;
groupColorsB: Map<number, string>; groupColorsB: Map<number, string>;
groupName: string;
groupDescription: string;
groupHomeroomId: number;
} }
export interface IGroupsAction export interface IGroupsAction
{ {
type: string; type: string;
payload: { payload?: {
arrayMaps?: Map<number, string[]>[]; arrayMaps?: Map<number, string[]>[];
stringMaps?: Map<number, string>[]; stringMaps?: Map<number, string>[];
stringValue?: string;
numberValue?: number;
} }
} }
export class GroupsActions export class GroupsActions
{ {
public static SET_BADGE_PARTS: string = 'GA_SET_BADGE_PARTS'; 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 = { export const initialGroups: IGroupsState = {
@ -39,7 +48,10 @@ export const initialGroups: IGroupsState = {
badgeSymbols: null, badgeSymbols: null,
badgePartColors: null, badgePartColors: null,
groupColorsA: null, groupColorsA: null,
groupColorsB: null groupColorsB: null,
groupName: '',
groupDescription: '',
groupHomeroomId: 0
}; };
export const GroupsReducer: Reducer<IGroupsState, IGroupsAction> = (state, action) => export const GroupsReducer: Reducer<IGroupsState, IGroupsAction> = (state, action) =>
@ -55,6 +67,28 @@ export const GroupsReducer: Reducer<IGroupsState, IGroupsAction> = (state, actio
return { ...state, badgeBases, badgeSymbols, badgePartColors, groupColorsA, groupColorsB }; 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: default:
return state; return state;
} }

View File

@ -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;
}
}
}
}

View File

@ -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<GroupCreatorViewProps> = props =>
{
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
const { groupName = null, groupHomeroomId = null } = groupsState;
const { isVisible = false, onClose = null } = props;
const [ currentTab, setCurrentTab ] = useState<number>(1);
const [ availableRooms, setAvailableRooms ] = useState<Map<number, string>>(null);
const [ cost, setCost ] = useState<number>(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 (
<NitroCardView className="nitro-group-creator" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('group.create.title') } onCloseClick={ onClose } />
<NitroCardContentView className="pb-2">
<div className="creator-tabs mb-2">
{ TABS.map((tab, index) =>
{
return (<div key={ index } className={ 'tab tab-' + (tab === 1 ? 'blue-flat' : tab === 4 ? 'yellow' : 'blue-arrow') + classNames({ ' active': currentTab === tab }) }>
<div>{ LocalizeText('group.create.steplabel.' + tab) }</div>
</div>);
}) }
</div>
<div className="d-flex align-items-center mb-2">
<div className={ 'flex-shrink-0 tab-image tab-' + currentTab }>
<div></div>
</div>
<div className="w-100 text-black ms-2">
<div className="fw-bold h4 m-0">{ LocalizeText('group.create.stepcaption.' + currentTab) }</div>
<div>{ LocalizeText('group.create.stepdesc.' + currentTab) }</div>
</div>
</div>
<div className="text-black">
{ currentTab === 1 && <GroupCreatorTabIdentityView availableRooms={ availableRooms } /> }
</div>
<div className="d-flex justify-content-between mt-2">
<Button variant="link" className="text-black" onClick={ previousStep }>{ LocalizeText(currentTab === 1 ? 'generic.cancel' : 'group.create.previousstep') }</Button>
<button className="btn btn-primary" onClick={ nextStep }>{ LocalizeText('group.create.nextstep') }</button>
</div>
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -0,0 +1,5 @@
export interface GroupCreatorViewProps
{
isVisible: boolean;
onClose: () => void;
}

View File

@ -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<GroupCreatorTabIdentityViewProps> = 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 (<>
<div className="form-group mb-2">
<label>{ LocalizeText('group.edit.name') }</label>
<input type="text" className="form-control form-control-sm" value={ groupName } onChange={ (e) => setName(e.target.value) } />
</div>
<div className="form-group mb-2">
<label>{ LocalizeText('group.edit.desc') }</label>
<input type="text" className="form-control form-control-sm" value={ groupDescription } onChange={ (e) => setDescription(e.target.value) } />
</div>
<div className="form-group mb-1">
<label>{ LocalizeText('group.edit.base') }</label>
<select className="form-select form-select-sm" value={ groupHomeroomId } onChange={ (e) => setHomeroomId(Number(e.target.value)) }>
<option value={ 0 } disabled>{ LocalizeText('group.edit.base.select.room') }</option>
{ availableRooms && Array.from(availableRooms.keys()).map((roomId, index) =>
{
return <option key={ index } value={ roomId }>{ availableRooms.get(roomId) }</option>;
}) }
</select>
</div>
<div className="small mb-2">{ LocalizeText('group.edit.base.warning') }</div>
<div className="cursor-pointer text-decoration-underline text-center" onClick={ () => CreateLinkEvent('navigator/create') }>{ LocalizeText('group.createroom') }</div>
</>);
};

View File

@ -0,0 +1,4 @@
export interface GroupCreatorTabIdentityViewProps
{
availableRooms: Map<number, string>;
}

View File

@ -143,6 +143,10 @@ export const NavigatorView: FC<NavigatorViewProps> = props =>
} }
} }
return; return;
case 'create':
setIsVisible(true);
setCreatorOpen(true);
return;
} }
}, []); }, []);