Skip to content

Commit

Permalink
wip: base of player modal
Browse files Browse the repository at this point in the history
  • Loading branch information
tabarra committed Dec 23, 2023
1 parent c17f618 commit 5a598c3
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 13 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ ConVar usage **example** for different port and profile:

## License, Credits and Thanks
- This project is licensed under the [MIT License](https://github.com/tabarra/txAdmin/blob/master/LICENSE);
- ["Kick All" button icon](https://www.flaticon.com/free-icon/users-avatar_8188385) made by __SeyfDesigner__ from [www.flaticon.com](https://www.flaticon.com);
- ["Kick" button icons](https://www.flaticon.com/free-icon/users-avatar_8188385) made by __SeyfDesigner__ from [www.flaticon.com](https://www.flaticon.com);
- Warning Sounds ([1](https://freesound.org/people/Ultranova105/sounds/136756/)/[2](https://freesound.org/people/Ultranova105/sounds/136754/)) made by __Ultranova105__ are licensed under [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/);
- [Announcement Sound](https://freesound.org/people/IENBA/sounds/545495/) made by __IENBA__ is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/);
- [Message Sound](https://freesound.org/people/Divinux/sounds/198414/) made by __Divinux__ is licensed under [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/);
Expand Down
4 changes: 4 additions & 0 deletions panel/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { InjectedTxConsts } from '@shared/otherTypes';

type LogoutNoticeMessage = { type: 'logoutNotice' }
type OpenAccountModalMessage = { type: 'openAccountModal' }
type OpenPlayerModalMessage = { type: 'openPlayerModal', ref: PlayerModalRefType }

export declare global {
interface Window {
txConsts: InjectedTxConsts;
invokeNative?: (nativeName: string, ...args: any[]) => void;
}
type MessageEventFromIframe = MessageEvent<LogoutNoticeMessage>
| MessageEvent<OpenAccountModalMessage>
| MessageEvent<OpenPlayerModalMessage>;
}
37 changes: 37 additions & 0 deletions panel/src/hooks/playerModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { atomWithReset } from 'jotai/utils';


/**
* Prompt Dialog Stuff
*/
type PlayerModalRefType = {
mutex: string;
netid: number;
} | {
license: string;
} | undefined;
export const playerModalOpenAtom = atomWithReset(false);
export const playerModalRefAtom = atomWithReset<PlayerModalRefType>(undefined);

//Hook to open the player modal
export const useOpenPlayerModal = () => {
const setModalRef = useSetAtom(playerModalRefAtom);
const setModalOpen = useSetAtom(playerModalOpenAtom);
return (data: Exclude<PlayerModalRefType, undefined>) => {
setModalRef(data);
setModalOpen(true);
}
};


//General hook for the state of the modal
export const usePlayerModalStateValue = () => {
const playerRef = useAtomValue(playerModalRefAtom);
const [isModalOpen, setIsModalOpen] = useAtom(playerModalOpenAtom);
return {
isModalOpen,
playerRef,
closeModal: () => setIsModalOpen(false),
}
}
15 changes: 11 additions & 4 deletions panel/src/hooks/playerlist.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { PlayerlistEventType, PlayerlistPlayerType } from "@shared/socketioTypes";
import { atom, useAtomValue, useSetAtom } from "jotai";
import { atom, useSetAtom } from "jotai";


/**
* Atoms
*/
export const playerlistAtom = atom<PlayerlistPlayerType[]>([]);
export const playerCountAtom = atom((get) => get(playerlistAtom).length);

//NOTE: In the old UI, we didn't store the mutex separately, but as a prop of players
// export const serverMutexAtom = atom<string | null>(null);
export const serverMutexAtom = atom<string | null>(null);


/**
* Hooks
*/
export const useProcessPlayerlistEvents = () => {
const setPlayerlist = useSetAtom(playerlistAtom);
const setServerMutex = useSetAtom(serverMutexAtom);

return (events: PlayerlistEventType[]) => {
//If there is a fullPlayerlist, skip everything before it
Expand All @@ -27,6 +26,7 @@ export const useProcessPlayerlistEvents = () => {
for (const event of events) {
if (event.type === 'fullPlayerlist') {
setPlayerlist(event.playerlist);
setServerMutex(event.mutex);
} else if (event.type === 'playerJoining') {
setPlayerlist((oldList) => [...oldList, event]);
} else if (event.type === 'playerDropped') {
Expand All @@ -37,3 +37,10 @@ export const useProcessPlayerlistEvents = () => {
}
}
};

//Getter for the server mutex
// const getCurrentMutex = useAtomCallback(
// useCallback((get) => {
// return get(serverMutexAtom)
// }, []),
// );
10 changes: 8 additions & 2 deletions panel/src/layout/MainShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { ServerSidebar } from './serverSidebar/ServerSidebar';
import { PlayerlistSidebar } from './playerlistSidebar/PlayerlistSidebar';
import MainSheets from './MainSheets';
import WarningBar from './WarningBar';
import BreakpointDebugger from '@/components/BreakpointDebugger';
import { useEffect, useRef } from 'react';
import { useSetGlobalStatus } from '@/hooks/status';
import { useProcessUpdateAvailableEvent, useSetOfflineWarning } from '@/hooks/useWarningBar';
Expand All @@ -19,17 +18,23 @@ import PromptDialog from '@/components/PromptDialog';
import TxToaster, { txToast } from '../components/TxToaster';
import AccountDialog from '@/components/AccountDialog';
import { useOpenAccountModal } from '@/hooks/dialogs';
import PlayerModal from './playerModal/PlayerModal';
import { useOpenPlayerModal } from '@/hooks/playerModal';


export default function MainShell() {
useAtomValue(pageTitleWatcher);
const expireSession = useExpireAuthData();
const openAccountModal = useOpenAccountModal();
const openPlayerModal = useOpenPlayerModal();

useEventListener('message', (e: MessageEventFromIframe) => {
if (e.data.type === 'logoutNotice') {
expireSession('child iframe');
} else if (e.data.type === 'openYourAccountModal') {
} else if (e.data.type === 'openAccountModal') {
openAccountModal();
} else if (e.data.type === 'openPlayerModal') {
openPlayerModal(e.data.ref);
}
});

Expand Down Expand Up @@ -106,6 +111,7 @@ export default function MainShell() {
<PromptDialog />
<TxToaster />
<AccountDialog />
<PlayerModal />
{/* <BreakpointDebugger /> */}
</>;
}
74 changes: 74 additions & 0 deletions panel/src/layout/playerModal/PlayerModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {
Dialog,
DialogContent, DialogFooter,
DialogHeader,
DialogTitle
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { usePlayerModalStateValue } from "@/hooks/playerModal";
import { AlertTriangleIcon, MailIcon, ShieldCheckIcon, InfoIcon, ListIcon, HistoryIcon, BanIcon } from "lucide-react";
import { KickOneIcon } from '@/components/KickIcons';


export default function PlayerModal() {
const { isModalOpen, closeModal, playerRef } = usePlayerModalStateValue();

const handleOpenClose = (newOpenState: boolean) => {
if (isModalOpen && !newOpenState) {
closeModal();
}
};

return (
<Dialog open={isModalOpen} onOpenChange={handleOpenClose}>
<DialogContent className="max-w-2xl" autoFocus={false}>
<DialogHeader>
<DialogTitle>[2] Tang Salvatore</DialogTitle>
</DialogHeader>

{JSON.stringify(playerRef)}
<span className="text-fuchsia-500">pretend here lies the player modal content</span>

<DialogFooter className="w-full justify-centerx xjustify-around sm:flex-colx gap-2">
<Button
variant='outline'
disabled={false} //FIXME:
onClick={() => { }} //FIXME:
className="pl-2"
>
<ShieldCheckIcon className="h-5 mr-1" /> Give Admin
</Button>
<Button
variant='outline'
disabled={false} //FIXME:
onClick={() => { }} //FIXME:
className="pl-2"
>
<MailIcon className="h-5 mr-1" /> DM
</Button>
<Button
variant='outline'
disabled={false} //FIXME:
onClick={() => { }} //FIXME:
className="pl-2"
>
<KickOneIcon style={{
height: '1.25rem',
width: '1.75rem',
marginRight: '0.25rem',
fill: 'currentcolor'
}} /> Kick
</Button>
<Button
variant='outline'
disabled={false} //FIXME:
onClick={() => { }} //FIXME:
className="pl-2"
>
<AlertTriangleIcon className="h-5 mr-1" /> Warn
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
21 changes: 18 additions & 3 deletions panel/src/layout/playerlistSidebar/Playerlist.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { playerlistAtom } from "@/hooks/playerlist";
import { playerlistAtom, serverMutexAtom } from "@/hooks/playerlist";
import cleanPlayerName from "@shared/cleanPlayerName";
import { useAtomValue } from "jotai";
import { VirtualItem, useVirtualizer } from '@tanstack/react-virtual';
Expand All @@ -18,6 +18,7 @@ import {
DropdownMenuRadioItem,
DropdownMenuSeparator, DropdownMenuTrigger
} from "@/components/ui/dropdown-menu";
import { useOpenPlayerModal } from "@/hooks/playerModal";


//NOTE: Move the styles (except color) to global.css since this component is rendered often
Expand Down Expand Up @@ -131,15 +132,21 @@ function PlayerlistFilter({ filterString, setFilterString }: PlayerlistFilterPro
const PlayerlistFilterMemo = memo(PlayerlistFilter);


type PlayerlistPlayerProps = {
virtualItem: VirtualItem,
player: PlayerlistPlayerType,
modalOpener: (netid: number) => void,
}
//NOTE: the styles have been added to global.css since this component is rendered A LOT
function PlayerlistPlayer({ virtualItem, player }: { virtualItem: VirtualItem, player: PlayerlistPlayerType }) {
function PlayerlistPlayer({ virtualItem, player, modalOpener }: PlayerlistPlayerProps) {
return (
<div
className="player"
style={{
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)`,
}}
onClick={() => modalOpener(player.netid)}
>
<div className="pid-block">
<span className="pid-badge">{player.netid}</span>
Expand All @@ -152,6 +159,8 @@ function PlayerlistPlayer({ virtualItem, player }: { virtualItem: VirtualItem, p

export default function Playerlist() {
const playerlist = useAtomValue(playerlistAtom);
const serverMutex = useAtomValue(serverMutexAtom);
const openPlayerModal = useOpenPlayerModal();
const scrollRef = useRef<HTMLDivElement>(null);
const [filterString, setFilterString] = useState('');

Expand Down Expand Up @@ -185,6 +194,11 @@ export default function Playerlist() {
});
const virtualItems = rowVirtualizer.getVirtualItems();

const modalOpener = (netid: number) => {
if(!serverMutex) return;
openPlayerModal({mutex: serverMutex, netid});
}

return (
<>
<PlayerlistFilterMemo filterString={filterString} setFilterString={setFilterString} />
Expand Down Expand Up @@ -217,7 +231,7 @@ export default function Playerlist() {
</div>

<style>{injectedStyle}</style>
<ScrollArea className="h-full" ref={scrollRef}>
<ScrollArea className="h-full select-none" ref={scrollRef}>
<div
className="tx-playerlist"
style={{
Expand All @@ -231,6 +245,7 @@ export default function Playerlist() {
key={virtualItem.key}
virtualItem={virtualItem}
player={filteredPlayerlist[virtualItem.index]}
modalOpener={modalOpener}
/>
))}
</div>
Expand Down
6 changes: 3 additions & 3 deletions web/main/adminManager.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
<td class="tableActions">
<% if (admin.isSelf) { %>
<button class="btn btn-sm btn-outline-primary"
onclick="openYourAccountModal()">
onclick="openAccountModal()">
<i class="icon-pencil"></i> Your Account
</button>
<% } else { %>
Expand Down Expand Up @@ -202,8 +202,8 @@
//============================================== Show Your Account Modal
function openYourAccountModal() {
window.parent.postMessage({ type: 'openYourAccountModal' });
function openAccountModal() {
window.parent.postMessage({ type: 'openAccountModal' });
}
//============================================== Show Admin Modal
Expand Down
2 changes: 2 additions & 0 deletions web/public/js/txadmin/players.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,11 @@ function tsToLocaleDate(ts) {
// Open Modal
function showPlayerByMutexNetid(mutexNetid) {
const [mutex, netid] = mutexNetid.split(/[_#]/, 2);
return window.parent.postMessage({ type: 'openPlayerModal', ref: { mutex, netid } });
return showPlayer({ mutex, netid });
}
function showPlayerByLicense(license) {
return window.parent.postMessage({ type: 'openPlayerModal', ref: { license } });
return showPlayer({ license });
}
function showPlayer(playerRef, keepTabSelection = false) {
Expand Down

0 comments on commit 5a598c3

Please sign in to comment.