forked from ppfun/pixelplanet
fixing bugs that got introduced in the past two commits
This commit is contained in:
parent
ac464ba5a7
commit
b79a12f931
|
@ -97,7 +97,7 @@ Configuration takes place in the environment variables that are defined in ecosy
|
|||
Notes:
|
||||
|
||||
- to be able to use USE_PROXYCHECK, you have to have an account on proxycheck.io or getipintel or another checker setup and you might set some proxies in`proxies.json that get used for making proxycheck requests. Look into `src/isProxy.js` to see how things work, but keep in mind that this isn't neccessarily how pixelplanet.fun uses it.
|
||||
- Admins are users with 0cd and access to `./admintools` for image-upload and whatever
|
||||
- Admins are users with 0cd and access to `Admintools`in their User Menu for image-upload and whatever
|
||||
- You can find out the id of a user by looking into the logs (i.e. `info: {ip} / {id} wants to place 2 in (1701, -8315)`) when he places a pixel or by checking the MySql Users database
|
||||
- If you use gmail as mail transport, make sure that less-secure apps are allowed to access it in your settings [here](https://myaccount.google.com/lesssecureapps)
|
||||
|
||||
|
|
|
@ -486,7 +486,6 @@ export function receivePixelUpdate(
|
|||
export function loginUser(
|
||||
me: Object,
|
||||
): Action {
|
||||
console.log('login', me);
|
||||
return {
|
||||
type: 'LOGIN',
|
||||
...me,
|
||||
|
@ -794,6 +793,20 @@ export function removeChatChannel(cid: number): Action {
|
|||
};
|
||||
}
|
||||
|
||||
export function muteChatChannel(cid: number): Action {
|
||||
return {
|
||||
type: 'MUTE_CHAT_CHANNEL',
|
||||
cid,
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteChatChannel(cid: number): Action {
|
||||
return {
|
||||
type: 'UNMUTE_CHAT_CHANNEL',
|
||||
cid,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* query: Object with either userId: number or userName: string
|
||||
*/
|
||||
|
|
|
@ -72,6 +72,8 @@ export type Action =
|
|||
| { type: 'SET_CHAT_CHANNEL', cid: number }
|
||||
| { type: 'ADD_CHAT_CHANNEL', channel: Object }
|
||||
| { type: 'REMOVE_CHAT_CHANNEL', cid: number }
|
||||
| { type: 'MUTE_CHAT_CHANNEL', cid: number }
|
||||
| { type: 'UNMUTE_CHAT_CHANNEL', cid: number }
|
||||
| { type: 'SET_CHAT_FETCHING', fetching: boolean }
|
||||
| { type: 'SET_CHAT_INPUT_MSG', message: string }
|
||||
| { type: 'ADD_CHAT_INPUT_MSG', message: string }
|
||||
|
|
|
@ -19,7 +19,6 @@ import ChatBox from './ChatBox';
|
|||
import Menu from './Menu';
|
||||
import UI from './UI';
|
||||
import ExpandMenuButton from './ExpandMenuButton';
|
||||
import MinecraftTPButton from './MinecraftTPButton';
|
||||
import ModalRoot from './ModalRoot';
|
||||
|
||||
const App = () => (
|
||||
|
@ -34,7 +33,6 @@ const App = () => (
|
|||
<OnlineBox />
|
||||
<CoordinatesBox />
|
||||
<ExpandMenuButton />
|
||||
<MinecraftTPButton />
|
||||
<UI />
|
||||
<ModalRoot />
|
||||
</IconContext.Provider>
|
||||
|
|
|
@ -11,6 +11,8 @@ import { connect } from 'react-redux';
|
|||
import {
|
||||
hideContextMenu,
|
||||
setLeaveChannel,
|
||||
muteChatChannel,
|
||||
unmuteChatChannel,
|
||||
} from '../actions';
|
||||
import type { State } from '../reducers';
|
||||
|
||||
|
@ -20,6 +22,9 @@ const UserContextMenu = ({
|
|||
cid,
|
||||
channels,
|
||||
leave,
|
||||
muteArr,
|
||||
mute,
|
||||
unmute,
|
||||
close,
|
||||
}) => {
|
||||
const wrapperRef = useRef(null);
|
||||
|
@ -41,6 +46,8 @@ const UserContextMenu = ({
|
|||
};
|
||||
}, [wrapperRef]);
|
||||
|
||||
const isMuted = muteArr.includes(cid);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
|
@ -50,8 +57,19 @@ const UserContextMenu = ({
|
|||
top: yPos,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
✔✘ Mute
|
||||
<div
|
||||
role="button"
|
||||
onClick={() => {
|
||||
if (isMuted) {
|
||||
unmute(cid);
|
||||
} else {
|
||||
mute(cid);
|
||||
}
|
||||
}}
|
||||
tabIndex={0}
|
||||
style={{ borderTop: 'none' }}
|
||||
>
|
||||
{`${(isMuted) ? '✔' : '✘'} Mute`}
|
||||
</div>
|
||||
{(channels[cid][1] !== 0)
|
||||
&& (
|
||||
|
@ -62,7 +80,6 @@ const UserContextMenu = ({
|
|||
close();
|
||||
}}
|
||||
tabIndex={0}
|
||||
style={{ borderTop: 'thin solid' }}
|
||||
>
|
||||
Close
|
||||
</div>
|
||||
|
@ -83,11 +100,13 @@ function mapStateToProps(state: State) {
|
|||
const {
|
||||
cid,
|
||||
} = args;
|
||||
const { mute: muteArr } = state.chatRead;
|
||||
return {
|
||||
xPos,
|
||||
yPos,
|
||||
cid,
|
||||
channels,
|
||||
muteArr,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -99,6 +118,12 @@ function mapDispatchToProps(dispatch) {
|
|||
leave(cid) {
|
||||
dispatch(setLeaveChannel(cid));
|
||||
},
|
||||
mute(cid) {
|
||||
dispatch(muteChatChannel(cid));
|
||||
},
|
||||
unmute(cid) {
|
||||
dispatch(unmuteChatChannel(cid));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ import {
|
|||
const ChannelDropDown = ({
|
||||
channels,
|
||||
chatChannel,
|
||||
chatRead,
|
||||
unread,
|
||||
chatNotify,
|
||||
mute,
|
||||
setChannel,
|
||||
}) => {
|
||||
const [show, setShow] = useState(false);
|
||||
|
@ -27,7 +29,9 @@ const ChannelDropDown = ({
|
|||
// 1: DMs
|
||||
const [type, setType] = useState(0);
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [unreadAny, setUnreadAny] = useState(false);
|
||||
const [chatChannelName, setChatChannelName] = useState('...');
|
||||
const [hasDm, setHasDm] = useState(false);
|
||||
const wrapperRef = useRef(null);
|
||||
const buttonRef = useRef(null);
|
||||
|
||||
|
@ -44,6 +48,10 @@ const ChannelDropDown = ({
|
|||
}
|
||||
}, []);
|
||||
|
||||
const handleWindowResize = useCallback(() => {
|
||||
setShow(false);
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (show) {
|
||||
if (channels[chatChannel]) {
|
||||
|
@ -52,12 +60,45 @@ const ChannelDropDown = ({
|
|||
}
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener('touchstart', handleClickOutside);
|
||||
window.addEventListener('resize', handleWindowResize);
|
||||
} else {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('touchstart', handleClickOutside);
|
||||
window.removeEventListener('resize', handleWindowResize);
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
useEffect(() => {
|
||||
const cids = Object.keys(channels);
|
||||
let i = 0;
|
||||
while (i < cids.length) {
|
||||
const cid = cids[i];
|
||||
if (
|
||||
channels[cid][1] !== 0
|
||||
&& unread[cid]
|
||||
&& !mute.includes(cid)
|
||||
) {
|
||||
setUnreadAny(true);
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if (i === cids.length) {
|
||||
setUnreadAny(false);
|
||||
}
|
||||
}, [unread]);
|
||||
|
||||
useEffect(() => {
|
||||
const cids = Object.keys(channels);
|
||||
for (let i = 0; i < cids.length; i += 1) {
|
||||
if (channels[cids[i]][1] === 1) {
|
||||
setHasDm(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setHasDm(false);
|
||||
}, [channels]);
|
||||
|
||||
useEffect(() => {
|
||||
if (channels[chatChannel]) {
|
||||
setChatChannelName(channels[chatChannel][0]);
|
||||
|
@ -70,12 +111,14 @@ const ChannelDropDown = ({
|
|||
>
|
||||
<div
|
||||
ref={buttonRef}
|
||||
style={{
|
||||
width: 50,
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
onClick={() => setShow(!show)}
|
||||
className="channelbtn"
|
||||
className={`channelbtn${(show) ? ' selected' : ''}`}
|
||||
>
|
||||
{(unreadAny && chatNotify && !show) && (
|
||||
<div style={{ top: -4 }} className="chnunread">⦿</div>
|
||||
)}
|
||||
{chatChannelName}
|
||||
</div>
|
||||
{(show)
|
||||
|
@ -84,23 +127,41 @@ const ChannelDropDown = ({
|
|||
ref={wrapperRef}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: offset + 5,
|
||||
bottom: offset,
|
||||
right: 9,
|
||||
}}
|
||||
className="channeldd"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="chntop"
|
||||
>
|
||||
<span
|
||||
style={{ borderLeft: 'none' }}
|
||||
className={`chntype${(type === 0) ? ' selected' : ''}`}
|
||||
onClick={() => setType(0)}
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<MdChat />
|
||||
</span>
|
||||
|
|
||||
<span
|
||||
onClick={() => setType(1)}
|
||||
>
|
||||
<FaUserFriends />
|
||||
</span>
|
||||
{(hasDm)
|
||||
&& (
|
||||
<span
|
||||
className={
|
||||
`chntype${
|
||||
(type === 1) ? ' selected' : ''
|
||||
}`
|
||||
}
|
||||
onClick={() => setType(1)}
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
>
|
||||
{(unreadAny && chatNotify && type !== 1) && (
|
||||
<div className="chnunread">⦿</div>
|
||||
)}
|
||||
<FaUserFriends />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="channeldds"
|
||||
|
@ -116,8 +177,7 @@ const ChannelDropDown = ({
|
|||
}
|
||||
return false;
|
||||
}).map((cid) => {
|
||||
const [name,, lastTs] = channels[cid];
|
||||
console.log(`name ${name} lastTC ${lastTs} compare to ${chatRead[cid]}`);
|
||||
const [name] = channels[cid];
|
||||
return (
|
||||
<div
|
||||
onClick={() => setChannel(cid)}
|
||||
|
@ -126,10 +186,12 @@ const ChannelDropDown = ({
|
|||
(cid === chatChannel) ? ' selected' : ''
|
||||
}`
|
||||
}
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
>
|
||||
{
|
||||
(chatRead[cid] < lastTs) ? (
|
||||
<span className="chnunread">※</span>
|
||||
(unread[cid] && chatNotify && !mute.includes(cid)) ? (
|
||||
<div className="chnunread">⦿</div>
|
||||
) : null
|
||||
}
|
||||
{name}
|
||||
|
@ -145,15 +207,19 @@ const ChannelDropDown = ({
|
|||
};
|
||||
|
||||
function mapStateToProps(state: State) {
|
||||
const { channels } = state.chat;
|
||||
const {
|
||||
chatChannel,
|
||||
chatRead,
|
||||
} = state.gui;
|
||||
const { channels } = state.chat;
|
||||
unread,
|
||||
mute,
|
||||
} = state.chatRead;
|
||||
const { chatNotify } = state.audio;
|
||||
return {
|
||||
channels,
|
||||
chatChannel,
|
||||
chatRead,
|
||||
unread,
|
||||
mute,
|
||||
chatNotify,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import { connect } from 'react-redux';
|
|||
import type { State } from '../reducers';
|
||||
import ChatMessage from './ChatMessage';
|
||||
import ChannelDropDown from './ChannelDropDown';
|
||||
import { MAX_CHAT_MESSAGES } from '../core/constants';
|
||||
|
||||
import {
|
||||
showUserAreaModal,
|
||||
|
@ -23,7 +22,6 @@ import {
|
|||
showContextMenu,
|
||||
} from '../actions';
|
||||
import ProtocolClient from '../socket/ProtocolClient';
|
||||
import { saveSelection, restoreSelection } from '../utils/storeSelection';
|
||||
import splitChatMessage from '../core/chatMessageFilter';
|
||||
|
||||
function escapeRegExp(string) {
|
||||
|
@ -48,7 +46,6 @@ const Chat = ({
|
|||
}) => {
|
||||
const listRef = useRef();
|
||||
const targetRef = useRef();
|
||||
const [selection, setSelection] = useState(null);
|
||||
const [nameRegExp, setNameRegExp] = useState(null);
|
||||
const [blockedIds, setBlockedIds] = useState([]);
|
||||
const [btnSize, setBtnSize] = useState(20);
|
||||
|
@ -59,7 +56,7 @@ const Chat = ({
|
|||
});
|
||||
|
||||
const channelMessages = messages[chatChannel] || [];
|
||||
if (!messages[chatChannel] && !fetching) {
|
||||
if (channels[chatChannel] && !messages[chatChannel] && !fetching) {
|
||||
fetchMessages(chatChannel);
|
||||
}
|
||||
|
||||
|
@ -67,16 +64,6 @@ const Chat = ({
|
|||
stayScrolled();
|
||||
}, [channelMessages.length]);
|
||||
|
||||
/*
|
||||
* TODO this removes focus from chat box, fix this
|
||||
*
|
||||
useEffect(() => {
|
||||
if (channelMessages.length === MAX_CHAT_MESSAGES) {
|
||||
restoreSelection(selection);
|
||||
}
|
||||
}, [channelMessages]);
|
||||
*/
|
||||
|
||||
useEffect(() => {
|
||||
const regExp = (ownName)
|
||||
? new RegExp(`(^|\\s)(@${escapeRegExp(ownName)})(\\s|$)`, 'g')
|
||||
|
@ -168,7 +155,6 @@ const Chat = ({
|
|||
className="chatarea"
|
||||
ref={listRef}
|
||||
style={{ flexGrow: 1 }}
|
||||
onMouseUp={() => { setSelection(saveSelection); }}
|
||||
role="presentation"
|
||||
>
|
||||
{
|
||||
|
@ -204,6 +190,7 @@ const Chat = ({
|
|||
style={{ flexGrow: 1, minWidth: 40 }}
|
||||
value={inputMessage}
|
||||
onChange={(e) => setInputMessage(e.target.value)}
|
||||
autoComplete="off"
|
||||
id="chatmsginput"
|
||||
maxLength="200"
|
||||
type="text"
|
||||
|
@ -235,7 +222,7 @@ const Chat = ({
|
|||
|
||||
function mapStateToProps(state: State) {
|
||||
const { name } = state.user;
|
||||
const { chatChannel } = state.gui;
|
||||
const { chatChannel } = state.chatRead;
|
||||
const {
|
||||
channels,
|
||||
messages,
|
||||
|
|
|
@ -3,24 +3,77 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, {
|
||||
useState, useEffect,
|
||||
} from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { MdForum } from 'react-icons/md';
|
||||
|
||||
import { showChatModal } from '../actions';
|
||||
|
||||
|
||||
const ChatButton = ({ open }) => (
|
||||
<div
|
||||
id="chatbutton"
|
||||
className="actionbuttons"
|
||||
onClick={open}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<MdForum />
|
||||
</div>: null
|
||||
);
|
||||
const ChatButton = ({
|
||||
chatOpen,
|
||||
modalOpen,
|
||||
chatNotify,
|
||||
channels,
|
||||
unread,
|
||||
mute,
|
||||
open,
|
||||
}) => {
|
||||
const [unreadAny, setUnreadAny] = useState(false);
|
||||
|
||||
/*
|
||||
* almost the same as in ChannelDropDown
|
||||
* just cares about chatNotify too
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!chatNotify || modalOpen || chatOpen) {
|
||||
setUnreadAny(false);
|
||||
return;
|
||||
}
|
||||
const cids = Object.keys(channels);
|
||||
let i = 0;
|
||||
while (i < cids.length) {
|
||||
const cid = cids[i];
|
||||
if (
|
||||
channels[cid][1] !== 0
|
||||
&& unread[cid]
|
||||
&& !mute.includes(cid)
|
||||
) {
|
||||
setUnreadAny(true);
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if (i === cids.length) {
|
||||
setUnreadAny(false);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
id="chatbutton"
|
||||
className="actionbuttons"
|
||||
onClick={open}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
{(unreadAny) && (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
bottom: 27,
|
||||
right: 62,
|
||||
top: 'unset',
|
||||
}}
|
||||
className="chnunread"
|
||||
>⦿</div>
|
||||
)}
|
||||
<MdForum />
|
||||
</div>: null
|
||||
);
|
||||
};
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
|
@ -30,4 +83,29 @@ function mapDispatchToProps(dispatch) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ChatButton);
|
||||
function mapStateToProps(state) {
|
||||
const {
|
||||
chatOpen,
|
||||
modalOpen,
|
||||
} = state.modal;
|
||||
const {
|
||||
chatNotify,
|
||||
} = state.audio;
|
||||
const {
|
||||
channels,
|
||||
} = state.chat;
|
||||
const {
|
||||
unread,
|
||||
mute,
|
||||
} = state.chatRead;
|
||||
return {
|
||||
chatOpen,
|
||||
modalOpen,
|
||||
chatNotify,
|
||||
channels,
|
||||
unread,
|
||||
mute,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ChatButton);
|
||||
|
|
|
@ -11,7 +11,13 @@ import HelpButton from './HelpButton';
|
|||
import SettingsButton from './SettingsButton';
|
||||
import LogInButton from './LogInButton';
|
||||
import DownloadButton from './DownloadButton';
|
||||
import MinecraftButton from './MinecraftButton';
|
||||
/*
|
||||
* removed MinecraftButton cause it didn't get used in over a year
|
||||
* also CSS rule got removed
|
||||
* and MinecraftModal from ModalRoot
|
||||
* and MinecraftTPButton from App
|
||||
* (support for it will be otherwise still kept)
|
||||
*/
|
||||
|
||||
function Menu({
|
||||
menuOpen,
|
||||
|
@ -37,7 +43,6 @@ function Menu({
|
|||
<SettingsButton />
|
||||
<LogInButton />
|
||||
<DownloadButton />
|
||||
<MinecraftButton />
|
||||
<HelpButton />
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -21,7 +21,6 @@ import RegisterModal from './RegisterModal';
|
|||
import CanvasSelectModal from './CanvasSelectModal';
|
||||
import ChatModal from './ChatModal';
|
||||
import ForgotPasswordModal from './ForgotPasswordModal';
|
||||
import MinecraftModal from './MinecraftModal';
|
||||
|
||||
|
||||
const MODAL_COMPONENTS = {
|
||||
|
@ -32,7 +31,6 @@ const MODAL_COMPONENTS = {
|
|||
REGISTER: RegisterModal,
|
||||
FORGOT_PASSWORD: ForgotPasswordModal,
|
||||
CHAT: ChatModal,
|
||||
MINECRAFT: MinecraftModal,
|
||||
CANVAS_SELECTION: CanvasSelectModal,
|
||||
/* other modals */
|
||||
};
|
||||
|
|
|
@ -135,7 +135,7 @@ function SettingsModal({
|
|||
<SettingsItem
|
||||
title="Disable Game Sounds"
|
||||
// eslint-disable-next-line max-len
|
||||
description="All sound effects except Chat Notification will be disabled."
|
||||
description="All sound effects will be disabled."
|
||||
keyBind="M"
|
||||
value={isMuted}
|
||||
onToggle={onMute}
|
||||
|
|
|
@ -70,7 +70,7 @@ const SocialSettings = ({
|
|||
}
|
||||
}}
|
||||
>
|
||||
{bl[1]}
|
||||
{`⦸ ${bl[1]}`}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
addToChatInputMessage,
|
||||
startDm,
|
||||
setUserBlock,
|
||||
setChatChannel,
|
||||
} from '../actions';
|
||||
import type { State } from '../reducers';
|
||||
|
||||
|
@ -24,6 +25,9 @@ const UserContextMenu = ({
|
|||
addToInput,
|
||||
dm,
|
||||
block,
|
||||
channels,
|
||||
fetching,
|
||||
setChannel,
|
||||
close,
|
||||
}) => {
|
||||
const wrapperRef = useRef(null);
|
||||
|
@ -56,13 +60,13 @@ const UserContextMenu = ({
|
|||
}}
|
||||
>
|
||||
<div
|
||||
style={{ borderBottom: 'thin solid' }}
|
||||
onClick={() => {
|
||||
block(uid, name);
|
||||
close();
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
style={{ borderTop: 'none' }}
|
||||
>
|
||||
Block
|
||||
</div>
|
||||
|
@ -70,11 +74,24 @@ const UserContextMenu = ({
|
|||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
dm(uid);
|
||||
// TODO if DM Channel with user already exist, just switch
|
||||
/*
|
||||
* if dm channel already exists,
|
||||
* just switch
|
||||
*/
|
||||
const cids = Object.keys(channels);
|
||||
for (let i = 0; i < cids.length; i += 1) {
|
||||
const cid = cids[i];
|
||||
if (channels[cid].length === 4 && channels[cid][3] === uid) {
|
||||
setChannel(cid);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!fetching) {
|
||||
dm(uid);
|
||||
}
|
||||
close();
|
||||
}}
|
||||
style={{ borderBottom: 'thin solid' }}
|
||||
>
|
||||
DM
|
||||
</div>
|
||||
|
@ -98,15 +115,23 @@ function mapStateToProps(state: State) {
|
|||
yPos,
|
||||
args,
|
||||
} = state.contextMenu;
|
||||
const {
|
||||
channels,
|
||||
} = state.chat;
|
||||
const {
|
||||
name,
|
||||
uid,
|
||||
} = args;
|
||||
const {
|
||||
fetchingApi: fetching,
|
||||
} = state.fetching;
|
||||
return {
|
||||
xPos,
|
||||
yPos,
|
||||
channels,
|
||||
name,
|
||||
uid,
|
||||
fetching,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -129,6 +154,9 @@ function mapDispatchToProps(dispatch) {
|
|||
close() {
|
||||
dispatch(hideContextMenu());
|
||||
},
|
||||
setChannel(channelId) {
|
||||
dispatch(setChatChannel(channelId));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -211,10 +211,7 @@ class PixelPlainterControls {
|
|||
this.store,
|
||||
this.viewport,
|
||||
this.renderer,
|
||||
[
|
||||
this.clickTapStartCoords[0],
|
||||
this.clickTapStartCoords[1],
|
||||
],
|
||||
this.clickTapStartCoords,
|
||||
);
|
||||
}, 800);
|
||||
}
|
||||
|
|
|
@ -105,28 +105,22 @@ export class ChatProvider {
|
|||
userId,
|
||||
channelId,
|
||||
channelArray,
|
||||
notify = true,
|
||||
) {
|
||||
/*
|
||||
* since UserId and ChannelId are primary keys,
|
||||
* this will throw if already exists
|
||||
*/
|
||||
const relation = await UserChannel.create({
|
||||
UserId: userId,
|
||||
ChannelId: channelId,
|
||||
}, {
|
||||
const [, created] = await UserChannel.findOrCreate({
|
||||
where: {
|
||||
UserId: userId,
|
||||
ChannelId: channelId,
|
||||
},
|
||||
raw: true,
|
||||
});
|
||||
|
||||
console.log('HEREEEEE HHEEERRREEE');
|
||||
console.log(relation);
|
||||
|
||||
webSockets.broadcastAddChatChannel(
|
||||
userId,
|
||||
channelId,
|
||||
channelArray,
|
||||
notify,
|
||||
);
|
||||
if (created) {
|
||||
webSockets.broadcastAddChatChannel(
|
||||
userId,
|
||||
channelId,
|
||||
channelArray,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
userHasChannelAccess(user, cid, write = false) {
|
||||
|
@ -134,7 +128,7 @@ export class ChatProvider {
|
|||
if (!write || user.regUser) {
|
||||
return true;
|
||||
}
|
||||
} else if (user.regUser && user.channelIds.includes(cid)) {
|
||||
} else if (user.regUser && user.channels[cid]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -146,7 +140,7 @@ export class ChatProvider {
|
|||
}
|
||||
const channelArray = user.channels[cid];
|
||||
if (channelArray && channelArray.length === 4) {
|
||||
return user.channels[cid][4];
|
||||
return user.channels[cid][3];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,10 @@ export const CHAT_CHANNELS = [
|
|||
name: 'en',
|
||||
}, {
|
||||
name: 'int',
|
||||
}, {
|
||||
name: 'pol',
|
||||
}, {
|
||||
name: 'art',
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -30,11 +30,13 @@ const Message = Model.define('Message', {
|
|||
Message.belongsTo(Channel, {
|
||||
as: 'channel',
|
||||
foreignKey: 'cid',
|
||||
onDelete: 'cascade',
|
||||
});
|
||||
|
||||
Message.belongsTo(RegUser, {
|
||||
as: 'user',
|
||||
foreignKey: 'uid',
|
||||
onDelete: 'cascade',
|
||||
});
|
||||
|
||||
export default Message;
|
||||
|
|
|
@ -64,10 +64,11 @@ const RegUser = Model.define('User', {
|
|||
defaultValue: false,
|
||||
},
|
||||
|
||||
blockDm: {
|
||||
type: DataType.BOOLEAN,
|
||||
// currently just blockDm
|
||||
blocks: {
|
||||
type: DataType.TINYINT,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
defaultValue: 0,
|
||||
},
|
||||
|
||||
discordid: {
|
||||
|
@ -120,6 +121,10 @@ const RegUser = Model.define('User', {
|
|||
mcVerified(): boolean {
|
||||
return this.verified & 0x02;
|
||||
},
|
||||
|
||||
blockDm(): boolean {
|
||||
return this.blocks & 0x01;
|
||||
},
|
||||
},
|
||||
|
||||
setterMethods: {
|
||||
|
@ -133,6 +138,11 @@ const RegUser = Model.define('User', {
|
|||
this.setDataValue('verified', val);
|
||||
},
|
||||
|
||||
blockDm(num: boolean) {
|
||||
const val = (num) ? (this.blocks | 0x01) : (this.blocks & ~0x01);
|
||||
this.setDataValue('blocks', val);
|
||||
},
|
||||
|
||||
password(value: string) {
|
||||
if (value) this.setDataValue('password', generateHash(value));
|
||||
},
|
||||
|
|
|
@ -29,7 +29,6 @@ class User {
|
|||
this.id = id;
|
||||
this.ip = ip;
|
||||
this.channels = {};
|
||||
this.channelIds = [];
|
||||
this.blocked = [];
|
||||
this.ipSub = getIPv6Subnet(ip);
|
||||
this.wait = null;
|
||||
|
@ -64,7 +63,6 @@ class User {
|
|||
dmu1,
|
||||
dmu2,
|
||||
} = reguser.channel[i];
|
||||
this.channelIds.push(id);
|
||||
if (type === 1) {
|
||||
/* in DMs:
|
||||
* the name is the name of the other user
|
||||
|
@ -72,19 +70,19 @@ class User {
|
|||
*/
|
||||
const name = (dmu1.id === this.id) ? dmu2.name : dmu1.name;
|
||||
const dmu = (dmu1.id === this.id) ? dmu2.id : dmu1.id;
|
||||
this.channels[id] = [
|
||||
this.addChannel(id, [
|
||||
name,
|
||||
type,
|
||||
lastTs,
|
||||
dmu,
|
||||
];
|
||||
]);
|
||||
} else {
|
||||
const { name } = reguser.channel[i];
|
||||
this.channels[id] = [
|
||||
this.addChannel(id, [
|
||||
name,
|
||||
type,
|
||||
lastTs,
|
||||
];
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +97,14 @@ class User {
|
|||
}
|
||||
}
|
||||
|
||||
addChannel(cid, channelArray) {
|
||||
this.channels[cid] = channelArray;
|
||||
}
|
||||
|
||||
removeChannel(cid) {
|
||||
delete this.channels[cid];
|
||||
}
|
||||
|
||||
getName() {
|
||||
return (this.regUser) ? this.regUser.name : null;
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ export default function chat(
|
|||
const channels = { ...state.channels };
|
||||
const messages = { ...state.messages };
|
||||
const keys = Object.keys(channels);
|
||||
for (let i = 0; i < messages.length; i += 1) {
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const cid = keys[i];
|
||||
if (channels[cid][1] === 0) {
|
||||
delete messages[cid];
|
||||
|
@ -81,7 +81,7 @@ export default function chat(
|
|||
*/
|
||||
const channels = { ...state.channels };
|
||||
const chanKeys = Object.keys(channels);
|
||||
for (let i = 0; i < chanKeys; i += 1) {
|
||||
for (let i = 0; i < chanKeys.length; i += 1) {
|
||||
const cid = chanKeys[i];
|
||||
if (channels[cid][1] === 1 && channels[cid][3] === userId) {
|
||||
delete channels[cid];
|
||||
|
@ -109,7 +109,10 @@ export default function chat(
|
|||
|
||||
case 'ADD_CHAT_CHANNEL': {
|
||||
const { channel } = action;
|
||||
console.log('adding channel', channel);
|
||||
const [cid] = Object.keys(channel);
|
||||
if (state.channels[cid]) {
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
channels: {
|
||||
|
@ -121,11 +124,17 @@ export default function chat(
|
|||
|
||||
case 'REMOVE_CHAT_CHANNEL': {
|
||||
const { cid } = action;
|
||||
if (!state.channels[cid]) {
|
||||
return state;
|
||||
}
|
||||
const channels = { ...state.channels };
|
||||
const messages = { ...state.messages };
|
||||
delete messages[cid];
|
||||
delete channels[cid];
|
||||
return {
|
||||
...state,
|
||||
channels,
|
||||
messages,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
153
src/reducers/chatRead.js
Normal file
153
src/reducers/chatRead.js
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* local save state for chat stuff
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type { Action } from '../actions/types';
|
||||
|
||||
const TIME_DIFF_THREASHOLD = 15000;
|
||||
|
||||
export type ChatReadState = {
|
||||
// channels that are muted
|
||||
// [cid, cid2, ...]
|
||||
mute: Array,
|
||||
// timestamps of last read
|
||||
// {cid: lastTs, ...}
|
||||
readTs: Object,
|
||||
// booleans if channel is unread
|
||||
// {cid: unread, ...}
|
||||
unread: Object,
|
||||
// selected chat channel
|
||||
chatChannel: number,
|
||||
};
|
||||
|
||||
const initialState: ChatReadState = {
|
||||
mute: [],
|
||||
readTs: {},
|
||||
unread: {},
|
||||
chatChannel: 1,
|
||||
};
|
||||
|
||||
|
||||
export default function chatRead(
|
||||
state: ModalState = initialState,
|
||||
action: Action,
|
||||
): ChatReadState {
|
||||
switch (action.type) {
|
||||
case 'RECEIVE_ME':
|
||||
case 'LOGIN': {
|
||||
const { channels } = action;
|
||||
const cids = Object.keys(channels);
|
||||
const readTs = {};
|
||||
const unread = {};
|
||||
for (let i = 0; i < cids.length; i += 1) {
|
||||
const cid = cids[i];
|
||||
if (!state.readTs[cid]) {
|
||||
readTs[cid] = 0;
|
||||
} else {
|
||||
readTs[cid] = state.readTs[cid];
|
||||
}
|
||||
unread[cid] = (channels[cid][2] > readTs[cid]);
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
readTs,
|
||||
unread,
|
||||
};
|
||||
}
|
||||
|
||||
case 'SET_CHAT_CHANNEL': {
|
||||
const { cid } = action;
|
||||
return {
|
||||
...state,
|
||||
chatChannel: cid,
|
||||
readTs: {
|
||||
...state.readTs,
|
||||
[cid]: Date.now() + TIME_DIFF_THREASHOLD,
|
||||
},
|
||||
unread: {
|
||||
...state.unread,
|
||||
[cid]: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
case 'ADD_CHAT_CHANNEL': {
|
||||
const [cid] = Object.keys(action.channel);
|
||||
return {
|
||||
...state,
|
||||
readTs: {
|
||||
...state.readTs,
|
||||
[cid]: state.readTs[cid] || 0,
|
||||
},
|
||||
unread: {
|
||||
...state.unread,
|
||||
[cid]: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
case 'REMOVE_CHAT_CHANNEL': {
|
||||
const { cid } = action;
|
||||
if (!state.readTs[cid]) {
|
||||
return state;
|
||||
}
|
||||
const readTs = { ...state.readTs };
|
||||
delete readTs[cid];
|
||||
const unread = { ...state.unread };
|
||||
delete unread[cid];
|
||||
return {
|
||||
...state,
|
||||
readTs,
|
||||
unread,
|
||||
};
|
||||
}
|
||||
|
||||
case 'RECEIVE_CHAT_MESSAGE': {
|
||||
const { channel: cid } = action;
|
||||
const { chatChannel } = state;
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const readTs = (chatChannel == cid)
|
||||
? {
|
||||
...state.readTs,
|
||||
// 15s treshold for desync
|
||||
[cid]: Date.now() + TIME_DIFF_THREASHOLD,
|
||||
} : state.readTs;
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const unread = (chatChannel != cid)
|
||||
? {
|
||||
...state.unread,
|
||||
[cid]: true,
|
||||
} : state.unread;
|
||||
return {
|
||||
...state,
|
||||
readTs,
|
||||
unread,
|
||||
};
|
||||
}
|
||||
|
||||
case 'MUTE_CHAT_CHANNEL': {
|
||||
const { cid } = action;
|
||||
return {
|
||||
...state,
|
||||
mute: [
|
||||
...state.mute,
|
||||
cid,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'UNMUTE_CHAT_CHANNEL': {
|
||||
const { cid } = action;
|
||||
const mute = state.mute.filter((id) => (id !== cid));
|
||||
return {
|
||||
...state,
|
||||
mute,
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -15,10 +15,6 @@ export type GUIState = {
|
|||
compactPalette: boolean,
|
||||
paletteOpen: boolean,
|
||||
menuOpen: boolean,
|
||||
chatChannel: number,
|
||||
// timestamps of last read post per channel
|
||||
// { 1: Date.now() }
|
||||
chatRead: {},
|
||||
style: string,
|
||||
};
|
||||
|
||||
|
@ -33,8 +29,6 @@ const initialState: GUIState = {
|
|||
compactPalette: false,
|
||||
paletteOpen: true,
|
||||
menuOpen: false,
|
||||
chatChannel: 1,
|
||||
chatRead: {},
|
||||
style: 'default',
|
||||
};
|
||||
|
||||
|
@ -108,19 +102,6 @@ export default function gui(
|
|||
};
|
||||
}
|
||||
|
||||
case 'SET_CHAT_CHANNEL': {
|
||||
const { cid } = action;
|
||||
|
||||
return {
|
||||
...state,
|
||||
chatChannel: cid,
|
||||
chatRead: {
|
||||
...state.chatRead,
|
||||
cid: Date.now(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
case 'SELECT_COLOR': {
|
||||
const {
|
||||
compactPalette,
|
||||
|
@ -145,43 +126,6 @@ export default function gui(
|
|||
};
|
||||
}
|
||||
|
||||
case 'RECEIVE_ME':
|
||||
case 'LOGIN': {
|
||||
const { channels } = action;
|
||||
const cids = Object.keys(channels);
|
||||
const chatRead = { ...state.chatRead };
|
||||
for (let i = 0; i < cids.length; i += 1) {
|
||||
const cid = cids[i];
|
||||
chatRead[cid] = 0;
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
chatRead,
|
||||
};
|
||||
}
|
||||
|
||||
case 'ADD_CHAT_CHANNEL': {
|
||||
const [cid] = Object.keys(action.channel);
|
||||
return {
|
||||
...state,
|
||||
chatRead: {
|
||||
...state.chatRead,
|
||||
[cid]: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
case 'REMOVE_CHAT_CHANNEL': {
|
||||
const { cid } = action;
|
||||
const chatRead = { ...state.chatRead };
|
||||
delete chatRead[cid];
|
||||
return {
|
||||
...state,
|
||||
chatRead,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
case 'PLACE_PIXEL': {
|
||||
let { pixelsPlaced } = state;
|
||||
pixelsPlaced += 1;
|
||||
|
|
|
@ -9,6 +9,7 @@ import modal from './modal';
|
|||
import user from './user';
|
||||
import chat from './chat';
|
||||
import contextMenu from './contextMenu';
|
||||
import chatRead from './chatRead';
|
||||
import fetching from './fetching';
|
||||
|
||||
import type { AudioState } from './audio';
|
||||
|
@ -28,6 +29,7 @@ export type State = {
|
|||
user: UserState,
|
||||
chat: ChatState,
|
||||
contextMenu: ContextMenuState,
|
||||
chatRead: ChatReadState,
|
||||
fetching: FetchingState,
|
||||
};
|
||||
|
||||
|
@ -52,5 +54,6 @@ export default persistCombineReducers(config, {
|
|||
user,
|
||||
chat,
|
||||
contextMenu,
|
||||
chatRead,
|
||||
fetching,
|
||||
});
|
||||
|
|
|
@ -108,8 +108,8 @@ async function block(req: Request, res: Response) {
|
|||
if (channel) {
|
||||
const channelId = channel.id;
|
||||
channel.destroy();
|
||||
webSockets.broadcastRemoveChatChannel(user.id, channelId, false);
|
||||
webSockets.broadcastRemoveChatChannel(userId, channelId, true);
|
||||
webSockets.broadcastRemoveChatChannel(user.id, channelId);
|
||||
webSockets.broadcastRemoveChatChannel(userId, channelId);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
|
|
|
@ -45,10 +45,10 @@ async function blockdm(req: Request, res: Response) {
|
|||
const channel = channels[i];
|
||||
if (channel.type === 1) {
|
||||
const channelId = channel.id;
|
||||
channel.destroy();
|
||||
const { dmu1id, dmu2id } = channel;
|
||||
webSockets.broadcastRemoveChatChannel(dmu1id, channelId, true);
|
||||
webSockets.broadcastRemoveChatChannel(dmu2id, channelId, true);
|
||||
channel.destroy();
|
||||
webSockets.broadcastRemoveChatChannel(dmu1id, channelId);
|
||||
webSockets.broadcastRemoveChatChannel(dmu2id, channelId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ async function leaveChan(req: Request, res: Response) {
|
|||
|
||||
user.regUser.removeChannel(channel);
|
||||
|
||||
webSockets.broadcastRemoveChatChannel(user.id, channelId, false);
|
||||
webSockets.broadcastRemoveChatChannel(user.id, channelId);
|
||||
|
||||
res.json({
|
||||
status: 'ok',
|
||||
|
|
|
@ -47,12 +47,6 @@ async function startDm(req: Request, res: Response) {
|
|||
|
||||
const targetUser = await RegUser.findOne({
|
||||
where: query,
|
||||
attributes: [
|
||||
'id',
|
||||
'name',
|
||||
'blockDm',
|
||||
],
|
||||
raw: true,
|
||||
});
|
||||
if (!targetUser) {
|
||||
res.status(401);
|
||||
|
@ -61,14 +55,15 @@ async function startDm(req: Request, res: Response) {
|
|||
});
|
||||
return;
|
||||
}
|
||||
userId = targetUser.id;
|
||||
userName = targetUser.name;
|
||||
if (targetUser.blockDm) {
|
||||
res.status(401);
|
||||
res.json({
|
||||
errors: ['Target user doesn\'t allo DMs'],
|
||||
errors: [`${userName} doesn't allow DMs`],
|
||||
});
|
||||
return;
|
||||
}
|
||||
userId = targetUser.id;
|
||||
userName = targetUser.name;
|
||||
|
||||
/*
|
||||
* check if blocked
|
||||
|
@ -76,7 +71,7 @@ async function startDm(req: Request, res: Response) {
|
|||
if (await isUserBlockedBy(user.id, userId)) {
|
||||
res.status(401);
|
||||
res.json({
|
||||
errors: ['You are blocked by this user'],
|
||||
errors: [`${userName} has blocked you.`],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -106,10 +101,19 @@ async function startDm(req: Request, res: Response) {
|
|||
raw: true,
|
||||
});
|
||||
const ChannelId = channel[0].id;
|
||||
const curTime = Date.now();
|
||||
|
||||
const promises = [
|
||||
ChatProvider.addUserToChannel(user.id, ChannelId, false),
|
||||
ChatProvider.addUserToChannel(userId, ChannelId, true),
|
||||
ChatProvider.addUserToChannel(
|
||||
user.id,
|
||||
ChannelId,
|
||||
[userName, 1, curTime, userId],
|
||||
),
|
||||
ChatProvider.addUserToChannel(
|
||||
userId,
|
||||
ChannelId,
|
||||
[user.getName(), 1, curTime, user.id],
|
||||
),
|
||||
];
|
||||
await Promise.all(promises);
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ class SocketServer extends WebSocketEvents {
|
|||
});
|
||||
ws.on('message', (message) => {
|
||||
if (typeof message === 'string') {
|
||||
SocketServer.onTextMessage(message, ws);
|
||||
this.onTextMessage(message, ws);
|
||||
} else {
|
||||
this.onBinaryMessage(message, ws);
|
||||
}
|
||||
|
@ -175,13 +175,19 @@ class SocketServer extends WebSocketEvents {
|
|||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* keep in mind that a user could
|
||||
* be connected from multiple devices
|
||||
*/
|
||||
findWsByUserId(userId) {
|
||||
const { clients } = this.wss;
|
||||
for (let i = 0; i < clients.length; i += 1) {
|
||||
const ws = clients[i];
|
||||
const it = this.wss.clients.keys();
|
||||
let client = it.next();
|
||||
while (!client.done) {
|
||||
const ws = client.value;
|
||||
if (ws.user.id === userId && ws.readyState === WebSocket.OPEN) {
|
||||
return ws;
|
||||
}
|
||||
client = it.next();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -190,35 +196,31 @@ class SocketServer extends WebSocketEvents {
|
|||
userId: number,
|
||||
channelId: number,
|
||||
channelArray: Array,
|
||||
notify: boolean,
|
||||
) {
|
||||
const ws = this.findWsByUserId(userId);
|
||||
if (ws) {
|
||||
ws.user.channels[channelId] = channelArray;
|
||||
const text = JSON.stringify([
|
||||
'addch', {
|
||||
[channelId]: channelArray,
|
||||
},
|
||||
]);
|
||||
if (notify) {
|
||||
this.wss.clients.forEach((ws) => {
|
||||
if (ws.user.id === userId && ws.readyState === WebSocket.OPEN) {
|
||||
ws.user.addChannel(channelId, channelArray);
|
||||
const text = JSON.stringify([
|
||||
'addch', {
|
||||
[channelId]: channelArray,
|
||||
},
|
||||
]);
|
||||
ws.send(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
broadcastRemoveChatChannel(
|
||||
userId: number,
|
||||
channelId: number,
|
||||
notify: boolean,
|
||||
) {
|
||||
const ws = this.findWsByUserId(userId);
|
||||
if (ws) {
|
||||
delete ws.user.channels[channelId];
|
||||
const text = JSON.stringify('remch', channelId);
|
||||
if (notify) {
|
||||
this.wss.clients.forEach((ws) => {
|
||||
if (ws.user.id === userId && ws.readyState === WebSocket.OPEN) {
|
||||
ws.user.removeChannel(channelId);
|
||||
const text = JSON.stringify(['remch', channelId]);
|
||||
ws.send(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
broadcastPixelBuffer(canvasId: number, chunkid, data: Buffer) {
|
||||
|
@ -286,7 +288,7 @@ class SocketServer extends WebSocketEvents {
|
|||
webSockets.broadcastOnlineCounter(online);
|
||||
}
|
||||
|
||||
static async onTextMessage(text, ws) {
|
||||
async onTextMessage(text, ws) {
|
||||
/*
|
||||
* all client -> server text messages are
|
||||
* chat messages in [message, channelId] format
|
||||
|
@ -333,13 +335,11 @@ class SocketServer extends WebSocketEvents {
|
|||
*/
|
||||
const dmUserId = chatProvider.checkIfDm(user, channelId);
|
||||
if (dmUserId) {
|
||||
console.log('is dm');
|
||||
const dmWs = this.findWsByUserId(dmUserId);
|
||||
if (!dmWs
|
||||
if (!dmWs
|
||||
|| !chatProvider.userHasChannelAccess(dmWs.user, channelId)
|
||||
) {
|
||||
console.log('adding channel')
|
||||
ChatProvider.addUserToChannel(
|
||||
await ChatProvider.addUserToChannel(
|
||||
dmUserId,
|
||||
channelId,
|
||||
[ws.name, 1, Date.now(), user.id],
|
||||
|
|
|
@ -28,14 +28,12 @@ class WebSocketEvents {
|
|||
userId: number,
|
||||
channelId: number,
|
||||
channelArray: Array,
|
||||
notify: boolean,
|
||||
) {
|
||||
}
|
||||
|
||||
broadcastRemoveChatChannel(
|
||||
userId: number,
|
||||
channelId: number,
|
||||
notify: boolean,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
|
@ -94,20 +94,17 @@ class WebSockets {
|
|||
* @param userId numerical id of user
|
||||
* @param channelId numerical id of chat channel
|
||||
* @param channelArray array with channel info [name, type, lastTs]
|
||||
* @param notify if user should get notified over websocket
|
||||
* (i.e. false if the user already gets it via api response)
|
||||
*/
|
||||
broadcastAddChatChannel(
|
||||
userId: number,
|
||||
channelId: number,
|
||||
channelArray: Array,
|
||||
notify: boolean = true,
|
||||
) {
|
||||
this.listeners.forEach(
|
||||
(listener) => listener.broadcastAddChatChannel(
|
||||
userId,
|
||||
channelId,
|
||||
channelArray,
|
||||
notify,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -116,19 +113,16 @@ class WebSockets {
|
|||
* broadcast Removing chat channel from user
|
||||
* @param userId numerical id of user
|
||||
* @param channelId numerical id of chat channel
|
||||
* @param notify if user should get notified over websocket
|
||||
* (i.e. false if the user already gets it via api response)
|
||||
*/
|
||||
broadcastRemoveChatChannel(
|
||||
userId: number,
|
||||
channelId: number,
|
||||
notify: boolean = true,
|
||||
) {
|
||||
this.listeners.forEach(
|
||||
(listener) => listener.broadcastRemoveChatChannel(
|
||||
userId,
|
||||
channelId,
|
||||
notify,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -200,21 +200,31 @@ export default (store) => (next) => (action) => {
|
|||
}
|
||||
|
||||
case 'RECEIVE_CHAT_MESSAGE': {
|
||||
if (!chatNotify) break;
|
||||
if (mute || !chatNotify) break;
|
||||
|
||||
const { isPing } = action;
|
||||
const { chatChannel } = state.gui;
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (!isPing && action.channel != chatChannel) {
|
||||
break;
|
||||
}
|
||||
const { isPing, channel } = action;
|
||||
const { mute: muteCh, chatChannel } = state.chatRead;
|
||||
if (muteCh.includes(channel)) break;
|
||||
if (muteCh.includes(`${channel}`)) break;
|
||||
const { channels } = state.chat;
|
||||
|
||||
const oscillatorNode = context.createOscillator();
|
||||
const gainNode = context.createGain();
|
||||
|
||||
oscillatorNode.type = 'sine';
|
||||
oscillatorNode.frequency.setValueAtTime(310, context.currentTime);
|
||||
const freq = (isPing) ? 540 : 355;
|
||||
/*
|
||||
* ping if user mention or
|
||||
* message in DM channel that is not currently open
|
||||
*/
|
||||
const freq = (isPing
|
||||
|| (
|
||||
channels[channel]
|
||||
&& channels[channel][1] === 1
|
||||
// eslint-disable-next-line eqeqeq
|
||||
&& channel != chatChannel
|
||||
)
|
||||
) ? 540 : 355;
|
||||
oscillatorNode.frequency.exponentialRampToValueAtTime(
|
||||
freq,
|
||||
context.currentTime + 0.025,
|
||||
|
|
|
@ -19,7 +19,7 @@ export default (store) => (next) => (action) => {
|
|||
}
|
||||
|
||||
case 'SET_NAME':
|
||||
case 'LOGIN:':
|
||||
case 'LOGIN':
|
||||
case 'LOGOUT': {
|
||||
ProtocolClient.reconnect();
|
||||
break;
|
||||
|
|
|
@ -22,10 +22,14 @@ tr:nth-child(odd) {
|
|||
color: #ff91a6;
|
||||
}
|
||||
|
||||
.actionbuttons:hover, .menu > div:hover {
|
||||
.actionbuttons:hover, .menu > div:hover, .channeldd, .contextmenu {
|
||||
background: linear-gradient(160deg, #61dcea , #ffb1e1, #ecffec, #ffb1e1, #61dcea);
|
||||
}
|
||||
|
||||
.chn, .chntype, .contextmenu > div {
|
||||
background-color: #ebebeb80;
|
||||
}
|
||||
|
||||
#chatbutton {
|
||||
background: linear-gradient(135deg, orange , yellow, green, aqua, blue, violet);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,25 @@ tr:nth-child(even) {
|
|||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.channeldd, .contextmenu {
|
||||
background-color: #535356;
|
||||
color: #efefef;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.chntop {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.chn, .chntype, .contextmenu > div {
|
||||
background-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.chn.selected, .chn:hover, .chntype.selected, .chntype:hover,
|
||||
.contextmenu > div:hover {
|
||||
background-color: #404040;
|
||||
}
|
||||
|
||||
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, #historyselect {
|
||||
background-color: rgba(59, 59, 59, 0.8);
|
||||
color: #f4f4f4;
|
||||
|
|
|
@ -65,6 +65,20 @@ tr:nth-child(even) {
|
|||
background-color: hsla(216, 4%, 74%, .3);
|
||||
}
|
||||
|
||||
.channeldd, .contextmenu {
|
||||
background-color: #535356;
|
||||
color: #efefef;
|
||||
}
|
||||
|
||||
.chn, .chntype, .contextmenu > div {
|
||||
background-color: #5f5f5f;
|
||||
}
|
||||
|
||||
.chn.selected, .chn:hover, .chntype.selected, .chntype:hover,
|
||||
.contextmenu > div:hover {
|
||||
background-color: #404040;
|
||||
}
|
||||
|
||||
.modalinfo {
|
||||
color: #ddd;
|
||||
}
|
||||
|
|
|
@ -134,15 +134,20 @@ tr:nth-child(even) {
|
|||
background-color: rgba(226, 226, 226);
|
||||
border: solid black;
|
||||
border-width: thin;
|
||||
color: #212121;
|
||||
box-shadow: 0 0 2px 2px rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
.contextmenu > div {
|
||||
border-width: thin;
|
||||
margin: 2px;
|
||||
height: 18px;
|
||||
padding: 3px 2px 0px 0px;
|
||||
background-color: #ebebeb;
|
||||
border-top: thin solid #b1b1b2;
|
||||
}
|
||||
|
||||
.contextmenu > div:hover {
|
||||
background-color: white;
|
||||
background-color: #c9c9c9;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@ -155,29 +160,42 @@ tr:nth-child(even) {
|
|||
}
|
||||
|
||||
.channelbtn {
|
||||
background-color: #d0d0d0;
|
||||
position: relative;
|
||||
background-color: #ebebeb;
|
||||
text-align: center;
|
||||
border-style: solid;
|
||||
border-width: thin;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.channelbtn:hover {
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
overflow-x: hidden;
|
||||
color: #212121;
|
||||
}
|
||||
|
||||
.channeldd {
|
||||
background-color: rgba(226, 226, 226);
|
||||
color: #212121;
|
||||
border: solid black;
|
||||
border-width: thin;
|
||||
width: 90px;
|
||||
box-shadow: 0 0 2px 2px rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
.channeldds {
|
||||
height: 120px;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.chntop {
|
||||
display: flex;
|
||||
height: 24px;
|
||||
border-bottom: solid thin;
|
||||
}
|
||||
|
||||
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, #palettebox {
|
||||
position: fixed;
|
||||
background-color: rgba(226, 226, 226, 0.80);
|
||||
|
@ -224,10 +242,6 @@ tr:nth-child(even) {
|
|||
}
|
||||
|
||||
#helpbutton {
|
||||
left: 16px;
|
||||
top: 221px;
|
||||
}
|
||||
#minecraftbutton {
|
||||
left: 16px;
|
||||
top: 180px;
|
||||
}
|
||||
|
@ -485,15 +499,43 @@ tr:nth-child(even) {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chn.selected, .chnunread {
|
||||
font-weight: bold;
|
||||
font-size: 17px;
|
||||
.chn {
|
||||
position: relative;
|
||||
background-color: #ebebeb;
|
||||
white-space: nowrap;
|
||||
border-bottom: solid thin #b1b1b2;
|
||||
padding: 1px;
|
||||
font-size: 15px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.chn.selected, .chn:hover, .channelbtn.selected, .channelbtn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #c9c9c9;
|
||||
}
|
||||
|
||||
.chnunread {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: 1px;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.chntype {
|
||||
position: relative;
|
||||
flex: auto;
|
||||
text-align: center;
|
||||
background-color: #ebebeb;
|
||||
border-left: solid thin #b1b1b2;
|
||||
}
|
||||
.chntype.selected, .chntype:hover {
|
||||
cursor: pointer;
|
||||
font-size: 110%;
|
||||
background-color: #c9c9c9;
|
||||
}
|
||||
|
||||
.usermessages {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
.chatbox {
|
||||
.chatbox, .channeldd, .contextmenu {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.chntop {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, #historyselect {
|
||||
border-radius: 21px;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user