[DO NOT MERGE] Chat restructure (#360)
* Create new subdirectory for chat-related stuff * Gitignore * Have new code show up on chat * Have new code show up on chat * Fix config issue * More script stuff * Create UserInput components * More chat changes * More updates to chat * Add chat:watch script * Move up state and pass down * Match up existing functionality entirely * Match up existing functionality entirely * Send a message when hitting Enter * feature based directories * First crack at emoji drawer * Leave everything in a fucked up state ugh * Leave it in a better state * Stop for the night * Decouple by abstract chat functionality to provider * Wait a minute... * Small chat restructure * Prepare for notifications * Add root context * Flash number of messages * Read this and u die * Add quote functionality * Couple tweaks * Shallowenize the features dir/ * Add activity list * Ch-ch-ch-ch-ch-changes * Enable moving drawer * Hover style on activities * UserList changes * Add emoji processing logic * Duhhhh * Scroll to top when changing query * Put the emoji in the drawer * Improve emoji drawer * Add emoji genres * Do not show activities * Add feature flag technology * Fix issue where own messages were triggering notifications * Adjust startup scripts * Responsive part 1 * Styling changes for emoji genres * More emoji drawer styling * Add QuickEmojis * Re-add classnames * Set version * Modify build script * Modify build script * Mild renaming * Lots of styling changes * Leggo.remotes/1693176582716663532/tmp_refs/heads/watchparty
parent
35683f65e0
commit
484de39566
|
@ -13,3 +13,6 @@ flask_session/
|
||||||
site_settings.json
|
site_settings.json
|
||||||
/files/test.py
|
/files/test.py
|
||||||
tags
|
tags
|
||||||
|
chat/node_modules
|
||||||
|
chat/build
|
||||||
|
chat/.env
|
12
Dockerfile
12
Dockerfile
|
@ -27,6 +27,18 @@ RUN mkdir /asset_submissions/hats
|
||||||
RUN mkdir /asset_submissions/marseys/original
|
RUN mkdir /asset_submissions/marseys/original
|
||||||
RUN mkdir /asset_submissions/hats/original
|
RUN mkdir /asset_submissions/hats/original
|
||||||
|
|
||||||
|
ENV NODE_VERSION=16.13.0
|
||||||
|
RUN apt install -y curl
|
||||||
|
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
|
||||||
|
ENV NVM_DIR=/root/.nvm
|
||||||
|
RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION}
|
||||||
|
RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION}
|
||||||
|
RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION}
|
||||||
|
ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}"
|
||||||
|
RUN node --version
|
||||||
|
RUN npm --version
|
||||||
|
RUN npm i -g yarn
|
||||||
|
|
||||||
EXPOSE 80/tcp
|
EXPOSE 80/tcp
|
||||||
|
|
||||||
CMD [ "/usr/bin/supervisord", "-c", "/etc/supervisord.conf" ]
|
CMD [ "/usr/bin/supervisord", "-c", "/etc/supervisord.conf" ]
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
FEATURES_ACTIVITY=false
|
||||||
|
DEBUG=false
|
||||||
|
NODE_ENV="production"
|
||||||
|
EMOJI_INPUT_TOKEN=":"
|
||||||
|
QUICK_EMOJIS_MAX_COUNT = 20
|
||||||
|
APPROXIMATE_CHARACTER_WIDTH = 8
|
|
@ -0,0 +1,19 @@
|
||||||
|
require('dotenv').config()
|
||||||
|
const path = require("path");
|
||||||
|
const { build } = require("esbuild");
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
entryPoints: ["./src/index.tsx"],
|
||||||
|
outfile: path.resolve(__dirname, "../files/assets/js/chat_done.js"),
|
||||||
|
bundle: true,
|
||||||
|
define: {
|
||||||
|
"process.env.NODE_ENV": `"${process.env.NODE_ENV}"`,
|
||||||
|
"process.env.DEBUG": process.env.DEBUG,
|
||||||
|
"process.env.FEATURES_ACTIVITY": process.env.FEATURES_ACTIVITY,
|
||||||
|
"process.env.EMOJI_INPUT_TOKEN": `"${process.env.EMOJI_INPUT_TOKEN}"`,
|
||||||
|
"process.env.QUICK_EMOJIS_MAX_COUNT": process.env.QUICK_EMOJIS_MAX_COUNT,
|
||||||
|
"process.env.APPROXIMATE_CHARACTER_WIDTH": process.env.APPROXIMATE_CHARACTER_WIDTH,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
build(options).catch(() => process.exit(1));
|
|
@ -0,0 +1,21 @@
|
||||||
|
declare var process: {
|
||||||
|
env: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare interface ChatSpeakResponse {
|
||||||
|
username: string;
|
||||||
|
avatar: string;
|
||||||
|
hat: string;
|
||||||
|
namecolor: string;
|
||||||
|
text: string;
|
||||||
|
text_censored: string;
|
||||||
|
text_html: string;
|
||||||
|
time: number;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface EmojiModSelection {
|
||||||
|
large: boolean;
|
||||||
|
mirror: boolean;
|
||||||
|
pat: boolean;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "chat",
|
||||||
|
"version": "0.0.2",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash.debounce": "^4.0.7",
|
||||||
|
"@types/lozad": "^1.16.1",
|
||||||
|
"@types/react": "^18.0.20",
|
||||||
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||||
|
"@types/react-window": "^1.8.5",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
|
"dotenv": "^16.0.2",
|
||||||
|
"esbuild": "^0.15.7",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
|
"lozad": "^1.16.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dnd": "^16.0.1",
|
||||||
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-virtualized-auto-sizer": "^1.0.7",
|
||||||
|
"react-window": "^1.8.7",
|
||||||
|
"run-when-changed": "^2.1.0",
|
||||||
|
"socket.io-client": "^4.5.2",
|
||||||
|
"typescript": "^4.8.3",
|
||||||
|
"weak-key": "^1.0.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"chat": "yarn check && yarn build && yarn css:move",
|
||||||
|
"chat:watch": "run-when-changed --watch \"**/*.*\" --exec \"yarn chat\"",
|
||||||
|
"check": "tsc",
|
||||||
|
"build": "node ./build",
|
||||||
|
"css:move": "mv ../files/assets/js/chat_done.css ../files/assets/css/chat_done.css"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
.App {
|
||||||
|
position: fixed;
|
||||||
|
width: 100vw;
|
||||||
|
height: calc(100vh - 42px);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1000px) {
|
||||||
|
.App {
|
||||||
|
height: calc(100vh - 12rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-heading {
|
||||||
|
flex-basis: 3rem;
|
||||||
|
border-bottom: 1px dashed var(--primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-side {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1100px) {
|
||||||
|
.App-side {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-content {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-drawer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-input {
|
||||||
|
flex-basis: 7rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React from "react";
|
||||||
|
import { DndProvider, useDrop } from "react-dnd";
|
||||||
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||||
|
import {
|
||||||
|
ChatHeading,
|
||||||
|
ChatMessageList,
|
||||||
|
UserInput,
|
||||||
|
UserList,
|
||||||
|
UsersTyping,
|
||||||
|
} from "./features";
|
||||||
|
import { ChatProvider, DrawerProvider, useDrawer } from "./hooks";
|
||||||
|
import "./App.css";
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<DndProvider backend={HTML5Backend}>
|
||||||
|
<DrawerProvider>
|
||||||
|
<ChatProvider>
|
||||||
|
<AppInner />
|
||||||
|
</ChatProvider>
|
||||||
|
</DrawerProvider>
|
||||||
|
</DndProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppInner() {
|
||||||
|
const [_, dropRef] = useDrop({
|
||||||
|
accept: "drawer",
|
||||||
|
});
|
||||||
|
const { open, config } = useDrawer();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="App" ref={dropRef}>
|
||||||
|
<div className="App-wrapper">
|
||||||
|
<div className="App-heading">
|
||||||
|
<ChatHeading />
|
||||||
|
</div>
|
||||||
|
<div id="chatWrapper" className="App-content">
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
{open && config.content ? (
|
||||||
|
<div className="App-drawer">{config.content}</div>
|
||||||
|
) : (
|
||||||
|
<ChatMessageList />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="App-side">
|
||||||
|
<UserList />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="App-input">
|
||||||
|
<UserInput />
|
||||||
|
<UsersTyping />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
.BaseDrawer {
|
||||||
|
flex: 1;
|
||||||
|
padding-right: 2rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import React, { PropsWithChildren, useEffect } from "react";
|
||||||
|
import "./BaseDrawer.css";
|
||||||
|
|
||||||
|
interface Props extends PropsWithChildren {
|
||||||
|
onClose?(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BaseDrawer({ onClose, children }: Props) {
|
||||||
|
return <div className="BaseDrawer">{children}</div>;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./BaseDrawer"
|
|
@ -0,0 +1,21 @@
|
||||||
|
.Activity {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Activity > section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Activity i {
|
||||||
|
margin-right: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Activity > section:not(:last-child) {
|
||||||
|
margin-right: 2rem;
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from "react";
|
||||||
|
import "./Activity.css";
|
||||||
|
|
||||||
|
const ACTIVITIES = [
|
||||||
|
{
|
||||||
|
icon: "circle",
|
||||||
|
title: "Roulette",
|
||||||
|
description: "Round and round the wheel of fate turns.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "cards",
|
||||||
|
title: "Blackjack",
|
||||||
|
description: "Twenty one ways to change your life.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "dollar-sign",
|
||||||
|
title: "Slots",
|
||||||
|
description: "Today's your lucky day.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "dollar-sign",
|
||||||
|
title: "Racing",
|
||||||
|
description: "Make it all back at the track.",
|
||||||
|
},
|
||||||
|
{ icon: "dollar-sign", title: "Crossing", description: "Take a load off." },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function Activity() {
|
||||||
|
return (
|
||||||
|
<div className="Activity">
|
||||||
|
{ACTIVITIES.map((activity) => (
|
||||||
|
<section key={activity.title}>
|
||||||
|
<i className={`fas fa-${activity.icon}`}></i>
|
||||||
|
<h4>{activity.title}</h4>
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./Activity";
|
|
@ -0,0 +1,35 @@
|
||||||
|
.ActivityList {
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ActivityList h4 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ActivityList h4 hr {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ActivityList-activity {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ActivityList-activity-icon {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ActivityList-activity {
|
||||||
|
transition: background 0.4s ease-in-out;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ActivityList-activity:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: #ffffff05;
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
import React from "react";
|
||||||
|
import cx from 'classnames'
|
||||||
|
import { useDrawer } from "../../hooks";
|
||||||
|
import "./ActivityList.css";
|
||||||
|
|
||||||
|
const ACTIVITIES = [
|
||||||
|
{
|
||||||
|
game: "Poker",
|
||||||
|
description: "Know when to hold 'em.",
|
||||||
|
icon: "fas fa-cards",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
game: "Roulette",
|
||||||
|
description: "Table go brrrr.",
|
||||||
|
icon: "fas fa-circle",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
game: "Slots",
|
||||||
|
description: "Is today your lucky day?",
|
||||||
|
icon: "fas fa-dollar-sign",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
game: "Blackjack",
|
||||||
|
description: "Twenty one ways to change your life.",
|
||||||
|
icon: "fas fa-cards",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
game: "Racing",
|
||||||
|
description: "Look at 'em go.",
|
||||||
|
icon: "fas fa-cards",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
game: "Crossing",
|
||||||
|
description: "A simple life.",
|
||||||
|
icon: "fas fa-cards",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
game: "Lottershe",
|
||||||
|
description: "Can't win if you don't play.",
|
||||||
|
icon: "fas fa-ticket",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function ActivityList() {
|
||||||
|
const { toggle } = useDrawer();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ActivityList">
|
||||||
|
<h4>
|
||||||
|
<hr />
|
||||||
|
<span>Activities</span>
|
||||||
|
</h4>
|
||||||
|
<section>
|
||||||
|
{ACTIVITIES.map(({ game, description, icon }) => (
|
||||||
|
<div key={game} role="button" onClick={toggle}>
|
||||||
|
<div className="ActivityList-activity">
|
||||||
|
<div className="ActivityList-activity">
|
||||||
|
<i className={cx("ActivityList-activity-icon", icon)} />
|
||||||
|
<h5>{game}<br /><small>{description}</small></h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<small><i className="far fa-user fa-sm" /> 0</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
.Chat {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 350px;
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Chat-mobile-top {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Chat-mobile-top i {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Chat-mobile-top span {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Chat-side {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1180px) {
|
||||||
|
.Chat-mobile-top {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.Chat-side {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Chat-drawer {
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Chat-typing {
|
||||||
|
height: 18px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.Chat-window {
|
||||||
|
width: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useChat, useDrawer } from "../../hooks";
|
||||||
|
import { ActivityList } from "./ActivityList";
|
||||||
|
import { ChatMessageList } from "./ChatMessage";
|
||||||
|
import { QuotedMessage } from "./QuotedMessage";
|
||||||
|
import { UserInput } from "./UserInput";
|
||||||
|
import { UserList } from "./UserList";
|
||||||
|
import "./Chat.css";
|
||||||
|
|
||||||
|
export function Chat() {
|
||||||
|
const { reveal } = useDrawer();
|
||||||
|
const { online, quote } = useChat();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="Chat" id="chatWrapper">
|
||||||
|
<div className="Chat-window">
|
||||||
|
<div className="Chat-mobile-top">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={() =>
|
||||||
|
reveal({
|
||||||
|
title: "Users in chat",
|
||||||
|
content: (
|
||||||
|
<div className="Chat-drawer">
|
||||||
|
<UserList fluid={true} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i className="far fa-user" /> <span>{online.length}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ChatMessageList />
|
||||||
|
{quote && <QuotedMessage />}
|
||||||
|
<UserInput />
|
||||||
|
</div>
|
||||||
|
<div className="Chat-side">
|
||||||
|
<UserList />
|
||||||
|
{process.env.FEATURES_ACTIVITY && <ActivityList />}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
.ChatHeading {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatHeading i {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React, { useCallback } from "react";
|
||||||
|
import { useChat, useDrawer } from "../../hooks";
|
||||||
|
import { UserList } from "./UserList";
|
||||||
|
import "./ChatHeading.css";
|
||||||
|
|
||||||
|
export function ChatHeading() {
|
||||||
|
const { reveal } = useDrawer();
|
||||||
|
const { online } = useChat();
|
||||||
|
const handleOpenUserListDrawer = useCallback(
|
||||||
|
() =>
|
||||||
|
reveal({
|
||||||
|
title: "Users in chat",
|
||||||
|
content: <UserList fluid={true} />,
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ChatHeading">
|
||||||
|
<div />
|
||||||
|
<div>
|
||||||
|
<i
|
||||||
|
role="button"
|
||||||
|
className="far fa-user"
|
||||||
|
onClick={handleOpenUserListDrawer}
|
||||||
|
/>
|
||||||
|
<em>{online.length} users online</em>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
@keyframes fading-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessage {
|
||||||
|
padding: 0.5rem;
|
||||||
|
padding-right: 3rem;
|
||||||
|
position: relative;
|
||||||
|
animation: fading-in 0.3s ease-in-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessage:nth-of-type(even) {
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessage-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessage-timestamp {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessage-bottom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessage-content {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
word-break: break-all;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessage-button {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessage-delete {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessageList {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ChatMessageList::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
lite-youtube {
|
||||||
|
min-width: min(80vw, 500px);
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import { Username } from "./Username";
|
||||||
|
import { useChat, useRootContext } from "../../hooks";
|
||||||
|
import "./ChatMessage.css";
|
||||||
|
import key from "weak-key";
|
||||||
|
|
||||||
|
interface ChatMessageProps extends ChatSpeakResponse {
|
||||||
|
showUser?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChatMessage({
|
||||||
|
avatar,
|
||||||
|
showUser = true,
|
||||||
|
namecolor,
|
||||||
|
username,
|
||||||
|
hat,
|
||||||
|
text,
|
||||||
|
text_html,
|
||||||
|
text_censored,
|
||||||
|
time,
|
||||||
|
timestamp,
|
||||||
|
}: ChatMessageProps) {
|
||||||
|
const message = {
|
||||||
|
avatar,
|
||||||
|
namecolor,
|
||||||
|
username,
|
||||||
|
hat,
|
||||||
|
text,
|
||||||
|
text_html,
|
||||||
|
text_censored,
|
||||||
|
time,
|
||||||
|
timestamp,
|
||||||
|
};
|
||||||
|
const {
|
||||||
|
username: loggedInUsername,
|
||||||
|
admin,
|
||||||
|
censored,
|
||||||
|
themeColor,
|
||||||
|
} = useRootContext();
|
||||||
|
const { quote, deleteMessage, quoteMessage } = useChat();
|
||||||
|
const content = censored ? text_censored : text_html;
|
||||||
|
const hasMention = content.includes(loggedInUsername);
|
||||||
|
const mentionStyle = hasMention
|
||||||
|
? { backgroundColor: `#${themeColor}55` }
|
||||||
|
: {};
|
||||||
|
const quoteStyle =
|
||||||
|
quote?.username === username && quote?.text === text && quote?.time === time
|
||||||
|
? { borderLeft: `2px solid #${themeColor}` }
|
||||||
|
: {};
|
||||||
|
const style = { ...mentionStyle, ...quoteStyle };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"ChatMessage"} style={style}>
|
||||||
|
{showUser && (
|
||||||
|
<div className="ChatMessage-top">
|
||||||
|
<Username
|
||||||
|
avatar={avatar}
|
||||||
|
name={username}
|
||||||
|
color={namecolor}
|
||||||
|
hat={hat}
|
||||||
|
/>
|
||||||
|
<div className="ChatMessage-timestamp">{timestamp}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="ChatMessage-bottom">
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
className="ChatMessage-content"
|
||||||
|
title={content}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: content,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="ChatMessage-button quote btn"
|
||||||
|
onClick={() => quoteMessage(message)}
|
||||||
|
>
|
||||||
|
<i className="fas fa-reply"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{admin && (
|
||||||
|
<button
|
||||||
|
className="ChatMessage-button ChatMessage-delete quote btn del"
|
||||||
|
onClick={() => deleteMessage(text)}
|
||||||
|
>
|
||||||
|
<i className="fas fa-trash-alt"></i>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChatMessageList() {
|
||||||
|
const { messages } = useChat();
|
||||||
|
const messageWrapper = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
messageWrapper.current.scrollTop = messageWrapper.current.scrollHeight;
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ChatMessageList" ref={messageWrapper}>
|
||||||
|
{messages.map((message, index) => (
|
||||||
|
<ChatMessage
|
||||||
|
key={key(message)}
|
||||||
|
{...message}
|
||||||
|
showUser={message.username !== messages[index - 1]?.username}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
@keyframes sliding-up {
|
||||||
|
from {
|
||||||
|
top: 50px;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.QuotedMessage {
|
||||||
|
position: relative;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-top: 1px solid var(--primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
animation: sliding-up 0.3s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.QuotedMessage-content {
|
||||||
|
margin-left: 1rem;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 420px;
|
||||||
|
max-height: 40px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useChat, useRootContext } from "../../hooks";
|
||||||
|
import { Username } from "./Username";
|
||||||
|
import "./QuotedMessage.css";
|
||||||
|
|
||||||
|
export function QuotedMessage() {
|
||||||
|
const { quote, quoteMessage } = useChat();
|
||||||
|
const { censored } = useRootContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="QuotedMessage">
|
||||||
|
<div>
|
||||||
|
<Username
|
||||||
|
avatar={quote.avatar}
|
||||||
|
color={quote.namecolor}
|
||||||
|
name={quote.username}
|
||||||
|
hat={quote.hat}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="QuotedMessage-content"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: censored ? quote.text_censored : quote.text_html,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={() => quoteMessage(null)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
.UserInput {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.UserInput-emoji {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
import React, {
|
||||||
|
ChangeEvent,
|
||||||
|
KeyboardEvent,
|
||||||
|
FormEvent,
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { useChat, useDrawer, useEmojis } from "../../hooks";
|
||||||
|
import { EmojiDrawer, QuickEmojis } from "../emoji";
|
||||||
|
import "./UserInput.css";
|
||||||
|
|
||||||
|
export function UserInput() {
|
||||||
|
const { draft, sendMessage, updateDraft } = useChat();
|
||||||
|
const { reveal, hide, open } = useDrawer();
|
||||||
|
const builtChatInput = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const { visible, addQuery } = useEmojis();
|
||||||
|
const form = useRef<HTMLFormElement>(null);
|
||||||
|
const [typingOffset, setTypingOffset] = useState(0);
|
||||||
|
const quickEmojis = useMemo(
|
||||||
|
() => visible.slice(0, process.env.QUICK_EMOJIS_MAX_COUNT),
|
||||||
|
[visible]
|
||||||
|
);
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
const input = event.target.value;
|
||||||
|
const [openEmojiToken, closeEmojiToken] = locateEmojiTokens(input);
|
||||||
|
const emojiSegment = input.slice(openEmojiToken + 1, closeEmojiToken + 1);
|
||||||
|
|
||||||
|
updateDraft(input);
|
||||||
|
addQuery(openEmojiToken === -1 ? "" : emojiSegment);
|
||||||
|
setTypingOffset(
|
||||||
|
emojiSegment.length * process.env.APPROXIMATE_CHARACTER_WIDTH
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const handleSendMessage = useCallback(
|
||||||
|
(event?: FormEvent<HTMLFormElement>) => {
|
||||||
|
event?.preventDefault();
|
||||||
|
sendMessage();
|
||||||
|
},
|
||||||
|
[sendMessage]
|
||||||
|
);
|
||||||
|
const handleKeyUp = useCallback(
|
||||||
|
(event: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
handleSendMessage();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[handleSendMessage]
|
||||||
|
);
|
||||||
|
const handleOpenEmojiDrawer = useCallback(
|
||||||
|
() =>
|
||||||
|
reveal({
|
||||||
|
title: "Select an emoji",
|
||||||
|
content: (
|
||||||
|
<EmojiDrawer
|
||||||
|
onSelectEmoji={handleSelectEmoji}
|
||||||
|
onClose={() => builtChatInput.current?.focus()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const handleCloseEmojiDrawer = useCallback(() => {
|
||||||
|
builtChatInput.current?.focus();
|
||||||
|
hide();
|
||||||
|
}, [hide]);
|
||||||
|
|
||||||
|
const handleSelectEmoji = useCallback((emoji: string) => {
|
||||||
|
updateDraft((prev) => `${prev} :${emoji}: `);
|
||||||
|
}, []);
|
||||||
|
const handleInsertQuickEmoji = useCallback(
|
||||||
|
(emoji: string) => {
|
||||||
|
const [openEmojiToken, closeEmojiToken] = locateEmojiTokens(draft);
|
||||||
|
const toReplace = draft.slice(openEmojiToken, closeEmojiToken + 1);
|
||||||
|
|
||||||
|
updateDraft((prev) => prev.replace(toReplace, `:${emoji}: `));
|
||||||
|
addQuery("");
|
||||||
|
|
||||||
|
builtChatInput.current?.focus();
|
||||||
|
},
|
||||||
|
[draft]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form ref={form} className="UserInput" onSubmit={handleSendMessage}>
|
||||||
|
{quickEmojis.length > 0 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: -254,
|
||||||
|
height: 250,
|
||||||
|
left: typingOffset,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column-reverse",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<QuickEmojis
|
||||||
|
emojis={quickEmojis}
|
||||||
|
onSelectEmoji={handleInsertQuickEmoji}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<textarea
|
||||||
|
ref={builtChatInput}
|
||||||
|
id="builtChatInput"
|
||||||
|
className="form-control"
|
||||||
|
style={{
|
||||||
|
minHeight: 50,
|
||||||
|
height: 50,
|
||||||
|
maxHeight: 50,
|
||||||
|
}}
|
||||||
|
minLength={1}
|
||||||
|
maxLength={1000}
|
||||||
|
rows={1}
|
||||||
|
onChange={handleChange}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
placeholder="Message"
|
||||||
|
autoComplete="off"
|
||||||
|
autoFocus={true}
|
||||||
|
value={draft}
|
||||||
|
/>
|
||||||
|
{open ? (
|
||||||
|
<span
|
||||||
|
role="button"
|
||||||
|
onClick={handleCloseEmojiDrawer}
|
||||||
|
className="UserInput-emoji"
|
||||||
|
style={{top: 6}}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<i
|
||||||
|
role="button"
|
||||||
|
onClick={handleOpenEmojiDrawer}
|
||||||
|
className="UserInput-emoji fas fa-smile-beam"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function locateEmojiTokens(text: string) {
|
||||||
|
let openEmojiInputToken = -1;
|
||||||
|
let endEmojiInputToken = -1;
|
||||||
|
|
||||||
|
if (text.length <= 1) {
|
||||||
|
return [openEmojiInputToken, endEmojiInputToken];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const character = text[i];
|
||||||
|
|
||||||
|
if (character === process.env.EMOJI_INPUT_TOKEN) {
|
||||||
|
if (openEmojiInputToken === -1) {
|
||||||
|
openEmojiInputToken = i;
|
||||||
|
} else {
|
||||||
|
openEmojiInputToken = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openEmojiInputToken !== -1) {
|
||||||
|
endEmojiInputToken = openEmojiInputToken;
|
||||||
|
|
||||||
|
while (
|
||||||
|
endEmojiInputToken < text.length - 1 &&
|
||||||
|
text[endEmojiInputToken] !== " "
|
||||||
|
) {
|
||||||
|
endEmojiInputToken++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [openEmojiInputToken, endEmojiInputToken];
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
.UserList {
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.UserList-heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.UserList-heading h4 {
|
||||||
|
margin-right: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.UserList ul:not(.fluid) {
|
||||||
|
max-height: 275px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.UserList ul::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from "react";
|
||||||
|
import cx from "classnames";
|
||||||
|
import { useChat } from "../../hooks";
|
||||||
|
import "./UserList.css";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
fluid?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UserList({ fluid = false }: Props) {
|
||||||
|
const { online } = useChat();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="UserList">
|
||||||
|
<div className="UserList-heading">
|
||||||
|
<h4>Users in chat right now</h4>
|
||||||
|
<div className="Chat-online">
|
||||||
|
<i className="far fa-user fa-sm" /> {online.length}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul className={cx({ fluid })}>
|
||||||
|
{online.map((user) => (
|
||||||
|
<li key={user}>
|
||||||
|
<a href={`/@${user}`}>@{user}</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
.Username {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Username > a {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React from "react";
|
||||||
|
import "./Username.css";
|
||||||
|
|
||||||
|
interface UsernameProps {
|
||||||
|
avatar: string;
|
||||||
|
color: string;
|
||||||
|
name: string;
|
||||||
|
hat?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Username({ avatar, color, name, hat = "" }: UsernameProps) {
|
||||||
|
return (
|
||||||
|
<div className="Username">
|
||||||
|
<div className="profile-pic-20-wrapper">
|
||||||
|
<img alt={name} src={avatar} className="pp20" />
|
||||||
|
{hat && (
|
||||||
|
<img
|
||||||
|
className="avatar-hat profile-pic-20-hat hat"
|
||||||
|
loading="lazy"
|
||||||
|
src={hat}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
className="userlink"
|
||||||
|
style={{ color: `#${color}` }}
|
||||||
|
target="_blank"
|
||||||
|
href={`/@${name}`}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
.UsersTyping {
|
||||||
|
height: 18px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useChat } from "../../hooks";
|
||||||
|
import "./UsersTyping.css";
|
||||||
|
|
||||||
|
export function UsersTyping() {
|
||||||
|
const { typing } = useChat();
|
||||||
|
const [first, second, third, ...rest] = typing;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="UsersTyping">
|
||||||
|
{(() => {
|
||||||
|
if (rest.length > 0) {
|
||||||
|
return `${first}, ${second}, ${third} and ${rest.length} more are typing...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first && second && third) {
|
||||||
|
return `${first}, ${second} and ${third} are typing...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first && second) {
|
||||||
|
return `${first} and ${second} are typing...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
return `${first} is typing...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
export * from "./ActivityList";
|
||||||
|
export * from "./Chat";
|
||||||
|
export * from "./ChatHeading";
|
||||||
|
export * from "./ChatMessage";
|
||||||
|
export * from "./QuotedMessage";
|
||||||
|
export * from "./UserInput";
|
||||||
|
export * from "./UserList";
|
||||||
|
export * from "./Username";
|
||||||
|
export * from "./UsersTyping";
|
|
@ -0,0 +1,6 @@
|
||||||
|
.EmojiDrawer-options {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
import React, {
|
||||||
|
useRef,
|
||||||
|
useMemo,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { FixedSizeGrid, FixedSizeGrid as Grid } from "react-window";
|
||||||
|
import AutoSizer from "react-virtualized-auto-sizer";
|
||||||
|
import { useDrawer, useEmojis } from "../../hooks";
|
||||||
|
import { BaseDrawer } from "../../drawers";
|
||||||
|
import { EmojiGenres } from "./EmojiGenres";
|
||||||
|
import { EmojiMods } from "./EmojiMods";
|
||||||
|
import "./EmojiDrawer.css";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onSelectEmoji(emoji: string): void;
|
||||||
|
onClose?(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EmojiDrawerTabs {
|
||||||
|
Favorites = "Favorites",
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EmojiDrawer({ onSelectEmoji, onClose }: Props) {
|
||||||
|
const {
|
||||||
|
visible,
|
||||||
|
genres,
|
||||||
|
collections,
|
||||||
|
favorites,
|
||||||
|
mostRecentQuery,
|
||||||
|
addQuery,
|
||||||
|
updateVisible,
|
||||||
|
updateFavorites,
|
||||||
|
} = useEmojis();
|
||||||
|
const { hide } = useDrawer();
|
||||||
|
const [modSelection, setModSelection] = useState<EmojiModSelection>({
|
||||||
|
large: false,
|
||||||
|
mirror: false,
|
||||||
|
pat: false,
|
||||||
|
});
|
||||||
|
const [activeGenre, setActiveGenre] = useState<string>(
|
||||||
|
EmojiDrawerTabs.Favorites
|
||||||
|
);
|
||||||
|
const emojiGrid = useMemo(() => {
|
||||||
|
const grid = [];
|
||||||
|
let temp = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < visible.length; i++) {
|
||||||
|
temp.push(visible[i]);
|
||||||
|
|
||||||
|
if (i % 7 === 0) {
|
||||||
|
grid.push([...temp]);
|
||||||
|
temp = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
// Refs
|
||||||
|
const gridRef = useRef<FixedSizeGrid<any>>();
|
||||||
|
const input = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
const handleEmojiGenreChange =
|
||||||
|
/*useCallback(*/
|
||||||
|
(genre: string) => {
|
||||||
|
setActiveGenre(genre);
|
||||||
|
|
||||||
|
if (genre === EmojiDrawerTabs.Favorites) {
|
||||||
|
updateVisible(favorites);
|
||||||
|
} else {
|
||||||
|
updateVisible(collections[genre] ?? []);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/*[favorites, collections]
|
||||||
|
);*/
|
||||||
|
const handleScrollToTop = useCallback(() => {
|
||||||
|
gridRef.current?.scrollToItem({
|
||||||
|
rowIndex: 0,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
const handleSelectEmoji = useCallback(
|
||||||
|
(emoji: string) => {
|
||||||
|
if (modSelection.large) {
|
||||||
|
emoji = `#${emoji}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modSelection.mirror) {
|
||||||
|
emoji = `!${emoji}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modSelection.pat) {
|
||||||
|
emoji = `${emoji}pat`;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectEmoji(emoji);
|
||||||
|
updateFavorites((prev) => Array.from(new Set(prev.concat(emoji))));
|
||||||
|
},
|
||||||
|
[modSelection]
|
||||||
|
);
|
||||||
|
const handleClose = useCallback(() => {
|
||||||
|
hide();
|
||||||
|
onClose();
|
||||||
|
}, [onClose]);
|
||||||
|
|
||||||
|
// Effects
|
||||||
|
// When the user types more, scroll back up.
|
||||||
|
useEffect(() => {
|
||||||
|
handleScrollToTop();
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
// Enter and Esc finish the interaction.
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyup = (event: KeyboardEvent) => {
|
||||||
|
if (["Escape", "Enter"].includes(event.key)) {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("keyup", handleKeyup);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keyup", handleKeyup);
|
||||||
|
};
|
||||||
|
}, [handleClose]);
|
||||||
|
|
||||||
|
// Cell
|
||||||
|
const Cell = useCallback(
|
||||||
|
({ columnIndex, rowIndex, style }) => {
|
||||||
|
const emoji = emojiGrid[rowIndex]?.[columnIndex];
|
||||||
|
|
||||||
|
return emoji ? (
|
||||||
|
<img
|
||||||
|
role="button"
|
||||||
|
onClick={() => handleSelectEmoji(emoji)}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
cursor: "pointer",
|
||||||
|
margin: "1rem",
|
||||||
|
}}
|
||||||
|
loading="lazy"
|
||||||
|
width={60}
|
||||||
|
src={`/e/${emoji}.webp`}
|
||||||
|
alt={emoji}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
[emojiGrid, handleSelectEmoji]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseDrawer onClose={handleClose}>
|
||||||
|
<div className="EmojiDrawer-input">
|
||||||
|
<input
|
||||||
|
ref={input}
|
||||||
|
className="form-control"
|
||||||
|
type="text"
|
||||||
|
onChange={(e) => addQuery(e.target.value)}
|
||||||
|
autoFocus={true}
|
||||||
|
placeholder="Search for emojis..."
|
||||||
|
style={{
|
||||||
|
margin: "1rem",
|
||||||
|
flex: 1,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="EmojiDrawer-options">
|
||||||
|
<EmojiMods
|
||||||
|
selection={modSelection}
|
||||||
|
onChangeSelection={setModSelection}
|
||||||
|
/>
|
||||||
|
<EmojiGenres
|
||||||
|
genres={genres}
|
||||||
|
activeGenre={activeGenre}
|
||||||
|
onGenreChange={handleEmojiGenreChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Results */}
|
||||||
|
{visible.length > 0 && (
|
||||||
|
<AutoSizer>
|
||||||
|
{({ width, height }) => (
|
||||||
|
<Grid
|
||||||
|
ref={gridRef}
|
||||||
|
columnCount={8}
|
||||||
|
columnWidth={64}
|
||||||
|
rowCount={visible.length}
|
||||||
|
rowHeight={64}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
>
|
||||||
|
{Cell}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</AutoSizer>
|
||||||
|
)}
|
||||||
|
{/* Searched, nothing found */}
|
||||||
|
{visible.length === 0 && mostRecentQuery !== "" && (
|
||||||
|
<div>Nothing found.</div>
|
||||||
|
)}
|
||||||
|
{/* Favorites */}
|
||||||
|
{visible.length === 0 &&
|
||||||
|
mostRecentQuery === "" &&
|
||||||
|
favorites.map((favorite) => (
|
||||||
|
<img
|
||||||
|
key={favorite}
|
||||||
|
role="button"
|
||||||
|
onClick={() => handleSelectEmoji(favorite)}
|
||||||
|
style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
margin: "1rem",
|
||||||
|
}}
|
||||||
|
loading="lazy"
|
||||||
|
width={60}
|
||||||
|
src={`/e/${favorite}.webp`}
|
||||||
|
alt={favorite}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</BaseDrawer>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React, { ChangeEvent, useCallback } from "react";
|
||||||
|
import "./EmojiGenres.css";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
genres: string[];
|
||||||
|
activeGenre: string;
|
||||||
|
onGenreChange(genre: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EmojiGenres({ genres, activeGenre, onGenreChange }: Props) {
|
||||||
|
const handleSelect = useCallback((event: ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
onGenreChange(event.target.value);
|
||||||
|
}, [onGenreChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<select onChange={handleSelect} value={activeGenre}>
|
||||||
|
<option value="Favorites">⭐ Favorites ⭐</option>
|
||||||
|
{genres.map((genre) => (
|
||||||
|
<option key={genre} value={genre}>
|
||||||
|
{genre}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
import React, { useCallback, useRef } from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
selection: EmojiModSelection;
|
||||||
|
onChangeSelection(selection: EmojiModSelection): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EmojiMods({ selection, onChangeSelection }: Props) {
|
||||||
|
const largeOption = useRef<HTMLInputElement>(null);
|
||||||
|
const mirrorOption = useRef<HTMLInputElement>(null);
|
||||||
|
const patOption = useRef<HTMLInputElement>(null);
|
||||||
|
const handleChangeSelection = useCallback(() => {
|
||||||
|
onChangeSelection({
|
||||||
|
large: largeOption.current.checked,
|
||||||
|
mirror: mirrorOption.current.checked,
|
||||||
|
pat: patOption.current.checked,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="EmojiMods">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="largeOption"
|
||||||
|
ref={largeOption}
|
||||||
|
onChange={handleChangeSelection}
|
||||||
|
checked={selection.large}
|
||||||
|
style={{ marginLeft: "1rem", marginRight: "0.5rem" }}
|
||||||
|
></input>
|
||||||
|
<label htmlFor="largeOption" style={{ marginRight: "1rem" }}>
|
||||||
|
Large
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="mirrorOption"
|
||||||
|
ref={mirrorOption}
|
||||||
|
onChange={handleChangeSelection}
|
||||||
|
checked={selection.mirror}
|
||||||
|
style={{ marginRight: "0.5rem" }}
|
||||||
|
></input>
|
||||||
|
<label htmlFor="mirrorOption" style={{ marginRight: "1rem" }}>
|
||||||
|
Mirror
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="patOption"
|
||||||
|
ref={patOption}
|
||||||
|
onChange={handleChangeSelection}
|
||||||
|
checked={selection.pat}
|
||||||
|
style={{ marginRight: "0.5rem" }}
|
||||||
|
></input>
|
||||||
|
<label htmlFor="patOption">Pat</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
emojis: string[];
|
||||||
|
onSelectEmoji(emoji: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function QuickEmojis({ emojis, onSelectEmoji }: Props) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: "var(--gray-700)",
|
||||||
|
maxHeight: 250,
|
||||||
|
overflowY: "auto",
|
||||||
|
overflowX: "hidden",
|
||||||
|
borderRadius: "4px",
|
||||||
|
border: "1px solid rgba(255, 255, 255, 0.3)",
|
||||||
|
boxShadow: "0px 2px 5px rgb(0 0 0 / 20%)",
|
||||||
|
zIndex: 999,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{emojis.map((emoji) => (
|
||||||
|
<div
|
||||||
|
key={emoji}
|
||||||
|
role="button"
|
||||||
|
onClick={() => onSelectEmoji(emoji)}
|
||||||
|
style={{
|
||||||
|
borderBottom: "1px solid #606060",
|
||||||
|
padding: "4px",
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={`/e/${emoji}.webp`}
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>{emoji}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from "./EmojiDrawer";
|
||||||
|
export * from "./EmojiGenres";
|
||||||
|
export * from "./EmojiMods";
|
||||||
|
export * from "./QuickEmojis";
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./activity";
|
||||||
|
export * from "./chat";
|
||||||
|
export * from "./emoji";
|
|
@ -0,0 +1,5 @@
|
||||||
|
export * from "./useChat";
|
||||||
|
export * from "./useDrawer";
|
||||||
|
export * from "./useEmojis";
|
||||||
|
export * from "./useRootContext";
|
||||||
|
export * from "./useWindowFocus";
|
|
@ -0,0 +1,172 @@
|
||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
PropsWithChildren,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { io, Socket } from "socket.io-client";
|
||||||
|
import lozad from "lozad";
|
||||||
|
import { useRootContext } from "./useRootContext";
|
||||||
|
import { useWindowFocus } from "./useWindowFocus";
|
||||||
|
|
||||||
|
enum ChatHandlers {
|
||||||
|
CONNECT = "connect",
|
||||||
|
CATCHUP = "catchup",
|
||||||
|
ONLINE = "online",
|
||||||
|
TYPING = "typing",
|
||||||
|
DELETE = "delete",
|
||||||
|
SPEAK = "speak",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChatProviderContext {
|
||||||
|
online: string[];
|
||||||
|
typing: string[];
|
||||||
|
messages: ChatSpeakResponse[];
|
||||||
|
draft: string;
|
||||||
|
quote: null | ChatSpeakResponse;
|
||||||
|
updateDraft: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
sendMessage(): void;
|
||||||
|
quoteMessage(message: null | ChatSpeakResponse): void;
|
||||||
|
deleteMessage(withText: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChatContext = createContext<ChatProviderContext>({
|
||||||
|
online: [],
|
||||||
|
typing: [],
|
||||||
|
messages: [],
|
||||||
|
draft: "",
|
||||||
|
quote: null,
|
||||||
|
updateDraft() {},
|
||||||
|
sendMessage() {},
|
||||||
|
quoteMessage() {},
|
||||||
|
deleteMessage() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function ChatProvider({ children }: PropsWithChildren) {
|
||||||
|
const { username, siteName } = useRootContext();
|
||||||
|
const socket = useRef<null | Socket>(null);
|
||||||
|
const [online, setOnline] = useState<string[]>([]);
|
||||||
|
const [typing, setTyping] = useState<string[]>([]);
|
||||||
|
const [messages, setMessages] = useState<ChatSpeakResponse[]>([]);
|
||||||
|
const [draft, setDraft] = useState("");
|
||||||
|
const [quote, setQuote] = useState<null | ChatSpeakResponse>(null);
|
||||||
|
const focused = useWindowFocus();
|
||||||
|
const [notifications, setNotifications] = useState<number>(0);
|
||||||
|
const addMessage = useCallback((message: ChatSpeakResponse) => {
|
||||||
|
setMessages((prev) => prev.concat(message));
|
||||||
|
|
||||||
|
if (message.username !== username && !document.hasFocus()) {
|
||||||
|
setNotifications((prev) => prev + 1);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
const sendMessage = useCallback(() => {
|
||||||
|
const message = quote
|
||||||
|
? `> ${quote.text}\n@${quote.username}<br /><br />${draft}`
|
||||||
|
: draft;
|
||||||
|
socket.current?.emit(ChatHandlers.SPEAK, message);
|
||||||
|
|
||||||
|
setQuote(null);
|
||||||
|
setDraft("");
|
||||||
|
}, [draft]);
|
||||||
|
const requestDeleteMessage = useCallback((withText: string) => {
|
||||||
|
socket.current?.emit(ChatHandlers.DELETE, withText);
|
||||||
|
}, []);
|
||||||
|
const deleteMessage = useCallback((withText: string) => {
|
||||||
|
setMessages((prev) =>
|
||||||
|
prev.filter((prevMessage) => prevMessage.text !== withText)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (quote?.text === withText) {
|
||||||
|
setQuote(null);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
const quoteMessage = useCallback((message: ChatSpeakResponse) => {
|
||||||
|
setQuote(message);
|
||||||
|
|
||||||
|
try {
|
||||||
|
document.getElementById("builtChatInput").focus();
|
||||||
|
} catch (error) {}
|
||||||
|
}, []);
|
||||||
|
const context = useMemo<ChatProviderContext>(
|
||||||
|
() => ({
|
||||||
|
online,
|
||||||
|
typing,
|
||||||
|
messages,
|
||||||
|
draft,
|
||||||
|
quote,
|
||||||
|
quoteMessage,
|
||||||
|
sendMessage,
|
||||||
|
deleteMessage: requestDeleteMessage,
|
||||||
|
updateDraft: setDraft,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
online,
|
||||||
|
typing,
|
||||||
|
messages,
|
||||||
|
draft,
|
||||||
|
quote,
|
||||||
|
sendMessage,
|
||||||
|
deleteMessage,
|
||||||
|
quoteMessage,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!socket.current) {
|
||||||
|
socket.current = io();
|
||||||
|
|
||||||
|
socket.current
|
||||||
|
.on(ChatHandlers.CATCHUP, setMessages)
|
||||||
|
.on(ChatHandlers.ONLINE, setOnline)
|
||||||
|
.on(ChatHandlers.TYPING, setTyping)
|
||||||
|
.on(ChatHandlers.SPEAK, addMessage)
|
||||||
|
.on(ChatHandlers.DELETE, deleteMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.current?.emit(ChatHandlers.TYPING, draft);
|
||||||
|
}, [draft]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (focused || document.hasFocus()) {
|
||||||
|
setNotifications(0);
|
||||||
|
}
|
||||||
|
}, [focused]);
|
||||||
|
|
||||||
|
// Display e.g. [+2] Chat when notifications occur when you're away.
|
||||||
|
useEffect(() => {
|
||||||
|
const title = document.getElementsByTagName("title")[0];
|
||||||
|
const favicon = document.getElementById("favicon") as HTMLLinkElement;
|
||||||
|
const escape = (window as any).escapeHTML;
|
||||||
|
const alertedWhileAway = notifications > 0 && !focused;
|
||||||
|
const pathIcon = alertedWhileAway ? "alert" : "icon";
|
||||||
|
|
||||||
|
favicon.href = escape(`/assets/images/${siteName}/${pathIcon}.webp?v=3`);
|
||||||
|
title.innerHTML = alertedWhileAway ? `[+${notifications}] Chat` : "Chat";
|
||||||
|
}, [notifications, focused]);
|
||||||
|
|
||||||
|
// Setup Lozad
|
||||||
|
useEffect(() => {
|
||||||
|
const { observe, observer } = lozad();
|
||||||
|
|
||||||
|
observe();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatContext.Provider value={context}>{children}</ChatContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useChat() {
|
||||||
|
const value = useContext(ChatContext);
|
||||||
|
return value;
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
import React, {
|
||||||
|
ReactNode,
|
||||||
|
createContext,
|
||||||
|
PropsWithChildren,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { useDrag } from "react-dnd";
|
||||||
|
|
||||||
|
interface DrawerConfig {
|
||||||
|
title?: string;
|
||||||
|
content: ReactNode;
|
||||||
|
actions?: Array<{ title: string; onClick(): void }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DrawerContextType {
|
||||||
|
config: DrawerConfig;
|
||||||
|
open: boolean;
|
||||||
|
coordinates: [number, number];
|
||||||
|
reveal(config: DrawerConfig): void;
|
||||||
|
hide(): void;
|
||||||
|
show(): void;
|
||||||
|
toggle(): void;
|
||||||
|
updateCoordinates(to: [number, number]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DRAWER_COORDINATES_STORAGE_KEY = "Drawer/Coordinates";
|
||||||
|
|
||||||
|
const DEFAULT_DRAWER_CONFIG = {
|
||||||
|
title: "",
|
||||||
|
content: null,
|
||||||
|
actions: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const DrawerContext = createContext<DrawerContextType>({
|
||||||
|
config: DEFAULT_DRAWER_CONFIG,
|
||||||
|
open: false,
|
||||||
|
coordinates: [0, 0] as [number, number],
|
||||||
|
reveal() {},
|
||||||
|
hide() {},
|
||||||
|
show() {},
|
||||||
|
toggle() {},
|
||||||
|
updateCoordinates() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useDrawer() {
|
||||||
|
const values = useContext(DrawerContext);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DrawerProvider({ children }: PropsWithChildren) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [config, setConfig] = useState<DrawerConfig>(DEFAULT_DRAWER_CONFIG);
|
||||||
|
const [coordinates, setCoordinates] = useState([0, 0] as [number, number]);
|
||||||
|
const reveal = useCallback((config: DrawerConfig) => {
|
||||||
|
setConfig(config);
|
||||||
|
show();
|
||||||
|
}, []);
|
||||||
|
const hide = useCallback(() => {
|
||||||
|
setOpen(false);
|
||||||
|
}, []);
|
||||||
|
const show = useCallback(() => {
|
||||||
|
setOpen(true);
|
||||||
|
}, []);
|
||||||
|
const toggle = useCallback(() => {
|
||||||
|
setOpen((prev) => !prev);
|
||||||
|
}, []);
|
||||||
|
const context = useMemo(
|
||||||
|
() => ({
|
||||||
|
config,
|
||||||
|
open,
|
||||||
|
coordinates,
|
||||||
|
reveal,
|
||||||
|
hide,
|
||||||
|
show,
|
||||||
|
toggle,
|
||||||
|
updateCoordinates: setCoordinates,
|
||||||
|
}),
|
||||||
|
[config, open, coordinates, reveal, hide, show, toggle]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load coordinates.
|
||||||
|
useEffect(() => {
|
||||||
|
const persisted = window.localStorage.getItem(
|
||||||
|
DRAWER_COORDINATES_STORAGE_KEY
|
||||||
|
);
|
||||||
|
|
||||||
|
if (persisted) {
|
||||||
|
setCoordinates(JSON.parse(persisted));
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Persist coordinates.
|
||||||
|
useEffect(() => {
|
||||||
|
window.localStorage.setItem(
|
||||||
|
DRAWER_COORDINATES_STORAGE_KEY,
|
||||||
|
JSON.stringify(coordinates)
|
||||||
|
);
|
||||||
|
}, [coordinates]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DrawerContext.Provider value={context}>{children}</DrawerContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Drawer() {
|
||||||
|
const {
|
||||||
|
config: { title = "", content, actions = [] },
|
||||||
|
open,
|
||||||
|
coordinates,
|
||||||
|
updateCoordinates,
|
||||||
|
hide,
|
||||||
|
} = useDrawer();
|
||||||
|
const [x, y] = coordinates;
|
||||||
|
const [{ isDragging }, dragRef] = useDrag({
|
||||||
|
type: "drawer",
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragging: monitor.isDragging(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const lastMousePosition = useRef([0, 0]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMouseMove = (event: MouseEvent) =>
|
||||||
|
(lastMousePosition.current = [event.clientX, event.clientY]);
|
||||||
|
|
||||||
|
window.addEventListener("mousemove", handleMouseMove);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
const chatWrapper = document.getElementById("chatWrapper");
|
||||||
|
const chatWrapperBox = chatWrapper.getBoundingClientRect();
|
||||||
|
|
||||||
|
updateCoordinates([chatWrapperBox.left, chatWrapperBox.top]);
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDragging) {
|
||||||
|
const onRelease = (event: DragEvent) => {
|
||||||
|
const drawer = document.getElementById("drawer");
|
||||||
|
const drawerBox = drawer.getBoundingClientRect();
|
||||||
|
const [mouseX, mouseY] = lastMousePosition.current;
|
||||||
|
const xDiff = mouseX - drawerBox.left;
|
||||||
|
const yDiff = mouseY - drawerBox.top;
|
||||||
|
|
||||||
|
updateCoordinates([event.clientX - xDiff, event.clientY - yDiff]);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("drop", onRelease);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("drop", onRelease);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [isDragging]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="drawer"
|
||||||
|
className="App-drawer"
|
||||||
|
ref={dragRef}
|
||||||
|
style={{
|
||||||
|
top: y,
|
||||||
|
left: x,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="App-drawer--close btn btn-default"
|
||||||
|
onClick={hide}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</button>
|
||||||
|
<div className="App-drawer--content">{content}</div>
|
||||||
|
{actions.map((action) => (
|
||||||
|
<button
|
||||||
|
key={action.title}
|
||||||
|
type="button"
|
||||||
|
onClick={action.onClick}
|
||||||
|
className="btn btn-secondary"
|
||||||
|
>
|
||||||
|
{action.title}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,230 @@
|
||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import debounce from "lodash.debounce";
|
||||||
|
|
||||||
|
const FAVORITES_STORAGE_KEY = "Emojis/Favorites";
|
||||||
|
const MINIMUM_SEARCH_INTERVAL = 350;
|
||||||
|
|
||||||
|
interface MarseyListEmoji {
|
||||||
|
author: string;
|
||||||
|
class: string;
|
||||||
|
count: number;
|
||||||
|
name: string;
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useEmojis() {
|
||||||
|
const emojiDictionary = useRef(new EmojiDictionary());
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [emojis, setEmojis] = useState<MarseyListEmoji[]>([]);
|
||||||
|
const [genres, setGenres] = useState<string[]>([]);
|
||||||
|
const [collections, setCollections] = useState<Record<string, string[]>>({});
|
||||||
|
const [favorites, setFavorites] = useState<string[]>([]);
|
||||||
|
const [queries, setQueries] = useState<string[]>([]);
|
||||||
|
const [mostRecentQuery, setMostRecentQuery] = useState("");
|
||||||
|
const [visible, setVisible] = useState<string[]>([]);
|
||||||
|
const addQuery = useCallback(
|
||||||
|
(query: string) => setQueries((prev) => prev.concat(query)),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const debouncedQueryAdder = useMemo(
|
||||||
|
() => debounce(addQuery, MINIMUM_SEARCH_INTERVAL),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Retrieve the list.
|
||||||
|
useEffect(() => {
|
||||||
|
fetch("/marsey_list.json")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then(setEmojis)
|
||||||
|
.catch(setError);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Load favorites.
|
||||||
|
useEffect(() => {
|
||||||
|
const persisted = window.localStorage.getItem(FAVORITES_STORAGE_KEY);
|
||||||
|
|
||||||
|
if (persisted) {
|
||||||
|
setFavorites(JSON.parse(persisted));
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Persist favorites.
|
||||||
|
useEffect(() => {
|
||||||
|
window.localStorage.setItem(
|
||||||
|
FAVORITES_STORAGE_KEY,
|
||||||
|
JSON.stringify(Array.from(new Set(favorites)))
|
||||||
|
);
|
||||||
|
}, [favorites]);
|
||||||
|
|
||||||
|
// When emojis are received, update the dictionary.
|
||||||
|
useEffect(() => {
|
||||||
|
const dictionary = emojiDictionary.current;
|
||||||
|
const genreCollections: Record<string, string[]> = {};
|
||||||
|
|
||||||
|
for (const emoji of emojis) {
|
||||||
|
dictionary.updateTag(emoji.name, emoji.name);
|
||||||
|
|
||||||
|
if (typeof emoji.author !== "undefined" && emoji.author !== null) {
|
||||||
|
dictionary.updateTag(emoji.author.toLowerCase(), emoji.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emoji.tags instanceof Array) {
|
||||||
|
for (const tag of emoji.tags) {
|
||||||
|
dictionary.updateTag(tag, emoji.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary.classes.add(emoji.class);
|
||||||
|
|
||||||
|
if (!genreCollections[emoji.class]) {
|
||||||
|
genreCollections[emoji.class] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
genreCollections[emoji.class].push(emoji.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
setGenres(Array.from(dictionary.classes.values()) as string[]);
|
||||||
|
setCollections(genreCollections);
|
||||||
|
}, [emojis]);
|
||||||
|
|
||||||
|
// Process queries as they come in.
|
||||||
|
useEffect(() => {
|
||||||
|
if (queries.length > 0) {
|
||||||
|
const lastQuery = queries[queries.length - 1].toLowerCase();
|
||||||
|
|
||||||
|
setQueries([]);
|
||||||
|
setMostRecentQuery(lastQuery);
|
||||||
|
|
||||||
|
if (lastQuery.length === 0) {
|
||||||
|
return setVisible([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = emojiDictionary.current.completeSearch(lastQuery);
|
||||||
|
const nextVisible = Array.from(results.values()) as string[];
|
||||||
|
|
||||||
|
setVisible(nextVisible);
|
||||||
|
}
|
||||||
|
}, [queries]);
|
||||||
|
|
||||||
|
// Clean up any debounced calls before exit.
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
debouncedQueryAdder.cancel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
error,
|
||||||
|
ready: emojis.length > 0,
|
||||||
|
visible,
|
||||||
|
genres,
|
||||||
|
collections,
|
||||||
|
favorites,
|
||||||
|
mostRecentQuery,
|
||||||
|
addQuery,
|
||||||
|
updateVisible: setVisible,
|
||||||
|
updateFavorites: setFavorites
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmojiDictionaryNode {
|
||||||
|
tag = "";
|
||||||
|
names = [];
|
||||||
|
|
||||||
|
constructor(tag: string, name: string) {
|
||||||
|
this.tag = tag;
|
||||||
|
this.names = [name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmojiDictionary {
|
||||||
|
dictionary = [];
|
||||||
|
classes = new Set();
|
||||||
|
|
||||||
|
updateTag(tag: string, name: string) {
|
||||||
|
let low = 0;
|
||||||
|
let high = this.dictionary.length;
|
||||||
|
|
||||||
|
while (low < high) {
|
||||||
|
let mid = (low + high) >>> 1;
|
||||||
|
|
||||||
|
if (this.dictionary[mid].tag.length < tag.length) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = low;
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof this.dictionary[target] !== "undefined" &&
|
||||||
|
this.dictionary[target].tag === tag
|
||||||
|
) {
|
||||||
|
this.dictionary[target].names.push(name);
|
||||||
|
} else {
|
||||||
|
this.dictionary.splice(target, 0, new EmojiDictionaryNode(tag, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
searchFor(query: string) {
|
||||||
|
query = query.toLowerCase();
|
||||||
|
const result = new Set();
|
||||||
|
|
||||||
|
if (this.dictionary.length === 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
let low = 0;
|
||||||
|
let high = this.dictionary.length;
|
||||||
|
|
||||||
|
while (low < high) {
|
||||||
|
let mid = (low + high) >>> 1;
|
||||||
|
|
||||||
|
if (this.dictionary[mid].tag.length < query.length) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = low;
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = target;
|
||||||
|
i >= 0 && this.dictionary[i].tag.startsWith(query);
|
||||||
|
i--
|
||||||
|
) {
|
||||||
|
for (const name of this.dictionary[i].names) {
|
||||||
|
result.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = target + 1;
|
||||||
|
i < this.dictionary.length && this.dictionary[i].tag.startsWith(query);
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
for (const name of this.dictionary[i].names) {
|
||||||
|
result.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
completeSearch(query: string) {
|
||||||
|
query = query.toLowerCase();
|
||||||
|
const result = new Set();
|
||||||
|
|
||||||
|
for (const { tag, names } of this.dictionary) {
|
||||||
|
if (tag.includes(query)) {
|
||||||
|
for (const name of names) {
|
||||||
|
result.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export function useRootContext() {
|
||||||
|
const [{ admin, id, username, censored, themeColor, siteName }, setContext] =
|
||||||
|
useState({
|
||||||
|
id: "",
|
||||||
|
username: "",
|
||||||
|
admin: false,
|
||||||
|
censored: true,
|
||||||
|
themeColor: "#ff66ac",
|
||||||
|
siteName: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const root = document.getElementById("root");
|
||||||
|
|
||||||
|
setContext({
|
||||||
|
id: root.dataset.id,
|
||||||
|
username: root.dataset.username,
|
||||||
|
admin: root.dataset.admin === "True",
|
||||||
|
censored: root.dataset.censored === "True",
|
||||||
|
themeColor: root.dataset.themecolor,
|
||||||
|
siteName: root.dataset.sitename,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { id, admin, username, censored, themeColor, siteName };
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export function useWindowFocus() {
|
||||||
|
const [focused, setFocused] = useState(true);
|
||||||
|
const onFocus = useCallback(() => setFocused(true), []);
|
||||||
|
const onBlur = useCallback(() => setFocused(false), []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("focus", onFocus);
|
||||||
|
window.addEventListener("blur", onBlur);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("focus", onFocus);
|
||||||
|
window.removeEventListener("blur", onBlur);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return focused;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import React from "react";
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { App } from "./App";
|
||||||
|
|
||||||
|
const root = createRoot(document.getElementById("root"))
|
||||||
|
|
||||||
|
root.render(<App />);
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"lib": ["es2015", "dom", "ESNext"],
|
||||||
|
"noEmit": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,522 @@
|
||||||
|
Arguments:
|
||||||
|
/usr/local/Cellar/node/18.2.0/bin/node /usr/local/bin/yarn add react-dnd react-dnd-htm5-backend
|
||||||
|
|
||||||
|
PATH:
|
||||||
|
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin:/usr/local/share/dotnet:~/.dotnet/tools:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands
|
||||||
|
|
||||||
|
Yarn version:
|
||||||
|
1.22.10
|
||||||
|
|
||||||
|
Node version:
|
||||||
|
18.2.0
|
||||||
|
|
||||||
|
Platform:
|
||||||
|
darwin x64
|
||||||
|
|
||||||
|
Trace:
|
||||||
|
Error: https://registry.yarnpkg.com/react-dnd-htm5-backend: Not found
|
||||||
|
at params.callback [as _callback] (/usr/local/lib/node_modules/yarn/lib/cli.js:66988:18)
|
||||||
|
at self.callback (/usr/local/lib/node_modules/yarn/lib/cli.js:140662:22)
|
||||||
|
at Request.emit (node:events:527:28)
|
||||||
|
at Request.<anonymous> (/usr/local/lib/node_modules/yarn/lib/cli.js:141634:10)
|
||||||
|
at Request.emit (node:events:527:28)
|
||||||
|
at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/yarn/lib/cli.js:141556:12)
|
||||||
|
at Object.onceWrapper (node:events:641:28)
|
||||||
|
at IncomingMessage.emit (node:events:539:35)
|
||||||
|
at endReadableNT (node:internal/streams/readable:1344:12)
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
|
||||||
|
|
||||||
|
npm manifest:
|
||||||
|
{
|
||||||
|
"name": "chat",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lozad": "^1.16.1",
|
||||||
|
"@types/react": "^18.0.20",
|
||||||
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"@types/react-window": "^1.8.5",
|
||||||
|
"esbuild": "^0.15.7",
|
||||||
|
"lozad": "^1.16.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-window": "^1.8.7",
|
||||||
|
"run-when-changed": "^2.1.0",
|
||||||
|
"socket.io-client": "^4.5.2",
|
||||||
|
"typescript": "^4.8.3",
|
||||||
|
"weak-key": "^1.0.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"chat": "yarn check && yarn build && yarn css:move",
|
||||||
|
"chat:watch": "run-when-changed --watch \"**/*.*\" --exec \"yarn chat\"",
|
||||||
|
"check": "tsc",
|
||||||
|
"build": "esbuild ./src --outfile=../files/assets/js/chat_built.js --bundle",
|
||||||
|
"css:move": "mv ../files/assets/js/chat_built.css ../files/assets/css/chat_built.css"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yarn manifest:
|
||||||
|
No manifest
|
||||||
|
|
||||||
|
Lockfile:
|
||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@babel/runtime@^7.0.0":
|
||||||
|
version "7.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
|
||||||
|
integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64@0.15.7":
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz#1ec4af4a16c554cbd402cc557ccdd874e3f7be53"
|
||||||
|
integrity sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw==
|
||||||
|
|
||||||
|
"@socket.io/component-emitter@~3.1.0":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
|
||||||
|
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
|
||||||
|
|
||||||
|
"@types/lozad@^1.16.1":
|
||||||
|
version "1.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lozad/-/lozad-1.16.1.tgz#fc06f73bfd787a456b0e12a9d83a954b56431400"
|
||||||
|
integrity sha512-wmGW368y1zu9ark/7fMNHdZ/c3wGV7fxoEqtK+V3Dsv2QGQQp34D9elDowuW12zZwxjO9NkNUorqg+D9j1VRGQ==
|
||||||
|
|
||||||
|
"@types/prop-types@*":
|
||||||
|
version "15.7.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||||
|
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||||
|
|
||||||
|
"@types/react-dom@^18.0.6":
|
||||||
|
version "18.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1"
|
||||||
|
integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-window@^1.8.5":
|
||||||
|
version "1.8.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.5.tgz#285fcc5cea703eef78d90f499e1457e9b5c02fc1"
|
||||||
|
integrity sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react@*", "@types/react@^18.0.20":
|
||||||
|
version "18.0.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.20.tgz#e4c36be3a55eb5b456ecf501bd4a00fd4fd0c9ab"
|
||||||
|
integrity sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA==
|
||||||
|
dependencies:
|
||||||
|
"@types/prop-types" "*"
|
||||||
|
"@types/scheduler" "*"
|
||||||
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@types/scheduler@*":
|
||||||
|
version "0.16.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
|
||||||
|
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
|
||||||
|
|
||||||
|
ansi-bold@^0.1.1:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-bold/-/ansi-bold-0.1.1.tgz#3e63950af5acc2ae2e670e6f67deb115d1a5f505"
|
||||||
|
integrity sha512-wWKwcViX1E28U6FohtWOP4sHFyArELHJ2p7+3BzbibqJiuISeskq6t7JnrLisUngMF5zMhgmXVw8Equjzz9OlA==
|
||||||
|
dependencies:
|
||||||
|
ansi-wrap "0.1.0"
|
||||||
|
|
||||||
|
ansi-wrap@0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
|
||||||
|
integrity sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==
|
||||||
|
|
||||||
|
balanced-match@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
|
brace-expansion@^1.1.7:
|
||||||
|
version "1.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
|
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||||
|
dependencies:
|
||||||
|
balanced-match "^1.0.0"
|
||||||
|
concat-map "0.0.1"
|
||||||
|
|
||||||
|
commander@^2.15.1:
|
||||||
|
version "2.20.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
|
concat-map@0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
|
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||||
|
|
||||||
|
core-js@^2.4.0:
|
||||||
|
version "2.6.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||||
|
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||||
|
|
||||||
|
csstype@^3.0.2:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
|
||||||
|
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
|
||||||
|
|
||||||
|
debug@~4.3.1, debug@~4.3.2:
|
||||||
|
version "4.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
|
engine.io-client@~6.2.1:
|
||||||
|
version "6.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.2.tgz#c6c5243167f5943dcd9c4abee1bfc634aa2cbdd0"
|
||||||
|
integrity sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.1"
|
||||||
|
engine.io-parser "~5.0.3"
|
||||||
|
ws "~8.2.3"
|
||||||
|
xmlhttprequest-ssl "~2.0.0"
|
||||||
|
|
||||||
|
engine.io-parser@~5.0.3:
|
||||||
|
version "5.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
|
||||||
|
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
|
||||||
|
|
||||||
|
es6-promise@^3.0.2:
|
||||||
|
version "3.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
|
||||||
|
integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==
|
||||||
|
|
||||||
|
esbuild-android-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.7.tgz#a521604d8c4c6befc7affedc897df8ccde189bea"
|
||||||
|
integrity sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w==
|
||||||
|
|
||||||
|
esbuild-android-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.7.tgz#307b81f1088bf1e81dfe5f3d1d63a2d2a2e3e68e"
|
||||||
|
integrity sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ==
|
||||||
|
|
||||||
|
esbuild-darwin-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz#270117b0c4ec6bcbc5cf3a297a7d11954f007e11"
|
||||||
|
integrity sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg==
|
||||||
|
|
||||||
|
esbuild-darwin-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.7.tgz#97851eacd11dacb7719713602e3319e16202fc77"
|
||||||
|
integrity sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ==
|
||||||
|
|
||||||
|
esbuild-freebsd-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.7.tgz#1de15ffaf5ae916aa925800aa6d02579960dd8c4"
|
||||||
|
integrity sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ==
|
||||||
|
|
||||||
|
esbuild-freebsd-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.7.tgz#0f160dbf5c9a31a1d8dd87acbbcb1a04b7031594"
|
||||||
|
integrity sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q==
|
||||||
|
|
||||||
|
esbuild-linux-32@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.7.tgz#422eb853370a5e40bdce8b39525380de11ccadec"
|
||||||
|
integrity sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg==
|
||||||
|
|
||||||
|
esbuild-linux-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.7.tgz#f89c468453bb3194b14f19dc32e0b99612e81d2b"
|
||||||
|
integrity sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ==
|
||||||
|
|
||||||
|
esbuild-linux-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.7.tgz#68a79d6eb5e032efb9168a0f340ccfd33d6350a1"
|
||||||
|
integrity sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw==
|
||||||
|
|
||||||
|
esbuild-linux-arm@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.7.tgz#2b7c784d0b3339878013dfa82bf5eaf82c7ce7d3"
|
||||||
|
integrity sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ==
|
||||||
|
|
||||||
|
esbuild-linux-mips64le@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.7.tgz#bb8330a50b14aa84673816cb63cc6c8b9beb62cc"
|
||||||
|
integrity sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw==
|
||||||
|
|
||||||
|
esbuild-linux-ppc64le@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.7.tgz#52544e7fa992811eb996674090d0bc41f067a14b"
|
||||||
|
integrity sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw==
|
||||||
|
|
||||||
|
esbuild-linux-riscv64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.7.tgz#a43ae60697992b957e454cbb622f7ee5297e8159"
|
||||||
|
integrity sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g==
|
||||||
|
|
||||||
|
esbuild-linux-s390x@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.7.tgz#8c76a125dd10a84c166294d77416caaf5e1c7b64"
|
||||||
|
integrity sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ==
|
||||||
|
|
||||||
|
esbuild-netbsd-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.7.tgz#19b2e75449d7d9c32b5d8a222bac2f1e0c3b08fd"
|
||||||
|
integrity sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ==
|
||||||
|
|
||||||
|
esbuild-openbsd-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.7.tgz#1357b2bf72fd037d9150e751420a1fe4c8618ad7"
|
||||||
|
integrity sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ==
|
||||||
|
|
||||||
|
esbuild-sunos-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.7.tgz#87ab2c604592a9c3c763e72969da0d72bcde91d2"
|
||||||
|
integrity sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag==
|
||||||
|
|
||||||
|
esbuild-windows-32@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.7.tgz#c81e688c0457665a8d463a669e5bf60870323e99"
|
||||||
|
integrity sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA==
|
||||||
|
|
||||||
|
esbuild-windows-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.7.tgz#2421d1ae34b0561a9d6767346b381961266c4eff"
|
||||||
|
integrity sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q==
|
||||||
|
|
||||||
|
esbuild-windows-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.7.tgz#7d5e9e060a7b454cb2f57f84a3f3c23c8f30b7d2"
|
||||||
|
integrity sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw==
|
||||||
|
|
||||||
|
esbuild@^0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.7.tgz#8a1f1aff58671a3199dd24df95314122fc1ddee8"
|
||||||
|
integrity sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@esbuild/linux-loong64" "0.15.7"
|
||||||
|
esbuild-android-64 "0.15.7"
|
||||||
|
esbuild-android-arm64 "0.15.7"
|
||||||
|
esbuild-darwin-64 "0.15.7"
|
||||||
|
esbuild-darwin-arm64 "0.15.7"
|
||||||
|
esbuild-freebsd-64 "0.15.7"
|
||||||
|
esbuild-freebsd-arm64 "0.15.7"
|
||||||
|
esbuild-linux-32 "0.15.7"
|
||||||
|
esbuild-linux-64 "0.15.7"
|
||||||
|
esbuild-linux-arm "0.15.7"
|
||||||
|
esbuild-linux-arm64 "0.15.7"
|
||||||
|
esbuild-linux-mips64le "0.15.7"
|
||||||
|
esbuild-linux-ppc64le "0.15.7"
|
||||||
|
esbuild-linux-riscv64 "0.15.7"
|
||||||
|
esbuild-linux-s390x "0.15.7"
|
||||||
|
esbuild-netbsd-64 "0.15.7"
|
||||||
|
esbuild-openbsd-64 "0.15.7"
|
||||||
|
esbuild-sunos-64 "0.15.7"
|
||||||
|
esbuild-windows-32 "0.15.7"
|
||||||
|
esbuild-windows-64 "0.15.7"
|
||||||
|
esbuild-windows-arm64 "0.15.7"
|
||||||
|
|
||||||
|
fs-find-root@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-find-root/-/fs-find-root-2.0.0.tgz#71c23b384db6bcb1e8ec637cade707fda39c593b"
|
||||||
|
integrity sha512-LmgsxDwnxd+sfm3EZ66P8nSlUMm69hKz/LdXKChK2a+5xXnrAB7MPn2uo0VCyrgj8lZNqyj6EIdD516ILZAEBg==
|
||||||
|
dependencies:
|
||||||
|
es6-promise "^3.0.2"
|
||||||
|
|
||||||
|
fs.realpath@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||||
|
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||||
|
|
||||||
|
gaze@^1.1.2:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
|
||||||
|
integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==
|
||||||
|
dependencies:
|
||||||
|
globule "^1.0.0"
|
||||||
|
|
||||||
|
glob@~7.1.1:
|
||||||
|
version "7.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
||||||
|
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
globule@^1.0.0:
|
||||||
|
version "1.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.4.tgz#7c11c43056055a75a6e68294453c17f2796170fb"
|
||||||
|
integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==
|
||||||
|
dependencies:
|
||||||
|
glob "~7.1.1"
|
||||||
|
lodash "^4.17.21"
|
||||||
|
minimatch "~3.0.2"
|
||||||
|
|
||||||
|
inflight@^1.0.4:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||||
|
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
|
||||||
|
dependencies:
|
||||||
|
once "^1.3.0"
|
||||||
|
wrappy "1"
|
||||||
|
|
||||||
|
inherits@2:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
|
"js-tokens@^3.0.0 || ^4.0.0":
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
|
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||||
|
|
||||||
|
lodash@^4.17.21:
|
||||||
|
version "4.17.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
|
loose-envify@^1.1.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
|
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||||
|
dependencies:
|
||||||
|
js-tokens "^3.0.0 || ^4.0.0"
|
||||||
|
|
||||||
|
lozad@^1.16.0:
|
||||||
|
version "1.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lozad/-/lozad-1.16.0.tgz#86ce732c64c69926ccdebb81c8c90bb3735948b4"
|
||||||
|
integrity sha512-JBr9WjvEFeKoyim3svo/gsQPTkgG/mOHJmDctZ/+U9H3ymUuvEkqpn8bdQMFsvTMcyRJrdJkLv0bXqGm0sP72w==
|
||||||
|
|
||||||
|
"memoize-one@>=3.1.1 <6":
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||||
|
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||||
|
|
||||||
|
minimatch@^3.0.4:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
|
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
minimatch@~3.0.2:
|
||||||
|
version "3.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
|
||||||
|
integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
ms@2.1.2:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
|
once@^1.3.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
|
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||||
|
dependencies:
|
||||||
|
wrappy "1"
|
||||||
|
|
||||||
|
path-is-absolute@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||||
|
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
|
||||||
|
|
||||||
|
react-dom@^18.2.0:
|
||||||
|
version "18.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||||
|
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
scheduler "^0.23.0"
|
||||||
|
|
||||||
|
react-window@^1.8.7:
|
||||||
|
version "1.8.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.7.tgz#5e9fd0d23f48f432d7022cdb327219353a15f0d4"
|
||||||
|
integrity sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.0.0"
|
||||||
|
memoize-one ">=3.1.1 <6"
|
||||||
|
|
||||||
|
react@^18.2.0:
|
||||||
|
version "18.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||||
|
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
|
regenerator-runtime@^0.13.4:
|
||||||
|
version "0.13.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
|
||||||
|
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
||||||
|
|
||||||
|
run-when-changed@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/run-when-changed/-/run-when-changed-2.1.0.tgz#2e76d6ff6014d38786a3a11b98e9291c7e934953"
|
||||||
|
integrity sha512-ge/wuPQAvQz0uDEOO8W2c3g4mqXDa1XXtnJmjP9+6Nqfb+XdUYkQX/KvKmSvJ4xG5weD3RGm0u2Q3UsGzAo5gw==
|
||||||
|
dependencies:
|
||||||
|
ansi-bold "^0.1.1"
|
||||||
|
commander "^2.15.1"
|
||||||
|
fs-find-root "^2.0.0"
|
||||||
|
gaze "^1.1.2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
|
scheduler@^0.23.0:
|
||||||
|
version "0.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||||
|
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
|
socket.io-client@^4.5.2:
|
||||||
|
version "4.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.2.tgz#9481518c560388c980c88b01e3cf62f367f04c96"
|
||||||
|
integrity sha512-naqYfFu7CLDiQ1B7AlLhRXKX3gdeaIMfgigwavDzgJoIUYulc1qHH5+2XflTsXTPY7BlPH5rppJyUjhjrKQKLg==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.2"
|
||||||
|
engine.io-client "~6.2.1"
|
||||||
|
socket.io-parser "~4.2.0"
|
||||||
|
|
||||||
|
socket.io-parser@~4.2.0:
|
||||||
|
version "4.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5"
|
||||||
|
integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.1"
|
||||||
|
|
||||||
|
typescript@^4.8.3:
|
||||||
|
version "4.8.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.3.tgz#d59344522c4bc464a65a730ac695007fdb66dd88"
|
||||||
|
integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==
|
||||||
|
|
||||||
|
weak-key@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/weak-key/-/weak-key-1.0.2.tgz#dd5f66648ffb7e83810ea0553a948c60b2b50588"
|
||||||
|
integrity sha512-x9y9moPEcom985nUdHxM+YWbMcP3Ru+fmYqVNHSb6djJGg7H6Ru2ohuzaVIXx1JNyp8E7GO7GsBnehRntaBlsg==
|
||||||
|
dependencies:
|
||||||
|
core-js "^2.4.0"
|
||||||
|
|
||||||
|
wrappy@1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
|
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||||
|
|
||||||
|
ws@~8.2.3:
|
||||||
|
version "8.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
|
||||||
|
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
|
||||||
|
|
||||||
|
xmlhttprequest-ssl@~2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
|
||||||
|
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
|
|
@ -0,0 +1,565 @@
|
||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.9.2":
|
||||||
|
version "7.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
|
||||||
|
integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64@0.15.7":
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz#1ec4af4a16c554cbd402cc557ccdd874e3f7be53"
|
||||||
|
integrity sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw==
|
||||||
|
|
||||||
|
"@react-dnd/asap@^5.0.1":
|
||||||
|
version "5.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488"
|
||||||
|
integrity sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==
|
||||||
|
|
||||||
|
"@react-dnd/invariant@^4.0.1":
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-4.0.2.tgz#b92edffca10a26466643349fac7cdfb8799769df"
|
||||||
|
integrity sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==
|
||||||
|
|
||||||
|
"@react-dnd/shallowequal@^4.0.1":
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz#d1b4befa423f692fa4abf1c79209702e7d8ae4b4"
|
||||||
|
integrity sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==
|
||||||
|
|
||||||
|
"@socket.io/component-emitter@~3.1.0":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
|
||||||
|
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
|
||||||
|
|
||||||
|
"@types/lodash.debounce@^4.0.7":
|
||||||
|
version "4.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz#0285879defb7cdb156ae633cecd62d5680eded9f"
|
||||||
|
integrity sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==
|
||||||
|
dependencies:
|
||||||
|
"@types/lodash" "*"
|
||||||
|
|
||||||
|
"@types/lodash@*":
|
||||||
|
version "4.14.185"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.185.tgz#c9843f5a40703a8f5edfd53358a58ae729816908"
|
||||||
|
integrity sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA==
|
||||||
|
|
||||||
|
"@types/lozad@^1.16.1":
|
||||||
|
version "1.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lozad/-/lozad-1.16.1.tgz#fc06f73bfd787a456b0e12a9d83a954b56431400"
|
||||||
|
integrity sha512-wmGW368y1zu9ark/7fMNHdZ/c3wGV7fxoEqtK+V3Dsv2QGQQp34D9elDowuW12zZwxjO9NkNUorqg+D9j1VRGQ==
|
||||||
|
|
||||||
|
"@types/prop-types@*":
|
||||||
|
version "15.7.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||||
|
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||||
|
|
||||||
|
"@types/react-dom@^18.0.6":
|
||||||
|
version "18.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1"
|
||||||
|
integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-virtualized-auto-sizer@^1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.1.tgz#b3187dae1dfc4c15880c9cfc5b45f2719ea6ebd4"
|
||||||
|
integrity sha512-GH8sAnBEM5GV9LTeiz56r4ZhMOUSrP43tAQNSRVxNexDjcNKLCEtnxusAItg1owFUFE6k0NslV26gqVClVvong==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-window@^1.8.5":
|
||||||
|
version "1.8.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.5.tgz#285fcc5cea703eef78d90f499e1457e9b5c02fc1"
|
||||||
|
integrity sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react@*", "@types/react@^18.0.20":
|
||||||
|
version "18.0.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.20.tgz#e4c36be3a55eb5b456ecf501bd4a00fd4fd0c9ab"
|
||||||
|
integrity sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA==
|
||||||
|
dependencies:
|
||||||
|
"@types/prop-types" "*"
|
||||||
|
"@types/scheduler" "*"
|
||||||
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@types/scheduler@*":
|
||||||
|
version "0.16.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
|
||||||
|
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
|
||||||
|
|
||||||
|
ansi-bold@^0.1.1:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-bold/-/ansi-bold-0.1.1.tgz#3e63950af5acc2ae2e670e6f67deb115d1a5f505"
|
||||||
|
integrity sha512-wWKwcViX1E28U6FohtWOP4sHFyArELHJ2p7+3BzbibqJiuISeskq6t7JnrLisUngMF5zMhgmXVw8Equjzz9OlA==
|
||||||
|
dependencies:
|
||||||
|
ansi-wrap "0.1.0"
|
||||||
|
|
||||||
|
ansi-wrap@0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
|
||||||
|
integrity sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==
|
||||||
|
|
||||||
|
balanced-match@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
|
brace-expansion@^1.1.7:
|
||||||
|
version "1.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
|
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||||
|
dependencies:
|
||||||
|
balanced-match "^1.0.0"
|
||||||
|
concat-map "0.0.1"
|
||||||
|
|
||||||
|
classnames@^2.3.2:
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
||||||
|
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
||||||
|
|
||||||
|
commander@^2.15.1:
|
||||||
|
version "2.20.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
|
concat-map@0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
|
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||||
|
|
||||||
|
core-js@^2.4.0:
|
||||||
|
version "2.6.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||||
|
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||||
|
|
||||||
|
csstype@^3.0.2:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
|
||||||
|
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
|
||||||
|
|
||||||
|
debug@~4.3.1, debug@~4.3.2:
|
||||||
|
version "4.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
|
dnd-core@^16.0.1:
|
||||||
|
version "16.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-16.0.1.tgz#a1c213ed08961f6bd1959a28bb76f1a868360d19"
|
||||||
|
integrity sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==
|
||||||
|
dependencies:
|
||||||
|
"@react-dnd/asap" "^5.0.1"
|
||||||
|
"@react-dnd/invariant" "^4.0.1"
|
||||||
|
redux "^4.2.0"
|
||||||
|
|
||||||
|
dotenv@^16.0.2:
|
||||||
|
version "16.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.2.tgz#0b0f8652c016a3858ef795024508cddc4bffc5bf"
|
||||||
|
integrity sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==
|
||||||
|
|
||||||
|
engine.io-client@~6.2.1:
|
||||||
|
version "6.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.2.tgz#c6c5243167f5943dcd9c4abee1bfc634aa2cbdd0"
|
||||||
|
integrity sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.1"
|
||||||
|
engine.io-parser "~5.0.3"
|
||||||
|
ws "~8.2.3"
|
||||||
|
xmlhttprequest-ssl "~2.0.0"
|
||||||
|
|
||||||
|
engine.io-parser@~5.0.3:
|
||||||
|
version "5.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
|
||||||
|
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
|
||||||
|
|
||||||
|
es6-promise@^3.0.2:
|
||||||
|
version "3.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
|
||||||
|
integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==
|
||||||
|
|
||||||
|
esbuild-android-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.7.tgz#a521604d8c4c6befc7affedc897df8ccde189bea"
|
||||||
|
integrity sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w==
|
||||||
|
|
||||||
|
esbuild-android-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.7.tgz#307b81f1088bf1e81dfe5f3d1d63a2d2a2e3e68e"
|
||||||
|
integrity sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ==
|
||||||
|
|
||||||
|
esbuild-darwin-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz#270117b0c4ec6bcbc5cf3a297a7d11954f007e11"
|
||||||
|
integrity sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg==
|
||||||
|
|
||||||
|
esbuild-darwin-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.7.tgz#97851eacd11dacb7719713602e3319e16202fc77"
|
||||||
|
integrity sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ==
|
||||||
|
|
||||||
|
esbuild-freebsd-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.7.tgz#1de15ffaf5ae916aa925800aa6d02579960dd8c4"
|
||||||
|
integrity sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ==
|
||||||
|
|
||||||
|
esbuild-freebsd-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.7.tgz#0f160dbf5c9a31a1d8dd87acbbcb1a04b7031594"
|
||||||
|
integrity sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q==
|
||||||
|
|
||||||
|
esbuild-linux-32@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.7.tgz#422eb853370a5e40bdce8b39525380de11ccadec"
|
||||||
|
integrity sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg==
|
||||||
|
|
||||||
|
esbuild-linux-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.7.tgz#f89c468453bb3194b14f19dc32e0b99612e81d2b"
|
||||||
|
integrity sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ==
|
||||||
|
|
||||||
|
esbuild-linux-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.7.tgz#68a79d6eb5e032efb9168a0f340ccfd33d6350a1"
|
||||||
|
integrity sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw==
|
||||||
|
|
||||||
|
esbuild-linux-arm@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.7.tgz#2b7c784d0b3339878013dfa82bf5eaf82c7ce7d3"
|
||||||
|
integrity sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ==
|
||||||
|
|
||||||
|
esbuild-linux-mips64le@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.7.tgz#bb8330a50b14aa84673816cb63cc6c8b9beb62cc"
|
||||||
|
integrity sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw==
|
||||||
|
|
||||||
|
esbuild-linux-ppc64le@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.7.tgz#52544e7fa992811eb996674090d0bc41f067a14b"
|
||||||
|
integrity sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw==
|
||||||
|
|
||||||
|
esbuild-linux-riscv64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.7.tgz#a43ae60697992b957e454cbb622f7ee5297e8159"
|
||||||
|
integrity sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g==
|
||||||
|
|
||||||
|
esbuild-linux-s390x@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.7.tgz#8c76a125dd10a84c166294d77416caaf5e1c7b64"
|
||||||
|
integrity sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ==
|
||||||
|
|
||||||
|
esbuild-netbsd-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.7.tgz#19b2e75449d7d9c32b5d8a222bac2f1e0c3b08fd"
|
||||||
|
integrity sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ==
|
||||||
|
|
||||||
|
esbuild-openbsd-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.7.tgz#1357b2bf72fd037d9150e751420a1fe4c8618ad7"
|
||||||
|
integrity sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ==
|
||||||
|
|
||||||
|
esbuild-sunos-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.7.tgz#87ab2c604592a9c3c763e72969da0d72bcde91d2"
|
||||||
|
integrity sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag==
|
||||||
|
|
||||||
|
esbuild-windows-32@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.7.tgz#c81e688c0457665a8d463a669e5bf60870323e99"
|
||||||
|
integrity sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA==
|
||||||
|
|
||||||
|
esbuild-windows-64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.7.tgz#2421d1ae34b0561a9d6767346b381961266c4eff"
|
||||||
|
integrity sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q==
|
||||||
|
|
||||||
|
esbuild-windows-arm64@0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.7.tgz#7d5e9e060a7b454cb2f57f84a3f3c23c8f30b7d2"
|
||||||
|
integrity sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw==
|
||||||
|
|
||||||
|
esbuild@^0.15.7:
|
||||||
|
version "0.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.7.tgz#8a1f1aff58671a3199dd24df95314122fc1ddee8"
|
||||||
|
integrity sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@esbuild/linux-loong64" "0.15.7"
|
||||||
|
esbuild-android-64 "0.15.7"
|
||||||
|
esbuild-android-arm64 "0.15.7"
|
||||||
|
esbuild-darwin-64 "0.15.7"
|
||||||
|
esbuild-darwin-arm64 "0.15.7"
|
||||||
|
esbuild-freebsd-64 "0.15.7"
|
||||||
|
esbuild-freebsd-arm64 "0.15.7"
|
||||||
|
esbuild-linux-32 "0.15.7"
|
||||||
|
esbuild-linux-64 "0.15.7"
|
||||||
|
esbuild-linux-arm "0.15.7"
|
||||||
|
esbuild-linux-arm64 "0.15.7"
|
||||||
|
esbuild-linux-mips64le "0.15.7"
|
||||||
|
esbuild-linux-ppc64le "0.15.7"
|
||||||
|
esbuild-linux-riscv64 "0.15.7"
|
||||||
|
esbuild-linux-s390x "0.15.7"
|
||||||
|
esbuild-netbsd-64 "0.15.7"
|
||||||
|
esbuild-openbsd-64 "0.15.7"
|
||||||
|
esbuild-sunos-64 "0.15.7"
|
||||||
|
esbuild-windows-32 "0.15.7"
|
||||||
|
esbuild-windows-64 "0.15.7"
|
||||||
|
esbuild-windows-arm64 "0.15.7"
|
||||||
|
|
||||||
|
fast-deep-equal@^3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||||
|
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||||
|
|
||||||
|
fs-find-root@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-find-root/-/fs-find-root-2.0.0.tgz#71c23b384db6bcb1e8ec637cade707fda39c593b"
|
||||||
|
integrity sha512-LmgsxDwnxd+sfm3EZ66P8nSlUMm69hKz/LdXKChK2a+5xXnrAB7MPn2uo0VCyrgj8lZNqyj6EIdD516ILZAEBg==
|
||||||
|
dependencies:
|
||||||
|
es6-promise "^3.0.2"
|
||||||
|
|
||||||
|
fs.realpath@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||||
|
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||||
|
|
||||||
|
gaze@^1.1.2:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
|
||||||
|
integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==
|
||||||
|
dependencies:
|
||||||
|
globule "^1.0.0"
|
||||||
|
|
||||||
|
glob@~7.1.1:
|
||||||
|
version "7.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
||||||
|
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
globule@^1.0.0:
|
||||||
|
version "1.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.4.tgz#7c11c43056055a75a6e68294453c17f2796170fb"
|
||||||
|
integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==
|
||||||
|
dependencies:
|
||||||
|
glob "~7.1.1"
|
||||||
|
lodash "^4.17.21"
|
||||||
|
minimatch "~3.0.2"
|
||||||
|
|
||||||
|
hoist-non-react-statics@^3.3.2:
|
||||||
|
version "3.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||||
|
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||||
|
dependencies:
|
||||||
|
react-is "^16.7.0"
|
||||||
|
|
||||||
|
inflight@^1.0.4:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||||
|
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
|
||||||
|
dependencies:
|
||||||
|
once "^1.3.0"
|
||||||
|
wrappy "1"
|
||||||
|
|
||||||
|
inherits@2:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
|
"js-tokens@^3.0.0 || ^4.0.0":
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
|
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||||
|
|
||||||
|
lodash.debounce@^4.0.8:
|
||||||
|
version "4.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||||
|
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
||||||
|
|
||||||
|
lodash@^4.17.21:
|
||||||
|
version "4.17.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
|
loose-envify@^1.1.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
|
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||||
|
dependencies:
|
||||||
|
js-tokens "^3.0.0 || ^4.0.0"
|
||||||
|
|
||||||
|
lozad@^1.16.0:
|
||||||
|
version "1.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lozad/-/lozad-1.16.0.tgz#86ce732c64c69926ccdebb81c8c90bb3735948b4"
|
||||||
|
integrity sha512-JBr9WjvEFeKoyim3svo/gsQPTkgG/mOHJmDctZ/+U9H3ymUuvEkqpn8bdQMFsvTMcyRJrdJkLv0bXqGm0sP72w==
|
||||||
|
|
||||||
|
"memoize-one@>=3.1.1 <6":
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||||
|
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||||
|
|
||||||
|
minimatch@^3.0.4:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
|
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
minimatch@~3.0.2:
|
||||||
|
version "3.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
|
||||||
|
integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
ms@2.1.2:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
|
once@^1.3.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
|
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||||
|
dependencies:
|
||||||
|
wrappy "1"
|
||||||
|
|
||||||
|
path-is-absolute@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||||
|
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
|
||||||
|
|
||||||
|
react-dnd-html5-backend@^16.0.1:
|
||||||
|
version "16.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz#87faef15845d512a23b3c08d29ecfd34871688b6"
|
||||||
|
integrity sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==
|
||||||
|
dependencies:
|
||||||
|
dnd-core "^16.0.1"
|
||||||
|
|
||||||
|
react-dnd@^16.0.1:
|
||||||
|
version "16.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-16.0.1.tgz#2442a3ec67892c60d40a1559eef45498ba26fa37"
|
||||||
|
integrity sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==
|
||||||
|
dependencies:
|
||||||
|
"@react-dnd/invariant" "^4.0.1"
|
||||||
|
"@react-dnd/shallowequal" "^4.0.1"
|
||||||
|
dnd-core "^16.0.1"
|
||||||
|
fast-deep-equal "^3.1.3"
|
||||||
|
hoist-non-react-statics "^3.3.2"
|
||||||
|
|
||||||
|
react-dom@^18.2.0:
|
||||||
|
version "18.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||||
|
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
scheduler "^0.23.0"
|
||||||
|
|
||||||
|
react-is@^16.7.0:
|
||||||
|
version "16.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
|
||||||
|
react-virtualized-auto-sizer@^1.0.7:
|
||||||
|
version "1.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.7.tgz#bfb8414698ad1597912473de3e2e5f82180c1195"
|
||||||
|
integrity sha512-Mxi6lwOmjwIjC1X4gABXMJcKHsOo0xWl3E3ugOgufB8GJU+MqrtY35aBuvCYv/razQ1Vbp7h1gWJjGjoNN5pmA==
|
||||||
|
|
||||||
|
react-window@^1.8.7:
|
||||||
|
version "1.8.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.7.tgz#5e9fd0d23f48f432d7022cdb327219353a15f0d4"
|
||||||
|
integrity sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.0.0"
|
||||||
|
memoize-one ">=3.1.1 <6"
|
||||||
|
|
||||||
|
react@^18.2.0:
|
||||||
|
version "18.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||||
|
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
|
redux@^4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13"
|
||||||
|
integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.9.2"
|
||||||
|
|
||||||
|
regenerator-runtime@^0.13.4:
|
||||||
|
version "0.13.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
|
||||||
|
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
||||||
|
|
||||||
|
run-when-changed@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/run-when-changed/-/run-when-changed-2.1.0.tgz#2e76d6ff6014d38786a3a11b98e9291c7e934953"
|
||||||
|
integrity sha512-ge/wuPQAvQz0uDEOO8W2c3g4mqXDa1XXtnJmjP9+6Nqfb+XdUYkQX/KvKmSvJ4xG5weD3RGm0u2Q3UsGzAo5gw==
|
||||||
|
dependencies:
|
||||||
|
ansi-bold "^0.1.1"
|
||||||
|
commander "^2.15.1"
|
||||||
|
fs-find-root "^2.0.0"
|
||||||
|
gaze "^1.1.2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
|
scheduler@^0.23.0:
|
||||||
|
version "0.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||||
|
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
|
socket.io-client@^4.5.2:
|
||||||
|
version "4.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.2.tgz#9481518c560388c980c88b01e3cf62f367f04c96"
|
||||||
|
integrity sha512-naqYfFu7CLDiQ1B7AlLhRXKX3gdeaIMfgigwavDzgJoIUYulc1qHH5+2XflTsXTPY7BlPH5rppJyUjhjrKQKLg==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.2"
|
||||||
|
engine.io-client "~6.2.1"
|
||||||
|
socket.io-parser "~4.2.0"
|
||||||
|
|
||||||
|
socket.io-parser@~4.2.0:
|
||||||
|
version "4.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5"
|
||||||
|
integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==
|
||||||
|
dependencies:
|
||||||
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
|
debug "~4.3.1"
|
||||||
|
|
||||||
|
typescript@^4.8.3:
|
||||||
|
version "4.8.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.3.tgz#d59344522c4bc464a65a730ac695007fdb66dd88"
|
||||||
|
integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==
|
||||||
|
|
||||||
|
weak-key@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/weak-key/-/weak-key-1.0.2.tgz#dd5f66648ffb7e83810ea0553a948c60b2b50588"
|
||||||
|
integrity sha512-x9y9moPEcom985nUdHxM+YWbMcP3Ru+fmYqVNHSb6djJGg7H6Ru2ohuzaVIXx1JNyp8E7GO7GsBnehRntaBlsg==
|
||||||
|
dependencies:
|
||||||
|
core-js "^2.4.0"
|
||||||
|
|
||||||
|
wrappy@1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
|
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||||
|
|
||||||
|
ws@~8.2.3:
|
||||||
|
version "8.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
|
||||||
|
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
|
||||||
|
|
||||||
|
xmlhttprequest-ssl@~2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
|
||||||
|
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
|
2
env
2
env
|
@ -32,3 +32,5 @@ DESCRIPTION=rdrama.net caters to drama in all forms such as: Real life, videos,
|
||||||
CF_KEY=blahblahblah
|
CF_KEY=blahblahblah
|
||||||
CF_ZONE=blahblahblah
|
CF_ZONE=blahblahblah
|
||||||
DEBIAN_FRONTEND=noninteractive
|
DEBIAN_FRONTEND=noninteractive
|
||||||
|
NODE_VERSION=16.13.0
|
||||||
|
NVM_DIR=/root/.nvm
|
|
@ -131,7 +131,7 @@ def teardown_request(error):
|
||||||
|
|
||||||
if app.config["SERVER_NAME"] == 'localhost':
|
if app.config["SERVER_NAME"] == 'localhost':
|
||||||
from files.routes import *
|
from files.routes import *
|
||||||
# from files.routes.chat import *
|
from files.routes.chat import *
|
||||||
elif "load_chat" in argv:
|
elif "load_chat" in argv:
|
||||||
from files.routes.chat import *
|
from files.routes.chat import *
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -0,0 +1,319 @@
|
||||||
|
/* src/features/activity/Activity.css */
|
||||||
|
.Activity {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0 4rem;
|
||||||
|
}
|
||||||
|
.Activity > section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
.Activity i {
|
||||||
|
margin-right: 3rem;
|
||||||
|
}
|
||||||
|
.Activity > section:not(:last-child) {
|
||||||
|
margin-right: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/ActivityList.css */
|
||||||
|
.ActivityList {
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
.ActivityList h4 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.ActivityList h4 hr {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
.ActivityList-activity {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.ActivityList-activity-icon {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
.ActivityList-activity {
|
||||||
|
transition: background 0.4s ease-in-out;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
.ActivityList-activity:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: #ffffff05;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/Username.css */
|
||||||
|
.Username {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.Username > a {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/ChatMessage.css */
|
||||||
|
@keyframes fading-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ChatMessage {
|
||||||
|
padding: 0.5rem;
|
||||||
|
padding-right: 3rem;
|
||||||
|
position: relative;
|
||||||
|
animation: fading-in 0.3s ease-in-out forwards;
|
||||||
|
}
|
||||||
|
.ChatMessage:nth-of-type(even) {
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
.ChatMessage-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.ChatMessage-timestamp {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
.ChatMessage-bottom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
.ChatMessage-content {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
word-break: break-all;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.ChatMessage-button {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
.ChatMessage-delete {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
|
.ChatMessageList {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
.ChatMessageList::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
lite-youtube {
|
||||||
|
min-width: min(80vw, 500px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/QuotedMessage.css */
|
||||||
|
@keyframes sliding-up {
|
||||||
|
from {
|
||||||
|
top: 50px;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.QuotedMessage {
|
||||||
|
position: relative;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-top: 1px solid var(--primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
animation: sliding-up 0.3s forwards;
|
||||||
|
}
|
||||||
|
.QuotedMessage-content {
|
||||||
|
margin-left: 1rem;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 420px;
|
||||||
|
max-height: 40px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/drawers/BaseDrawer.css */
|
||||||
|
.BaseDrawer {
|
||||||
|
flex: 1;
|
||||||
|
padding-right: 2rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/emoji/EmojiGenres.css */
|
||||||
|
|
||||||
|
/* src/features/emoji/EmojiDrawer.css */
|
||||||
|
.EmojiDrawer-options {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/UserInput.css */
|
||||||
|
.UserInput {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.UserInput-emoji {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/UserList.css */
|
||||||
|
.UserList {
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
.UserList-heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.UserList-heading h4 {
|
||||||
|
margin-right: 2rem;
|
||||||
|
}
|
||||||
|
.UserList ul:not(.fluid) {
|
||||||
|
max-height: 275px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.UserList ul::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/Chat.css */
|
||||||
|
.Chat {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 350px;
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
.Chat-mobile-top {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.Chat-mobile-top i {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
.Chat-mobile-top span {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.Chat-side {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 1180px) {
|
||||||
|
.Chat-mobile-top {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.Chat-side {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Chat-drawer {
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
.Chat-typing {
|
||||||
|
height: 18px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.Chat-window {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/ChatHeading.css */
|
||||||
|
.ChatHeading {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.ChatHeading i {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/features/chat/UsersTyping.css */
|
||||||
|
.UsersTyping {
|
||||||
|
height: 18px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/App.css */
|
||||||
|
.App {
|
||||||
|
position: fixed;
|
||||||
|
width: 100vw;
|
||||||
|
height: calc(100vh - 42px);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 1000px) {
|
||||||
|
.App {
|
||||||
|
height: calc(100vh - 12rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.App-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 1000px;
|
||||||
|
}
|
||||||
|
.App-heading {
|
||||||
|
flex-basis: 3rem;
|
||||||
|
border-bottom: 1px dashed var(--primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.App-side {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--gray-500);
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 1100px) {
|
||||||
|
.App-side {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.App-content {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.App-drawer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.App-input {
|
||||||
|
flex-basis: 7rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -189,6 +189,7 @@ const emojisSearchDictionary = {
|
||||||
const emojiRequest = new XMLHttpRequest();
|
const emojiRequest = new XMLHttpRequest();
|
||||||
emojiRequest.open("GET", '/marsey_list.json');
|
emojiRequest.open("GET", '/marsey_list.json');
|
||||||
emojiRequest.onload = async (e) => {
|
emojiRequest.onload = async (e) => {
|
||||||
|
console.log("HERE")
|
||||||
let emojis = JSON.parse(emojiRequest.response);
|
let emojis = JSON.parse(emojiRequest.response);
|
||||||
if(! (emojis instanceof Array ))
|
if(! (emojis instanceof Array ))
|
||||||
throw new TypeError("[EMOJI DIALOG] rDrama's server should have sent a JSON-coded Array!");
|
throw new TypeError("[EMOJI DIALOG] rDrama's server should have sent a JSON-coded Array!");
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import time
|
import time
|
||||||
|
from files.helpers.jinja2 import timestamp
|
||||||
from files.helpers.wrappers import *
|
from files.helpers.wrappers import *
|
||||||
from files.helpers.sanitize import sanitize
|
from files.helpers.sanitize import sanitize
|
||||||
from files.helpers.const import *
|
from files.helpers.const import *
|
||||||
|
@ -44,6 +45,12 @@ def chatjs():
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
@app.get('/chat_done.js')
|
||||||
|
def chatbuiltjs():
|
||||||
|
resp = make_response(send_from_directory('assets', 'js/chat_done.js'))
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@socketio.on('speak')
|
@socketio.on('speak')
|
||||||
@limiter.limit("3/second;10/minute")
|
@limiter.limit("3/second;10/minute")
|
||||||
@limiter.limit("3/second;10/minute", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
|
@limiter.limit("3/second;10/minute", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
|
||||||
|
@ -63,7 +70,8 @@ def speak(data, v):
|
||||||
|
|
||||||
if not text: return '', 403
|
if not text: return '', 403
|
||||||
text_html = sanitize(text, count_marseys=True)
|
text_html = sanitize(text, count_marseys=True)
|
||||||
|
time_number = int(time.time())
|
||||||
|
time_string = timestamp(time_number)
|
||||||
data={
|
data={
|
||||||
"avatar": v.profile_url,
|
"avatar": v.profile_url,
|
||||||
"hat": v.hat_active,
|
"hat": v.hat_active,
|
||||||
|
@ -72,7 +80,8 @@ def speak(data, v):
|
||||||
"text": text,
|
"text": text,
|
||||||
"text_html": text_html,
|
"text_html": text_html,
|
||||||
"text_censored": censor_slurs(text_html, 'chat'),
|
"text_censored": censor_slurs(text_html, 'chat'),
|
||||||
"time": int(time.time())
|
"time": time_number,
|
||||||
|
"timestamp": time_string
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.shadowbanned:
|
if v.shadowbanned:
|
||||||
|
@ -107,6 +116,8 @@ def connect(v):
|
||||||
emit("online", online, broadcast=True)
|
emit("online", online, broadcast=True)
|
||||||
cache.set(ONLINE_STR, len(online), timeout=0)
|
cache.set(ONLINE_STR, len(online), timeout=0)
|
||||||
|
|
||||||
|
emit('online', online)
|
||||||
|
emit('catchup', messages)
|
||||||
emit('typing', typing)
|
emit('typing', typing)
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
|
|
@ -4,17 +4,13 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<script defer src="{{asset('js/bootstrap.js')}}"></script>
|
<script defer src="{{asset('js/bootstrap.js')}}"></script>
|
||||||
|
|
||||||
<meta name="description" content="{{DESCRIPTION}}">
|
<meta name="description" content="{{DESCRIPTION}}">
|
||||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; connect-src 'self'; object-src 'none';">
|
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; connect-src 'self'; object-src 'none';">
|
||||||
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
|
|
||||||
<link id="favicon" rel="icon" type="image/webp" href="{{asset_siteimg('icon.webp')}}">
|
<link id="favicon" rel="icon" type="image/webp" href="{{asset_siteimg('icon.webp')}}">
|
||||||
|
|
||||||
<title>Chat</title>
|
<title>Chat</title>
|
||||||
|
|
||||||
<style>:root{--primary:#{{v.themecolor}}}</style>
|
<style>:root{--primary:#{{v.themecolor}}}</style>
|
||||||
|
@ -25,236 +21,22 @@
|
||||||
{{v.css | safe}}
|
{{v.css | safe}}
|
||||||
</style>
|
</style>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<link rel="stylesheet" href="{{asset('css/chat_done.css')}}">
|
||||||
<style>
|
|
||||||
#chat-window {
|
|
||||||
max-height: calc(100vh - 220px);
|
|
||||||
overflow-y: auto;
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#online {
|
|
||||||
max-height: calc(100vh - 200px);
|
|
||||||
overflow-y: auto;
|
|
||||||
background-color: var(--background) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-window .chat-profile {
|
|
||||||
min-width: 42px;
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-window::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-window {
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-mention {
|
|
||||||
background-color: #{{v.themecolor}}55;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p, h1, h2, h3, h4, h5 {
|
|
||||||
display: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote + :not(blockquote) {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diff {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
#shrink * {
|
|
||||||
font-size: 10px !important;
|
|
||||||
}
|
|
||||||
.fa-reply:before {
|
|
||||||
font-size: 9px;
|
|
||||||
}
|
|
||||||
.diff {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
#chat-window {
|
|
||||||
max-height: 62vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-line .btn {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-line-content {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cdiv {
|
|
||||||
overflow: hidden;
|
|
||||||
margin-left: 27px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 0.5rem !important;
|
|
||||||
margin-bottom: 0.25rem !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.del {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
lite-youtube {
|
|
||||||
min-width: min(80vw,500px);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{% include "header.html" %}
|
{% include "header.html" %}
|
||||||
|
<div
|
||||||
<div class="container pb-4">
|
id="root"
|
||||||
<div class="row justify-content-around" id="main-content-row">
|
data-id="{{v.id}}"
|
||||||
<div class="col h-100 {% block customPadding %}{% if request.path.startswith('/@') %}user-gutters{% else %}custom-gutters{% endif %}{% endblock %}" id="main-content-col">
|
data-username="{{v.username}}"
|
||||||
|
data-admin="{{v.admin_level > 1}}"
|
||||||
<div class="border-right pb-1 pt-2 px-3">
|
data-censored="{{v.slurreplacer}}"
|
||||||
<span id="online2" data-bs-html="true" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Users in chat right now" class="text-muted">
|
data-sitename="{{SITE_NAME}}"
|
||||||
<i class="far fa-user fa-sm mr-1"></i>
|
data-themecolor="{{v.themecolor}}">
|
||||||
<span class="board-chat-count">0</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="chat-line-template" class="d-none">
|
|
||||||
<div class="chat-line">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div class="pl-md-3 text-muted chat-line-content">
|
|
||||||
<div>
|
|
||||||
<div class="avatar profile-pic-20-wrapper">
|
|
||||||
<img class="avatar-pic pp20 mr-1">
|
|
||||||
<img class="avatar-hat profile-pic-20-hat hat" loading="lazy">
|
|
||||||
</div>
|
|
||||||
<a href="" class="font-weight-bold text-black userlink" target="_blank"></a>
|
|
||||||
<span class="text-black time ml-2 d-none">just now</span>
|
|
||||||
<div class="cdiv">
|
|
||||||
<span class="chat-message text-black text-break"></span>
|
|
||||||
<span class="text d-none"></span>
|
|
||||||
<button class="quote btn" onclick="quote(this)"><i class="fas fa-reply" aria-hidden="true"></i></button>
|
|
||||||
{% if v.admin_level > 1 %}
|
|
||||||
<button class="quote btn del" onclick="this.nextElementSibling.classList.remove('d-none');this.classList.add('d-none')">
|
|
||||||
<i class="fas fa-trash-alt" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<button class="quote btn d-none del delmsg" onclick="del(this)">
|
|
||||||
<i class="fas fa-trash-alt text-danger" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="shrink">
|
|
||||||
<div id="chat-window" class="container pl-0 py-0">
|
|
||||||
{% for m in messages %}
|
|
||||||
{% set text_html = m['text_censored'] if v.slurreplacer else m['text_html'] %}
|
|
||||||
{% set link = '<a href="/id/' + v.id|string + '">' %}
|
|
||||||
{% set same = loop.index > 1 and m['username'] == messages[loop.index-2]['username'] %}
|
|
||||||
<div class="chat-line {% if link in text_html %}chat-mention{% endif %} {% if not same %}diff{% endif %}">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div class="pl-md-3 text-muted chat-line-content">
|
|
||||||
<div>
|
|
||||||
{% if not same %}
|
|
||||||
<div class="profile-pic-20-wrapper">
|
|
||||||
<img src="{{m['avatar']}}" class="pp20 mr-1">
|
|
||||||
{% if m.get('hat') -%}
|
|
||||||
<img class="profile-pic-20-hat hat" loading="lazy" src="{{m['hat']}}?h=7">
|
|
||||||
{%- endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<a class="{% if same %}d-none{% endif %} font-weight-bold text-black userlink" style="color:#{{m['namecolor']}}" target="_blank" href="/@{{m['username']}}">{{m['username']}}</a>
|
|
||||||
|
|
||||||
{% if not same %}
|
|
||||||
<span class="text-black time ml-2">{{m['time'] | timestamp}}</span>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="cdiv">
|
|
||||||
<span class="chat-message text-black text-break">{{text_html | safe}}</span>
|
|
||||||
<span class="text d-none">{{m['text']}}</span>
|
|
||||||
<button class="quote btn" onclick="quote(this)"><i class="fas fa-reply" aria-hidden="true"></i></button>
|
|
||||||
{% if v.admin_level > 1 %}
|
|
||||||
<button class="quote btn del" onclick="this.nextElementSibling.classList.remove('d-none');this.classList.add('d-none')">
|
|
||||||
<i class="fas fa-trash-alt" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<button class="quote btn d-none del delmsg" onclick="del(this)">
|
|
||||||
<i class="fas fa-trash-alt text-danger" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id='message' class="d-none position-relative form-group d-flex mt-3">
|
|
||||||
<div class="position-absolute text-muted text-small ml-1" style="bottom: -1.5rem; line-height: 1;">
|
|
||||||
<span id="typing-indicator"></span>
|
|
||||||
<span id="loading-indicator" class="d-none"></span>
|
|
||||||
</div>
|
|
||||||
<i class="btn btn-secondary mr-2 fas fa-smile-beam" style="padding-top:0.65rem" onclick="loadEmojis('input-text')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-placement="bottom" title="Add Emoji"></i>
|
|
||||||
<textarea onclick="scroll_chat()" id="input-text" minlength="1" maxlength="{% if SITE == 'rdrama.net' %}200{% else %}1000{% endif %}" style="font-size:16px!important" class="form-control" placeholder="Message" autocomplete="off" autofocus rows="1"></textarea>
|
|
||||||
<button id="chatsend" onclick="send()" class="btn btn-primary ml-3" type="submit" onclick="disable(this)">Send</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col text-left d-none d-lg-block pt-3" style="max-width:300px">
|
|
||||||
<h4>Users in chat right now</h4>
|
|
||||||
<div id="online" class="mt-3"></div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<script>window.global = window</script>
|
||||||
</div>
|
<script defer src="/assets/js/lite-youtube.js?v=4000"></script>
|
||||||
|
<script defer src="/chat_done.js"></script>
|
||||||
<input id="vid" type="hidden" value="{{v.id}}">
|
|
||||||
<input id="vusername" type="hidden" value="{{v.username}}">
|
|
||||||
<input id="site_name" type="hidden" value="{{SITE_NAME}}">
|
|
||||||
<input id="slurreplacer" type="hidden" value="{{v.slurreplacer}}">
|
|
||||||
|
|
||||||
{% include "emoji_modal.html" %}
|
|
||||||
{% include "expanded_image_modal.html" %}
|
|
||||||
|
|
||||||
<script defer src="{{asset('js/lozad.js')}}"></script>
|
|
||||||
<script defer src="/assets/js/lite-youtube.js?v=4000"></script>
|
|
||||||
<script defer src="/chat.js?h=7"></script>
|
|
||||||
|
|
||||||
{% if v.admin_level > 1 %}
|
|
||||||
<script>
|
|
||||||
document.addEventListener('click', function (e) {
|
|
||||||
if (!e.target.classList.contains('del') && !e.target.classList.contains('fa-trash-alt')) {
|
|
||||||
for (const btn of document.querySelectorAll('.delmsg:not(.d-none)')) {
|
|
||||||
btn.classList.add('d-none');
|
|
||||||
btn.previousElementSibling.classList.remove('d-none');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endif %}
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
cd /rDrama
|
cd /rDrama
|
||||||
git pull
|
git pull
|
||||||
|
cd ./chat && yarn chat && cd ../
|
||||||
. /env
|
. /env
|
||||||
gunicorn files.__main__:app load_chat -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 -b 0.0.0.0:5001 --max-requests 30000 --max-requests-jitter 30000
|
gunicorn files.__main__:app load_chat -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 -b 0.0.0.0:5001 --max-requests 30000 --max-requests-jitter 30000
|
|
@ -32,6 +32,16 @@ psql -U postgres -f schema.sql postgres
|
||||||
psql -U postgres -f seed-db.sql postgres
|
psql -U postgres -f seed-db.sql postgres
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
apt -y install curl
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
|
||||||
|
. "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION}
|
||||||
|
. "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION}
|
||||||
|
. "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION}
|
||||||
|
PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}"
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
npm i -g yarn
|
||||||
|
|
||||||
mkdir /images
|
mkdir /images
|
||||||
mkdir /songs
|
mkdir /songs
|
||||||
mkdir /videos
|
mkdir /videos
|
||||||
|
|
Loading…
Reference in New Issue