mirror of
https://github.com/billsonnn/nitro-react.git
synced 2025-01-18 21:36:27 +01:00
Group creation
This commit is contained in:
parent
e427ebb961
commit
796c4a12e1
@ -390,7 +390,7 @@ $container-max-widths: (
|
||||
|
||||
$grid-columns: 12 !default;
|
||||
$grid-gutter-width: 16px !default;
|
||||
$grid-row-columns: 6 !default;
|
||||
$grid-row-columns: 18 !default;
|
||||
|
||||
$gutters: $spacers !default;
|
||||
|
||||
|
@ -34,5 +34,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
.row-cols-6 {
|
||||
|
||||
.col {
|
||||
padding-right: 0.25rem;
|
||||
|
||||
&:nth-child(6n+6) {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row-cols-7 {
|
||||
|
||||
.col {
|
||||
padding-right: 0.25rem;
|
||||
|
||||
&:nth-child(7n+7) {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row-cols-8 {
|
||||
|
||||
.col {
|
||||
padding-right: 0.25rem;
|
||||
|
||||
&:nth-child(8n+8) {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@import './item/NitroCardGridItemView.scss';
|
||||
}
|
||||
|
@ -2,3 +2,4 @@
|
||||
@import './views/information/GroupInformationView';
|
||||
@import './views/information-standalone/GroupInformationStandaloneView';
|
||||
@import './views/room-information/GroupRoomInformationView';
|
||||
@import './views/shared-tabs/GroupSharedTabs';
|
||||
|
@ -1,26 +1,112 @@
|
||||
import { GroupBadgePartsEvent } from '@nitrots/nitro-renderer';
|
||||
import { GroupBadgePartsEvent, GroupBuyDataEvent, RoomCreatedEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { CreateMessageHook } from '../../hooks';
|
||||
import { useGroupsContext } from './context/GroupsContext';
|
||||
import { GroupsActions } from './context/GroupsContext.types';
|
||||
|
||||
function compareId(a, b)
|
||||
{
|
||||
if( a.id < b.id ) return -1;
|
||||
if( a.id > b.id ) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export const GroupsMessageHandler: FC<{}> = props =>
|
||||
{
|
||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||
const { availableRooms = null } = groupsState;
|
||||
|
||||
const onGroupBuyDataEvent = useCallback((event: GroupBuyDataEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const rooms: { id: number, name: string }[] = [];
|
||||
|
||||
parser.availableRooms.forEach((name, id) =>
|
||||
{
|
||||
rooms.push({ id, name });
|
||||
});
|
||||
|
||||
dispatchGroupsState({
|
||||
type: GroupsActions.SET_PURHCASE_SETTINGS,
|
||||
payload: {
|
||||
objectArrays: rooms,
|
||||
numberValue: parser.groupCost
|
||||
}
|
||||
});
|
||||
}, [ dispatchGroupsState ]);
|
||||
|
||||
|
||||
const onRoomCreatedEvent = useCallback((event: RoomCreatedEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const clonedRooms = Array.from(availableRooms);
|
||||
clonedRooms.push({
|
||||
id: parser.roomId,
|
||||
name: parser.roomName
|
||||
});
|
||||
|
||||
dispatchGroupsState({
|
||||
type: GroupsActions.SET_PURHCASE_SETTINGS,
|
||||
payload: {
|
||||
objectArrays: clonedRooms
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
const onGroupBadgePartsEvent = useCallback((event: GroupBadgePartsEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const bases: { id: number, images: string[] }[] = [];
|
||||
const symbols: { id: number, images: string[] }[] = [];
|
||||
const partColors: { id: number, color: string }[] = [];
|
||||
const colorsA: { id: number, color: string }[] = [];
|
||||
const colorsB: { id: number, color: string }[] = [];
|
||||
|
||||
parser.bases.forEach((images, id) =>
|
||||
{
|
||||
bases.push({ id, images });
|
||||
});
|
||||
|
||||
parser.symbols.forEach((images, id) =>
|
||||
{
|
||||
symbols.push({ id, images });
|
||||
});
|
||||
|
||||
parser.partColors.forEach((color, id) =>
|
||||
{
|
||||
partColors.push({ id, color });
|
||||
});
|
||||
|
||||
parser.colorsA.forEach((color, id) =>
|
||||
{
|
||||
colorsA.push({ id, color });
|
||||
});
|
||||
|
||||
parser.colorsB.forEach((color, id) =>
|
||||
{
|
||||
colorsB.push({ id, color });
|
||||
});
|
||||
|
||||
bases.sort(compareId);
|
||||
symbols.sort(compareId);
|
||||
partColors.sort(compareId);
|
||||
colorsA.sort(compareId);
|
||||
colorsB.sort(compareId);
|
||||
|
||||
dispatchGroupsState({
|
||||
type: GroupsActions.SET_BADGE_PARTS,
|
||||
type: GroupsActions.SET_GROUP_BADGE_PARTS_CONFIG,
|
||||
payload: {
|
||||
arrayMaps: [ parser.bases, parser.symbols ],
|
||||
stringMaps: [ parser.partColors, parser.colorsA, parser.colorsB ]
|
||||
objectArrays: [ bases, symbols, partColors, colorsA, colorsB ]
|
||||
}
|
||||
})
|
||||
}, [ dispatchGroupsState ]);
|
||||
|
||||
CreateMessageHook(GroupBuyDataEvent, onGroupBuyDataEvent);
|
||||
CreateMessageHook(RoomCreatedEvent, onRoomCreatedEvent);
|
||||
CreateMessageHook(GroupBadgePartsEvent, onGroupBadgePartsEvent);
|
||||
|
||||
return null;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GroupBadgePartsComposer, ILinkEventTracker } from '@nitrots/nitro-renderer';
|
||||
import { GroupBadgePartsComposer, GroupPurchasedEvent, ILinkEventTracker } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useReducer, useState } from 'react';
|
||||
import { AddEventLinkTracker, RemoveLinkEventTracker } from '../../api';
|
||||
import { SendMessageHook } from '../../hooks';
|
||||
import { AddEventLinkTracker, RemoveLinkEventTracker, TryVisitRoom } from '../../api';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../hooks';
|
||||
import { GroupsContextProvider } from './context/GroupsContext';
|
||||
import { GroupsReducer, initialGroups } from './context/GroupsContext.types';
|
||||
import { GroupsMessageHandler } from './GroupsMessageHandler';
|
||||
@ -44,12 +44,24 @@ export const GroupsView: FC<{}> = props =>
|
||||
|
||||
return () => RemoveLinkEventTracker(linkTracker);
|
||||
}, [ linkReceived ]);
|
||||
|
||||
const onGroupPurchasedEvent = useCallback((event: GroupPurchasedEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setIsCreatorVisible(false);
|
||||
TryVisitRoom(parser.roomId);
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(GroupPurchasedEvent, onGroupPurchasedEvent);
|
||||
|
||||
return (
|
||||
<GroupsContextProvider value={ { groupsState, dispatchGroupsState } }>
|
||||
<GroupsMessageHandler />
|
||||
<div className="nitro-groups">
|
||||
<GroupCreatorView isVisible={ isCreatorVisible } onClose={ () => setIsCreatorVisible(false) } />
|
||||
<GroupInformationStandaloneView />
|
||||
</div>
|
||||
</GroupsContextProvider>
|
||||
);
|
||||
};
|
||||
|
@ -9,18 +9,23 @@ export class GroupBadgePart
|
||||
public color: number;
|
||||
public position: number;
|
||||
|
||||
constructor(type: string)
|
||||
constructor(type: string, key?: number, color?: number, position?: number)
|
||||
{
|
||||
this.type = type;
|
||||
this.key = 0;
|
||||
this.color = 0;
|
||||
this.position = 4;
|
||||
this.key = key ? key : 0;
|
||||
this.color = color ? color : 0;
|
||||
this.position = position ? 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);
|
||||
return GroupBadgePart.getCode(this.type, this.key, this.color, this.position);
|
||||
}
|
||||
|
||||
public static getCode(type: string, key: number, color: number, position: number): string
|
||||
{
|
||||
return (type === GroupBadgePart.BASE ? type : key >= 100 ? GroupBadgePart.SYMBOL_ALT : GroupBadgePart.SYMBOL) + (key < 10 ? '0' : '') + (type === GroupBadgePart.BASE ? key : key >= 100 ? key - 100 : key) + (color < 10 ? '0' : '') + color + position;
|
||||
}
|
||||
}
|
||||
|
@ -1,172 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import { Dispatch, ProviderProps, Reducer } from 'react';
|
||||
import { GroupBadgePart } from './../common/GroupBadgePart';
|
||||
|
||||
export interface IGroupsContext
|
||||
{
|
||||
@ -13,37 +14,46 @@ export interface GroupsContextProps extends ProviderProps<IGroupsContext>
|
||||
|
||||
export interface IGroupsState
|
||||
{
|
||||
badgeBases: Map<number, string[]>;
|
||||
badgeSymbols: Map<number, string[]>;
|
||||
badgePartColors: Map<number, string>;
|
||||
groupColorsA: Map<number, string>;
|
||||
groupColorsB: Map<number, string>;
|
||||
availableRooms: { id: number, name: string }[];
|
||||
purchaseCost: number;
|
||||
badgeBases: { id: number, images: string[] }[];
|
||||
badgeSymbols: { id: number, images: string[] }[];
|
||||
badgePartColors: { id: number, color: string }[];
|
||||
groupColorsA: { id: number, color: string }[];
|
||||
groupColorsB: { id: number, color: string }[];
|
||||
groupName: string;
|
||||
groupDescription: string;
|
||||
groupHomeroomId: number;
|
||||
groupBadgeParts: GroupBadgePart[];
|
||||
groupColors: number[];
|
||||
}
|
||||
|
||||
export interface IGroupsAction
|
||||
{
|
||||
type: string;
|
||||
payload?: {
|
||||
arrayMaps?: Map<number, string[]>[];
|
||||
stringMaps?: Map<number, string>[];
|
||||
objectArrays?: any[];
|
||||
stringValue?: string;
|
||||
numberValue?: number;
|
||||
badgeParts?: GroupBadgePart[];
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupsActions
|
||||
{
|
||||
public static SET_BADGE_PARTS: string = 'GA_SET_BADGE_PARTS';
|
||||
public static SET_PURHCASE_SETTINGS: string = 'GA_SET_PURHCASE_SETTINGS';
|
||||
public static SET_GROUP_BADGE_PARTS_CONFIG: string = 'GA_SET_GROUP_BADGE_PARTS_CONFIG';
|
||||
public static SET_GROUP_NAME: string = 'GA_SET_GROUP_NAME';
|
||||
public static SET_GROUP_DESCRIPTION: string = 'GA_SET_GROUP_DESCRIPTION';
|
||||
public static SET_GROUP_HOMEROOM_ID: string = 'GA_SET_GROUP_HOMEROOM_ID';
|
||||
public static SET_GROUP_BADGE_PARTS: string = 'GA_SET_BADGE_PARTS';
|
||||
public static SET_GROUP_COLORS: string = 'GA_SET_GROUP_COLORS';
|
||||
public static RESET_GROUP_SETTINGS: string = 'GA_RESET_GROUP_SETTINGS';
|
||||
}
|
||||
|
||||
export const initialGroups: IGroupsState = {
|
||||
availableRooms: null,
|
||||
purchaseCost: null,
|
||||
badgeBases: null,
|
||||
badgeSymbols: null,
|
||||
badgePartColors: null,
|
||||
@ -51,19 +61,27 @@ export const initialGroups: IGroupsState = {
|
||||
groupColorsB: null,
|
||||
groupName: '',
|
||||
groupDescription: '',
|
||||
groupHomeroomId: 0
|
||||
groupHomeroomId: 0,
|
||||
groupBadgeParts: null,
|
||||
groupColors: null
|
||||
};
|
||||
|
||||
export const GroupsReducer: Reducer<IGroupsState, IGroupsAction> = (state, action) =>
|
||||
{
|
||||
switch(action.type)
|
||||
{
|
||||
case GroupsActions.SET_BADGE_PARTS: {
|
||||
const badgeBases = (action.payload.arrayMaps[0] || state.badgeBases || null);
|
||||
const badgeSymbols = (action.payload.arrayMaps[1] || state.badgeSymbols || null);
|
||||
const badgePartColors = (action.payload.stringMaps[0] || state.badgePartColors || null);
|
||||
const groupColorsA = (action.payload.stringMaps[1] || state.groupColorsA || null);
|
||||
const groupColorsB = (action.payload.stringMaps[2] || state.groupColorsB || null);
|
||||
case GroupsActions.SET_PURHCASE_SETTINGS: {
|
||||
const availableRooms = action.payload.objectArrays;
|
||||
const purchaseCost = (action.payload.numberValue || state.purchaseCost || 0);
|
||||
|
||||
return { ...state, availableRooms, purchaseCost };
|
||||
}
|
||||
case GroupsActions.SET_GROUP_BADGE_PARTS_CONFIG: {
|
||||
const badgeBases = (action.payload.objectArrays[0] || state.badgeBases || null);
|
||||
const badgeSymbols = (action.payload.objectArrays[1] || state.badgeSymbols || null);
|
||||
const badgePartColors = (action.payload.objectArrays[2] || state.badgePartColors || null);
|
||||
const groupColorsA = (action.payload.objectArrays[3] || state.groupColorsA || null);
|
||||
const groupColorsB = (action.payload.objectArrays[4] || state.groupColorsB || null);
|
||||
|
||||
return { ...state, badgeBases, badgeSymbols, badgePartColors, groupColorsA, groupColorsB };
|
||||
}
|
||||
@ -82,12 +100,24 @@ export const GroupsReducer: Reducer<IGroupsState, IGroupsAction> = (state, actio
|
||||
|
||||
return { ...state, groupHomeroomId };
|
||||
}
|
||||
case GroupsActions.SET_GROUP_BADGE_PARTS: {
|
||||
const groupBadgeParts = action.payload.badgeParts;
|
||||
|
||||
return { ...state, groupBadgeParts };
|
||||
}
|
||||
case GroupsActions.SET_GROUP_COLORS: {
|
||||
const groupColors = action.payload.objectArrays;
|
||||
|
||||
return { ...state, groupColors };
|
||||
}
|
||||
case GroupsActions.RESET_GROUP_SETTINGS: {
|
||||
const groupName = '';
|
||||
const groupDescription = '';
|
||||
const groupHomeroomId = 0;
|
||||
|
||||
return { ...state, groupName, groupDescription, groupHomeroomId };
|
||||
const groupBadgeParts = null;
|
||||
const groupColors = null;
|
||||
|
||||
return { ...state, groupName, groupDescription, groupHomeroomId, groupBadgeParts, groupColors };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
|
@ -112,4 +112,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.creator-tab {
|
||||
height: 230px;
|
||||
min-height: 230px;
|
||||
max-height: 230px;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,28 @@
|
||||
import { GroupBuyDataComposer, GroupBuyDataEvent } from '@nitrots/nitro-renderer';
|
||||
import { GroupBuyComposer, GroupBuyDataComposer } 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 { HasHabboClub, LocalizeText } from '../../../../api';
|
||||
import { SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { useGroupsContext } from '../../context/GroupsContext';
|
||||
import { GroupsActions } from '../../context/GroupsContext.types';
|
||||
import { GroupSharedTabBadgeView } from '../shared-tabs/tab-badge/GroupSharedTabBadgeView';
|
||||
import { GroupSharedTabColorsView } from '../shared-tabs/tab-colors/GroupSharedTabColorsView';
|
||||
import { GroupCreatorViewProps } from './GroupCreatorView.types';
|
||||
import { GroupCreatorTabIdentityView } from './views/creator-tab-identity/GroupCreatorTabIdentityView';
|
||||
import { GroupCreatorTabConfirmationView } from './views/tab-confirmation/GroupCreatorTabConfirmationView';
|
||||
import { GroupCreatorTabIdentityView } from './views/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 { groupName = null, groupDescription = null, groupHomeroomId = null, groupColors = null, groupBadgeParts = 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(() =>
|
||||
{
|
||||
@ -38,15 +39,24 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
||||
}
|
||||
}, [ isVisible ]);
|
||||
|
||||
const onGroupBuyDataEvent = useCallback((event: GroupBuyDataEvent) =>
|
||||
const buyGroup = useCallback(() =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
const badge = [];
|
||||
|
||||
setAvailableRooms(parser.availableRooms);
|
||||
setCost(parser.groupCost);
|
||||
}, []);
|
||||
if(!groupBadgeParts) return;
|
||||
|
||||
CreateMessageHook(GroupBuyDataEvent, onGroupBuyDataEvent);
|
||||
groupBadgeParts.forEach((part) =>
|
||||
{
|
||||
if(part.code)
|
||||
{
|
||||
badge.push(part.key);
|
||||
badge.push(part.color);
|
||||
badge.push(part.position);
|
||||
}
|
||||
});
|
||||
|
||||
SendMessageHook(new GroupBuyComposer(groupName, groupDescription, groupHomeroomId, groupColors[0], groupColors[1], badge));
|
||||
}, [ groupName, groupDescription, groupHomeroomId, groupColors, groupBadgeParts ]);
|
||||
|
||||
const previousStep = useCallback(() =>
|
||||
{
|
||||
@ -67,6 +77,10 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
buyGroup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentTab(value =>
|
||||
@ -98,12 +112,15 @@ export const GroupCreatorView: FC<GroupCreatorViewProps> = props =>
|
||||
<div>{ LocalizeText('group.create.stepdesc.' + currentTab) }</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-black">
|
||||
{ currentTab === 1 && <GroupCreatorTabIdentityView availableRooms={ availableRooms } /> }
|
||||
<div className="text-black creator-tab">
|
||||
{ currentTab === 1 && <GroupCreatorTabIdentityView /> }
|
||||
{ currentTab === 2 && <GroupSharedTabBadgeView /> }
|
||||
{ currentTab === 3 && <GroupSharedTabColorsView /> }
|
||||
{ currentTab === 4 && <GroupCreatorTabConfirmationView /> }
|
||||
</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>
|
||||
<button disabled={ currentTab === 4 && !HasHabboClub() } className={ 'btn ' + (currentTab === 4 ? HasHabboClub() ? 'btn-success' : 'btn-danger' : 'btn-primary') } onClick={ nextStep }>{ LocalizeText(currentTab === 4 ? HasHabboClub() ? 'group.create.confirm.buy' : 'group.create.confirm.viprequired' : 'group.create.nextstep') }</button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
|
@ -1,4 +0,0 @@
|
||||
export interface GroupCreatorTabIdentityViewProps
|
||||
{
|
||||
availableRooms: Map<number, string>;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import { FC, useCallback } from 'react';
|
||||
import { LocalizeText } from '../../../../../../api';
|
||||
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
|
||||
import { useGroupsContext } from '../../../../context/GroupsContext';
|
||||
|
||||
export const GroupCreatorTabConfirmationView: FC<{}> = props =>
|
||||
{
|
||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||
const { groupName = '', groupDescription = '', groupBadgeParts = null, groupColors = null, groupColorsA = null, groupColorsB = null, purchaseCost = 0 } = groupsState;
|
||||
|
||||
const getCompleteBadgeCode = useCallback(() =>
|
||||
{
|
||||
let code = '';
|
||||
|
||||
if(!groupBadgeParts) return code;
|
||||
|
||||
groupBadgeParts.forEach((badgePart) =>
|
||||
{
|
||||
if(badgePart.code) code = code + badgePart.code;
|
||||
});
|
||||
|
||||
return code;
|
||||
}, [ groupBadgeParts ]);
|
||||
|
||||
const getGroupColor = useCallback((colorIndex: number) =>
|
||||
{
|
||||
if(colorIndex === 0) return groupColorsA.find(c => c.id === groupColors[colorIndex]).color;
|
||||
|
||||
return groupColorsB.find(c => c.id === groupColors[colorIndex]).color;
|
||||
}, [ groupColors, groupColorsA, groupColorsB ]);
|
||||
|
||||
return (
|
||||
<div className="d-flex gap-3 h-100">
|
||||
<div>
|
||||
<div className="fw-bold text-nowrap">{ LocalizeText('group.create.confirm.guildbadge') }</div>
|
||||
<div className="badge-preview">
|
||||
<BadgeImageView badgeCode={ getCompleteBadgeCode() } isGroup={ true } />
|
||||
</div>
|
||||
<div className="d-flex flex-column align-items-center mt-2">
|
||||
<div className="fw-bold text-nowrap">{ LocalizeText('group.edit.color.guild.color') }</div>
|
||||
{ groupColors && <div className="d-flex">
|
||||
<div className="group-color-swatch" style={{ backgroundColor: '#' + getGroupColor(0) }}></div>
|
||||
<div className="group-color-swatch" style={{ backgroundColor: '#' + getGroupColor(1) }}></div>
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-column h-100">
|
||||
<div className="fw-bold">{ groupName }</div>
|
||||
<div>{ groupDescription }</div>
|
||||
<div className="mt-3" dangerouslySetInnerHTML={ { __html: LocalizeText('group.create.confirm.info') } } />
|
||||
<div className="mt-auto rounded bg-primary p-1 text-center text-white">{ LocalizeText('group.create.confirm.buyinfo', ['amount'], [purchaseCost.toString()]) }</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -2,14 +2,11 @@ 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 =>
|
||||
export const GroupCreatorTabIdentityView: FC<{}> = props =>
|
||||
{
|
||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||
const { groupName = '', groupDescription = '', groupHomeroomId = 0 } = groupsState;
|
||||
|
||||
const { availableRooms = null } = props;
|
||||
const { groupName = '', groupDescription = '', groupHomeroomId = 0, availableRooms = null } = groupsState;
|
||||
|
||||
const setName = useCallback((name: string) =>
|
||||
{
|
||||
@ -41,26 +38,26 @@ export const GroupCreatorTabIdentityView: FC<GroupCreatorTabIdentityViewProps> =
|
||||
})
|
||||
}, [ dispatchGroupsState ]);
|
||||
|
||||
return (<>
|
||||
return (<div className="d-flex flex-column justify-content-center h-100">
|
||||
<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) } />
|
||||
<label className="fw-bold">{ LocalizeText('group.edit.name') }</label>
|
||||
<input type="text" className="form-control form-control-sm" value={ groupName } maxLength={ 29 } 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) } />
|
||||
<label className="fw-bold">{ LocalizeText('group.edit.desc') }</label>
|
||||
<input type="text" className="form-control form-control-sm" value={ groupDescription } maxLength={ 254 } onChange={ (e) => setDescription(e.target.value) } />
|
||||
</div>
|
||||
<div className="form-group mb-1">
|
||||
<label>{ LocalizeText('group.edit.base') }</label>
|
||||
<label className="fw-bold">{ 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) =>
|
||||
{ availableRooms && availableRooms.map((room, index) =>
|
||||
{
|
||||
return <option key={ index } value={ roomId }>{ availableRooms.get(roomId) }</option>;
|
||||
return <option key={ index } value={ room.id }>{ room.name }</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>
|
||||
</>);
|
||||
</div>);
|
||||
};
|
40
src/views/groups/views/shared-tabs/GroupSharedTabs.scss
Normal file
40
src/views/groups/views/shared-tabs/GroupSharedTabs.scss
Normal file
@ -0,0 +1,40 @@
|
||||
.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;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: $border-radius;
|
||||
border-bottom-left-radius: $border-radius;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: $border-radius;
|
||||
border-bottom-right-radius: $border-radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@import './tab-badge/GroupSharedTabBadgeView';
|
||||
@import './tab-colors/GroupSharedTabColorsView';
|
@ -0,0 +1,26 @@
|
||||
.shared-tab-badge {
|
||||
.position-swatch {
|
||||
position: relative;
|
||||
border-radius: $border-radius;
|
||||
width: 16px;
|
||||
height: 12px;
|
||||
background: $white;
|
||||
|
||||
&.active {
|
||||
background: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.color-swatch {
|
||||
position: relative;
|
||||
border-radius: $border-radius;
|
||||
width: 16px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.selection-list {
|
||||
height: 160px;
|
||||
min-height: 160px;
|
||||
max-height: 160px;
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
import classNames from 'classnames';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { BadgeImageView } from '../../../../shared/badge-image/BadgeImageView';
|
||||
import { GroupBadgePart } from '../../../common/GroupBadgePart';
|
||||
import { useGroupsContext } from '../../../context/GroupsContext';
|
||||
import { GroupsActions } from '../../../context/GroupsContext.types';
|
||||
|
||||
const POSITIONS: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
|
||||
export const GroupSharedTabBadgeView: FC<{}> = props =>
|
||||
{
|
||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||
const { badgeBases = null, badgeSymbols = null, badgePartColors = null, groupBadgeParts = null } = groupsState;
|
||||
|
||||
const [ editingIndex, setEditingIndex ] = useState<number>(0);
|
||||
const [ isSelectingModel, setIsSelectingModel ] = useState<boolean>(false);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!badgeBases || !badgePartColors || groupBadgeParts) return;
|
||||
|
||||
const badgeParts: GroupBadgePart[] = [
|
||||
new GroupBadgePart(GroupBadgePart.BASE, badgeBases[0].id, badgePartColors[0].id),
|
||||
new GroupBadgePart(GroupBadgePart.SYMBOL),
|
||||
new GroupBadgePart(GroupBadgePart.SYMBOL),
|
||||
new GroupBadgePart(GroupBadgePart.SYMBOL),
|
||||
new GroupBadgePart(GroupBadgePart.SYMBOL)
|
||||
];
|
||||
|
||||
dispatchGroupsState({
|
||||
type: GroupsActions.SET_GROUP_BADGE_PARTS,
|
||||
payload: { badgeParts }
|
||||
});
|
||||
|
||||
}, [ badgeBases, badgePartColors, groupBadgeParts ]);
|
||||
|
||||
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 (
|
||||
<div className="shared-tab-badge">
|
||||
<div className="d-flex gap-2">
|
||||
<div className="badge-preview flex-shrink-0 me-3">
|
||||
<BadgeImageView badgeCode={ getCompleteBadgeCode() } isGroup={ true } />
|
||||
</div>
|
||||
<div>
|
||||
<div className="d-flex gap-2 align-items-center">
|
||||
{ groupBadgeParts && groupBadgeParts.map((badgePart, partIndex) =>
|
||||
{
|
||||
return (
|
||||
<div key={ partIndex } className={ 'badge-preview flex-shrink-0 d-flex align-items-center justify-content-center cursor-pointer' + classNames({ ' active': editingIndex === partIndex }) } onClick={ () => setEditingIndex(partIndex) }>
|
||||
{ badgePart.code && <BadgeImageView badgeCode={ badgePart.code } isGroup={ true } /> }
|
||||
{ !badgePart.code && <i className="fas fa-plus text-primary h4 m-0" /> }
|
||||
</div>
|
||||
)
|
||||
}) }
|
||||
</div>
|
||||
{ !isSelectingModel && groupBadgeParts && <div className="d-flex gap-2 mt-3">
|
||||
<div className="badge-preview d-flex align-items-center justify-content-center flex-shrink-0 cursor-pointer" onClick={ () => setIsSelectingModel(true) }>
|
||||
{ getCurrentPart('code') && <BadgeImageView badgeCode={ getCurrentPart('code') } isGroup={ true } /> }
|
||||
{ !getCurrentPart('code') && <i className="fas fa-plus text-primary h4 m-0" /> }
|
||||
</div>
|
||||
<div>
|
||||
<div className="row row-cols-3 g-0 gap-1 w-100 h-100 overflow-auto">
|
||||
{ POSITIONS.map((position) =>
|
||||
{
|
||||
return <div key={ position } className={ 'position-swatch cursor-pointer' + classNames({ ' active': getCurrentPart('position') === position }) } onClick={ () => selectPartProperty('position', position) }></div>
|
||||
}) }
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="row row-cols-8 g-0 gap-1 w-100 h-100 overflow-auto">
|
||||
{ badgePartColors && badgePartColors.map((item, index) =>
|
||||
{
|
||||
return <div key={ index } className="color-swatch cursor-pointer" style={{ backgroundColor: '#' + item.color }} onClick={ () => selectPartProperty('color', item.id) }></div>
|
||||
}) }
|
||||
</div>
|
||||
</div>
|
||||
</div> }
|
||||
{ isSelectingModel && <div className="selection-list row row-cols-8 g-0 align-content-start gap-1 h-100 overflow-auto mt-3">
|
||||
{ groupBadgeParts[editingIndex].type !== GroupBadgePart.BASE && <>
|
||||
<div className="cursor-pointer badge-preview d-flex align-items-center justify-content-center" onClick={ () => selectPartProperty('key', 0) }>
|
||||
<i className="fas fa-times text-primary h4 m-0" />
|
||||
</div>
|
||||
</> }
|
||||
{ (groupBadgeParts[editingIndex].type === GroupBadgePart.BASE ? badgeBases : badgeSymbols).map((item, index) =>
|
||||
{
|
||||
return <div key={ index } className={ 'cursor-pointer badge-preview' + classNames({ ' bg-primary': groupBadgeParts[editingIndex].key === item.id }) } onClick={ () => selectPartProperty('key', item.id) }>
|
||||
<BadgeImageView badgeCode={ GroupBadgePart.getCode(groupBadgeParts[editingIndex].type, item.id, groupBadgeParts[editingIndex].color, 4) } isGroup={ true } />
|
||||
</div>
|
||||
}) }
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>);
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
.shared-tab-colors {
|
||||
|
||||
.color-swatch {
|
||||
position: relative;
|
||||
border-radius: $border-radius;
|
||||
width: 15px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../api';
|
||||
import { useGroupsContext } from '../../../context/GroupsContext';
|
||||
import { GroupsActions } from '../../../context/GroupsContext.types';
|
||||
|
||||
export const GroupSharedTabColorsView: FC<{}> = props =>
|
||||
{
|
||||
const { groupsState = null, dispatchGroupsState = null } = useGroupsContext();
|
||||
const { groupColors = null, groupColorsA = null, groupColorsB = null } = groupsState;
|
||||
|
||||
const [ selectingColorIndex, setSelectingColorIndex ] = useState<number>(0);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!groupColorsA || !groupColorsB || groupColors) return;
|
||||
|
||||
const colors: number[] = [
|
||||
groupColorsA[0].id,
|
||||
groupColorsB[0].id
|
||||
];
|
||||
|
||||
dispatchGroupsState({
|
||||
type: GroupsActions.SET_GROUP_COLORS,
|
||||
payload: {
|
||||
objectArrays: colors
|
||||
}
|
||||
});
|
||||
|
||||
}, [ 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: {
|
||||
objectArrays: clonedGroupColors
|
||||
}
|
||||
});
|
||||
}, [ selectingColorIndex, groupColors, dispatchGroupsState ]);
|
||||
|
||||
return (
|
||||
<div className="shared-tab-colors d-flex flex-column gap-2">
|
||||
<div className="d-flex flex-column align-items-center">
|
||||
<div className="fw-bold">{ LocalizeText('group.edit.color.guild.color') }</div>
|
||||
{ groupColors && <div className="d-flex">
|
||||
<div className="group-color-swatch" style={{ backgroundColor: '#' + getGroupColor(0) }}></div>
|
||||
<div className="group-color-swatch" style={{ backgroundColor: '#' + getGroupColor(1) }}></div>
|
||||
</div> }
|
||||
</div>
|
||||
{ selectingColorIndex === 0 && <div>
|
||||
<div className="fw-bold">{ LocalizeText('group.edit.color.primary.color') }</div>
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<div className="row row-cols-18 g-0 gap-1 w-100 h-100 overflow-auto">
|
||||
{ groupColorsA && groupColorsA.map((item, index) =>
|
||||
{
|
||||
return <div key={ index } className="color-swatch cursor-pointer" style={{ backgroundColor: '#' + item.color }} onClick={ () => selectColor(item.id) }></div>
|
||||
}) }
|
||||
</div>
|
||||
<div><i className="fas fa-chevron-right h2 m-0 text-primary cursor-pointer" onClick={ () => setSelectingColorIndex(1) } /></div>
|
||||
</div>
|
||||
</div> }
|
||||
{ selectingColorIndex === 1 && <div>
|
||||
<div className="fw-bold text-end">{ LocalizeText('group.edit.color.secondary.color') }</div>
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<div><i className="fas fa-chevron-left h2 m-0 text-primary cursor-pointer" onClick={ () => setSelectingColorIndex(0) } /></div>
|
||||
<div className="row row-cols-18 g-0 gap-1 w-100 h-100 overflow-auto">
|
||||
{ groupColorsB && groupColorsB.map((item, index) =>
|
||||
{
|
||||
return <div key={ index } className="color-swatch cursor-pointer" style={{ backgroundColor: '#' + item.color }} onClick={ () => selectColor(item.id) }></div>
|
||||
}) }
|
||||
</div>
|
||||
</div>
|
||||
</div> }
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user