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:
|
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.
|
- 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
|
- 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)
|
- 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(
|
export function loginUser(
|
||||||
me: Object,
|
me: Object,
|
||||||
): Action {
|
): Action {
|
||||||
console.log('login', me);
|
|
||||||
return {
|
return {
|
||||||
type: 'LOGIN',
|
type: 'LOGIN',
|
||||||
...me,
|
...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
|
* query: Object with either userId: number or userName: string
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -72,6 +72,8 @@ export type Action =
|
||||||
| { type: 'SET_CHAT_CHANNEL', cid: number }
|
| { type: 'SET_CHAT_CHANNEL', cid: number }
|
||||||
| { type: 'ADD_CHAT_CHANNEL', channel: Object }
|
| { type: 'ADD_CHAT_CHANNEL', channel: Object }
|
||||||
| { type: 'REMOVE_CHAT_CHANNEL', cid: number }
|
| { 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_FETCHING', fetching: boolean }
|
||||||
| { type: 'SET_CHAT_INPUT_MSG', message: string }
|
| { type: 'SET_CHAT_INPUT_MSG', message: string }
|
||||||
| { type: 'ADD_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 Menu from './Menu';
|
||||||
import UI from './UI';
|
import UI from './UI';
|
||||||
import ExpandMenuButton from './ExpandMenuButton';
|
import ExpandMenuButton from './ExpandMenuButton';
|
||||||
import MinecraftTPButton from './MinecraftTPButton';
|
|
||||||
import ModalRoot from './ModalRoot';
|
import ModalRoot from './ModalRoot';
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
|
@ -34,7 +33,6 @@ const App = () => (
|
||||||
<OnlineBox />
|
<OnlineBox />
|
||||||
<CoordinatesBox />
|
<CoordinatesBox />
|
||||||
<ExpandMenuButton />
|
<ExpandMenuButton />
|
||||||
<MinecraftTPButton />
|
|
||||||
<UI />
|
<UI />
|
||||||
<ModalRoot />
|
<ModalRoot />
|
||||||
</IconContext.Provider>
|
</IconContext.Provider>
|
||||||
|
|
|
@ -11,6 +11,8 @@ import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
hideContextMenu,
|
hideContextMenu,
|
||||||
setLeaveChannel,
|
setLeaveChannel,
|
||||||
|
muteChatChannel,
|
||||||
|
unmuteChatChannel,
|
||||||
} from '../actions';
|
} from '../actions';
|
||||||
import type { State } from '../reducers';
|
import type { State } from '../reducers';
|
||||||
|
|
||||||
|
@ -20,6 +22,9 @@ const UserContextMenu = ({
|
||||||
cid,
|
cid,
|
||||||
channels,
|
channels,
|
||||||
leave,
|
leave,
|
||||||
|
muteArr,
|
||||||
|
mute,
|
||||||
|
unmute,
|
||||||
close,
|
close,
|
||||||
}) => {
|
}) => {
|
||||||
const wrapperRef = useRef(null);
|
const wrapperRef = useRef(null);
|
||||||
|
@ -41,6 +46,8 @@ const UserContextMenu = ({
|
||||||
};
|
};
|
||||||
}, [wrapperRef]);
|
}, [wrapperRef]);
|
||||||
|
|
||||||
|
const isMuted = muteArr.includes(cid);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={wrapperRef}
|
ref={wrapperRef}
|
||||||
|
@ -50,8 +57,19 @@ const UserContextMenu = ({
|
||||||
top: yPos,
|
top: yPos,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div
|
||||||
✔✘ Mute
|
role="button"
|
||||||
|
onClick={() => {
|
||||||
|
if (isMuted) {
|
||||||
|
unmute(cid);
|
||||||
|
} else {
|
||||||
|
mute(cid);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
tabIndex={0}
|
||||||
|
style={{ borderTop: 'none' }}
|
||||||
|
>
|
||||||
|
{`${(isMuted) ? '✔' : '✘'} Mute`}
|
||||||
</div>
|
</div>
|
||||||
{(channels[cid][1] !== 0)
|
{(channels[cid][1] !== 0)
|
||||||
&& (
|
&& (
|
||||||
|
@ -62,7 +80,6 @@ const UserContextMenu = ({
|
||||||
close();
|
close();
|
||||||
}}
|
}}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
style={{ borderTop: 'thin solid' }}
|
|
||||||
>
|
>
|
||||||
Close
|
Close
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,11 +100,13 @@ function mapStateToProps(state: State) {
|
||||||
const {
|
const {
|
||||||
cid,
|
cid,
|
||||||
} = args;
|
} = args;
|
||||||
|
const { mute: muteArr } = state.chatRead;
|
||||||
return {
|
return {
|
||||||
xPos,
|
xPos,
|
||||||
yPos,
|
yPos,
|
||||||
cid,
|
cid,
|
||||||
channels,
|
channels,
|
||||||
|
muteArr,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +118,12 @@ function mapDispatchToProps(dispatch) {
|
||||||
leave(cid) {
|
leave(cid) {
|
||||||
dispatch(setLeaveChannel(cid));
|
dispatch(setLeaveChannel(cid));
|
||||||
},
|
},
|
||||||
|
mute(cid) {
|
||||||
|
dispatch(muteChatChannel(cid));
|
||||||
|
},
|
||||||
|
unmute(cid) {
|
||||||
|
dispatch(unmuteChatChannel(cid));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,9 @@ import {
|
||||||
const ChannelDropDown = ({
|
const ChannelDropDown = ({
|
||||||
channels,
|
channels,
|
||||||
chatChannel,
|
chatChannel,
|
||||||
chatRead,
|
unread,
|
||||||
|
chatNotify,
|
||||||
|
mute,
|
||||||
setChannel,
|
setChannel,
|
||||||
}) => {
|
}) => {
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
|
@ -27,7 +29,9 @@ const ChannelDropDown = ({
|
||||||
// 1: DMs
|
// 1: DMs
|
||||||
const [type, setType] = useState(0);
|
const [type, setType] = useState(0);
|
||||||
const [offset, setOffset] = useState(0);
|
const [offset, setOffset] = useState(0);
|
||||||
|
const [unreadAny, setUnreadAny] = useState(false);
|
||||||
const [chatChannelName, setChatChannelName] = useState('...');
|
const [chatChannelName, setChatChannelName] = useState('...');
|
||||||
|
const [hasDm, setHasDm] = useState(false);
|
||||||
const wrapperRef = useRef(null);
|
const wrapperRef = useRef(null);
|
||||||
const buttonRef = useRef(null);
|
const buttonRef = useRef(null);
|
||||||
|
|
||||||
|
@ -44,6 +48,10 @@ const ChannelDropDown = ({
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleWindowResize = useCallback(() => {
|
||||||
|
setShow(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (show) {
|
if (show) {
|
||||||
if (channels[chatChannel]) {
|
if (channels[chatChannel]) {
|
||||||
|
@ -52,12 +60,45 @@ const ChannelDropDown = ({
|
||||||
}
|
}
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
document.addEventListener('touchstart', handleClickOutside);
|
document.addEventListener('touchstart', handleClickOutside);
|
||||||
|
window.addEventListener('resize', handleWindowResize);
|
||||||
} else {
|
} else {
|
||||||
document.removeEventListener('mousedown', handleClickOutside);
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
document.removeEventListener('touchstart', handleClickOutside);
|
document.removeEventListener('touchstart', handleClickOutside);
|
||||||
|
window.removeEventListener('resize', handleWindowResize);
|
||||||
}
|
}
|
||||||
}, [show]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
if (channels[chatChannel]) {
|
if (channels[chatChannel]) {
|
||||||
setChatChannelName(channels[chatChannel][0]);
|
setChatChannelName(channels[chatChannel][0]);
|
||||||
|
@ -70,12 +111,14 @@ const ChannelDropDown = ({
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
style={{
|
role="button"
|
||||||
width: 50,
|
tabIndex={-1}
|
||||||
}}
|
|
||||||
onClick={() => setShow(!show)}
|
onClick={() => setShow(!show)}
|
||||||
className="channelbtn"
|
className={`channelbtn${(show) ? ' selected' : ''}`}
|
||||||
>
|
>
|
||||||
|
{(unreadAny && chatNotify && !show) && (
|
||||||
|
<div style={{ top: -4 }} className="chnunread">⦿</div>
|
||||||
|
)}
|
||||||
{chatChannelName}
|
{chatChannelName}
|
||||||
</div>
|
</div>
|
||||||
{(show)
|
{(show)
|
||||||
|
@ -84,23 +127,41 @@ const ChannelDropDown = ({
|
||||||
ref={wrapperRef}
|
ref={wrapperRef}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: offset + 5,
|
bottom: offset,
|
||||||
right: 9,
|
right: 9,
|
||||||
}}
|
}}
|
||||||
className="channeldd"
|
className="channeldd"
|
||||||
>
|
>
|
||||||
<div>
|
<div
|
||||||
|
className="chntop"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
|
style={{ borderLeft: 'none' }}
|
||||||
|
className={`chntype${(type === 0) ? ' selected' : ''}`}
|
||||||
onClick={() => setType(0)}
|
onClick={() => setType(0)}
|
||||||
|
role="button"
|
||||||
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<MdChat />
|
<MdChat />
|
||||||
</span>
|
</span>
|
||||||
|
|
{(hasDm)
|
||||||
<span
|
&& (
|
||||||
onClick={() => setType(1)}
|
<span
|
||||||
>
|
className={
|
||||||
<FaUserFriends />
|
`chntype${
|
||||||
</span>
|
(type === 1) ? ' selected' : ''
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
onClick={() => setType(1)}
|
||||||
|
role="button"
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
|
{(unreadAny && chatNotify && type !== 1) && (
|
||||||
|
<div className="chnunread">⦿</div>
|
||||||
|
)}
|
||||||
|
<FaUserFriends />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="channeldds"
|
className="channeldds"
|
||||||
|
@ -116,8 +177,7 @@ const ChannelDropDown = ({
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}).map((cid) => {
|
}).map((cid) => {
|
||||||
const [name,, lastTs] = channels[cid];
|
const [name] = channels[cid];
|
||||||
console.log(`name ${name} lastTC ${lastTs} compare to ${chatRead[cid]}`);
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => setChannel(cid)}
|
onClick={() => setChannel(cid)}
|
||||||
|
@ -126,10 +186,12 @@ const ChannelDropDown = ({
|
||||||
(cid === chatChannel) ? ' selected' : ''
|
(cid === chatChannel) ? ' selected' : ''
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
|
role="button"
|
||||||
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
(chatRead[cid] < lastTs) ? (
|
(unread[cid] && chatNotify && !mute.includes(cid)) ? (
|
||||||
<span className="chnunread">※</span>
|
<div className="chnunread">⦿</div>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
{name}
|
{name}
|
||||||
|
@ -145,15 +207,19 @@ const ChannelDropDown = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
function mapStateToProps(state: State) {
|
function mapStateToProps(state: State) {
|
||||||
|
const { channels } = state.chat;
|
||||||
const {
|
const {
|
||||||
chatChannel,
|
chatChannel,
|
||||||
chatRead,
|
unread,
|
||||||
} = state.gui;
|
mute,
|
||||||
const { channels } = state.chat;
|
} = state.chatRead;
|
||||||
|
const { chatNotify } = state.audio;
|
||||||
return {
|
return {
|
||||||
channels,
|
channels,
|
||||||
chatChannel,
|
chatChannel,
|
||||||
chatRead,
|
unread,
|
||||||
|
mute,
|
||||||
|
chatNotify,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { connect } from 'react-redux';
|
||||||
import type { State } from '../reducers';
|
import type { State } from '../reducers';
|
||||||
import ChatMessage from './ChatMessage';
|
import ChatMessage from './ChatMessage';
|
||||||
import ChannelDropDown from './ChannelDropDown';
|
import ChannelDropDown from './ChannelDropDown';
|
||||||
import { MAX_CHAT_MESSAGES } from '../core/constants';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
showUserAreaModal,
|
showUserAreaModal,
|
||||||
|
@ -23,7 +22,6 @@ import {
|
||||||
showContextMenu,
|
showContextMenu,
|
||||||
} from '../actions';
|
} from '../actions';
|
||||||
import ProtocolClient from '../socket/ProtocolClient';
|
import ProtocolClient from '../socket/ProtocolClient';
|
||||||
import { saveSelection, restoreSelection } from '../utils/storeSelection';
|
|
||||||
import splitChatMessage from '../core/chatMessageFilter';
|
import splitChatMessage from '../core/chatMessageFilter';
|
||||||
|
|
||||||
function escapeRegExp(string) {
|
function escapeRegExp(string) {
|
||||||
|
@ -48,7 +46,6 @@ const Chat = ({
|
||||||
}) => {
|
}) => {
|
||||||
const listRef = useRef();
|
const listRef = useRef();
|
||||||
const targetRef = useRef();
|
const targetRef = useRef();
|
||||||
const [selection, setSelection] = useState(null);
|
|
||||||
const [nameRegExp, setNameRegExp] = useState(null);
|
const [nameRegExp, setNameRegExp] = useState(null);
|
||||||
const [blockedIds, setBlockedIds] = useState([]);
|
const [blockedIds, setBlockedIds] = useState([]);
|
||||||
const [btnSize, setBtnSize] = useState(20);
|
const [btnSize, setBtnSize] = useState(20);
|
||||||
|
@ -59,7 +56,7 @@ const Chat = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const channelMessages = messages[chatChannel] || [];
|
const channelMessages = messages[chatChannel] || [];
|
||||||
if (!messages[chatChannel] && !fetching) {
|
if (channels[chatChannel] && !messages[chatChannel] && !fetching) {
|
||||||
fetchMessages(chatChannel);
|
fetchMessages(chatChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,16 +64,6 @@ const Chat = ({
|
||||||
stayScrolled();
|
stayScrolled();
|
||||||
}, [channelMessages.length]);
|
}, [channelMessages.length]);
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO this removes focus from chat box, fix this
|
|
||||||
*
|
|
||||||
useEffect(() => {
|
|
||||||
if (channelMessages.length === MAX_CHAT_MESSAGES) {
|
|
||||||
restoreSelection(selection);
|
|
||||||
}
|
|
||||||
}, [channelMessages]);
|
|
||||||
*/
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const regExp = (ownName)
|
const regExp = (ownName)
|
||||||
? new RegExp(`(^|\\s)(@${escapeRegExp(ownName)})(\\s|$)`, 'g')
|
? new RegExp(`(^|\\s)(@${escapeRegExp(ownName)})(\\s|$)`, 'g')
|
||||||
|
@ -168,7 +155,6 @@ const Chat = ({
|
||||||
className="chatarea"
|
className="chatarea"
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
style={{ flexGrow: 1 }}
|
style={{ flexGrow: 1 }}
|
||||||
onMouseUp={() => { setSelection(saveSelection); }}
|
|
||||||
role="presentation"
|
role="presentation"
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
@ -204,6 +190,7 @@ const Chat = ({
|
||||||
style={{ flexGrow: 1, minWidth: 40 }}
|
style={{ flexGrow: 1, minWidth: 40 }}
|
||||||
value={inputMessage}
|
value={inputMessage}
|
||||||
onChange={(e) => setInputMessage(e.target.value)}
|
onChange={(e) => setInputMessage(e.target.value)}
|
||||||
|
autoComplete="off"
|
||||||
id="chatmsginput"
|
id="chatmsginput"
|
||||||
maxLength="200"
|
maxLength="200"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -235,7 +222,7 @@ const Chat = ({
|
||||||
|
|
||||||
function mapStateToProps(state: State) {
|
function mapStateToProps(state: State) {
|
||||||
const { name } = state.user;
|
const { name } = state.user;
|
||||||
const { chatChannel } = state.gui;
|
const { chatChannel } = state.chatRead;
|
||||||
const {
|
const {
|
||||||
channels,
|
channels,
|
||||||
messages,
|
messages,
|
||||||
|
|
|
@ -3,24 +3,77 @@
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {
|
||||||
|
useState, useEffect,
|
||||||
|
} from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { MdForum } from 'react-icons/md';
|
import { MdForum } from 'react-icons/md';
|
||||||
|
|
||||||
import { showChatModal } from '../actions';
|
import { showChatModal } from '../actions';
|
||||||
|
|
||||||
|
|
||||||
const ChatButton = ({ open }) => (
|
const ChatButton = ({
|
||||||
<div
|
chatOpen,
|
||||||
id="chatbutton"
|
modalOpen,
|
||||||
className="actionbuttons"
|
chatNotify,
|
||||||
onClick={open}
|
channels,
|
||||||
role="button"
|
unread,
|
||||||
tabIndex={0}
|
mute,
|
||||||
>
|
open,
|
||||||
<MdForum />
|
}) => {
|
||||||
</div>: null
|
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) {
|
function mapDispatchToProps(dispatch) {
|
||||||
return {
|
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 SettingsButton from './SettingsButton';
|
||||||
import LogInButton from './LogInButton';
|
import LogInButton from './LogInButton';
|
||||||
import DownloadButton from './DownloadButton';
|
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({
|
function Menu({
|
||||||
menuOpen,
|
menuOpen,
|
||||||
|
@ -37,7 +43,6 @@ function Menu({
|
||||||
<SettingsButton />
|
<SettingsButton />
|
||||||
<LogInButton />
|
<LogInButton />
|
||||||
<DownloadButton />
|
<DownloadButton />
|
||||||
<MinecraftButton />
|
|
||||||
<HelpButton />
|
<HelpButton />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,6 @@ import RegisterModal from './RegisterModal';
|
||||||
import CanvasSelectModal from './CanvasSelectModal';
|
import CanvasSelectModal from './CanvasSelectModal';
|
||||||
import ChatModal from './ChatModal';
|
import ChatModal from './ChatModal';
|
||||||
import ForgotPasswordModal from './ForgotPasswordModal';
|
import ForgotPasswordModal from './ForgotPasswordModal';
|
||||||
import MinecraftModal from './MinecraftModal';
|
|
||||||
|
|
||||||
|
|
||||||
const MODAL_COMPONENTS = {
|
const MODAL_COMPONENTS = {
|
||||||
|
@ -32,7 +31,6 @@ const MODAL_COMPONENTS = {
|
||||||
REGISTER: RegisterModal,
|
REGISTER: RegisterModal,
|
||||||
FORGOT_PASSWORD: ForgotPasswordModal,
|
FORGOT_PASSWORD: ForgotPasswordModal,
|
||||||
CHAT: ChatModal,
|
CHAT: ChatModal,
|
||||||
MINECRAFT: MinecraftModal,
|
|
||||||
CANVAS_SELECTION: CanvasSelectModal,
|
CANVAS_SELECTION: CanvasSelectModal,
|
||||||
/* other modals */
|
/* other modals */
|
||||||
};
|
};
|
||||||
|
|
|
@ -135,7 +135,7 @@ function SettingsModal({
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="Disable Game Sounds"
|
title="Disable Game Sounds"
|
||||||
// eslint-disable-next-line max-len
|
// 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"
|
keyBind="M"
|
||||||
value={isMuted}
|
value={isMuted}
|
||||||
onToggle={onMute}
|
onToggle={onMute}
|
||||||
|
|
|
@ -70,7 +70,7 @@ const SocialSettings = ({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{bl[1]}
|
{`⦸ ${bl[1]}`}
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
addToChatInputMessage,
|
addToChatInputMessage,
|
||||||
startDm,
|
startDm,
|
||||||
setUserBlock,
|
setUserBlock,
|
||||||
|
setChatChannel,
|
||||||
} from '../actions';
|
} from '../actions';
|
||||||
import type { State } from '../reducers';
|
import type { State } from '../reducers';
|
||||||
|
|
||||||
|
@ -24,6 +25,9 @@ const UserContextMenu = ({
|
||||||
addToInput,
|
addToInput,
|
||||||
dm,
|
dm,
|
||||||
block,
|
block,
|
||||||
|
channels,
|
||||||
|
fetching,
|
||||||
|
setChannel,
|
||||||
close,
|
close,
|
||||||
}) => {
|
}) => {
|
||||||
const wrapperRef = useRef(null);
|
const wrapperRef = useRef(null);
|
||||||
|
@ -56,13 +60,13 @@ const UserContextMenu = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{ borderBottom: 'thin solid' }}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
block(uid, name);
|
block(uid, name);
|
||||||
close();
|
close();
|
||||||
}}
|
}}
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
style={{ borderTop: 'none' }}
|
||||||
>
|
>
|
||||||
Block
|
Block
|
||||||
</div>
|
</div>
|
||||||
|
@ -70,11 +74,24 @@ const UserContextMenu = ({
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => {
|
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();
|
close();
|
||||||
}}
|
}}
|
||||||
style={{ borderBottom: 'thin solid' }}
|
|
||||||
>
|
>
|
||||||
DM
|
DM
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,15 +115,23 @@ function mapStateToProps(state: State) {
|
||||||
yPos,
|
yPos,
|
||||||
args,
|
args,
|
||||||
} = state.contextMenu;
|
} = state.contextMenu;
|
||||||
|
const {
|
||||||
|
channels,
|
||||||
|
} = state.chat;
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
uid,
|
uid,
|
||||||
} = args;
|
} = args;
|
||||||
|
const {
|
||||||
|
fetchingApi: fetching,
|
||||||
|
} = state.fetching;
|
||||||
return {
|
return {
|
||||||
xPos,
|
xPos,
|
||||||
yPos,
|
yPos,
|
||||||
|
channels,
|
||||||
name,
|
name,
|
||||||
uid,
|
uid,
|
||||||
|
fetching,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +154,9 @@ function mapDispatchToProps(dispatch) {
|
||||||
close() {
|
close() {
|
||||||
dispatch(hideContextMenu());
|
dispatch(hideContextMenu());
|
||||||
},
|
},
|
||||||
|
setChannel(channelId) {
|
||||||
|
dispatch(setChatChannel(channelId));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -211,10 +211,7 @@ class PixelPlainterControls {
|
||||||
this.store,
|
this.store,
|
||||||
this.viewport,
|
this.viewport,
|
||||||
this.renderer,
|
this.renderer,
|
||||||
[
|
this.clickTapStartCoords,
|
||||||
this.clickTapStartCoords[0],
|
|
||||||
this.clickTapStartCoords[1],
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}, 800);
|
}, 800);
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,28 +105,22 @@ export class ChatProvider {
|
||||||
userId,
|
userId,
|
||||||
channelId,
|
channelId,
|
||||||
channelArray,
|
channelArray,
|
||||||
notify = true,
|
|
||||||
) {
|
) {
|
||||||
/*
|
const [, created] = await UserChannel.findOrCreate({
|
||||||
* since UserId and ChannelId are primary keys,
|
where: {
|
||||||
* this will throw if already exists
|
UserId: userId,
|
||||||
*/
|
ChannelId: channelId,
|
||||||
const relation = await UserChannel.create({
|
},
|
||||||
UserId: userId,
|
|
||||||
ChannelId: channelId,
|
|
||||||
}, {
|
|
||||||
raw: true,
|
raw: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('HEREEEEE HHEEERRREEE');
|
if (created) {
|
||||||
console.log(relation);
|
webSockets.broadcastAddChatChannel(
|
||||||
|
userId,
|
||||||
webSockets.broadcastAddChatChannel(
|
channelId,
|
||||||
userId,
|
channelArray,
|
||||||
channelId,
|
);
|
||||||
channelArray,
|
}
|
||||||
notify,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userHasChannelAccess(user, cid, write = false) {
|
userHasChannelAccess(user, cid, write = false) {
|
||||||
|
@ -134,7 +128,7 @@ export class ChatProvider {
|
||||||
if (!write || user.regUser) {
|
if (!write || user.regUser) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (user.regUser && user.channelIds.includes(cid)) {
|
} else if (user.regUser && user.channels[cid]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -146,7 +140,7 @@ export class ChatProvider {
|
||||||
}
|
}
|
||||||
const channelArray = user.channels[cid];
|
const channelArray = user.channels[cid];
|
||||||
if (channelArray && channelArray.length === 4) {
|
if (channelArray && channelArray.length === 4) {
|
||||||
return user.channels[cid][4];
|
return user.channels[cid][3];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,10 @@ export const CHAT_CHANNELS = [
|
||||||
name: 'en',
|
name: 'en',
|
||||||
}, {
|
}, {
|
||||||
name: 'int',
|
name: 'int',
|
||||||
|
}, {
|
||||||
|
name: 'pol',
|
||||||
|
}, {
|
||||||
|
name: 'art',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -30,11 +30,13 @@ const Message = Model.define('Message', {
|
||||||
Message.belongsTo(Channel, {
|
Message.belongsTo(Channel, {
|
||||||
as: 'channel',
|
as: 'channel',
|
||||||
foreignKey: 'cid',
|
foreignKey: 'cid',
|
||||||
|
onDelete: 'cascade',
|
||||||
});
|
});
|
||||||
|
|
||||||
Message.belongsTo(RegUser, {
|
Message.belongsTo(RegUser, {
|
||||||
as: 'user',
|
as: 'user',
|
||||||
foreignKey: 'uid',
|
foreignKey: 'uid',
|
||||||
|
onDelete: 'cascade',
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Message;
|
export default Message;
|
||||||
|
|
|
@ -64,10 +64,11 @@ const RegUser = Model.define('User', {
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
blockDm: {
|
// currently just blockDm
|
||||||
type: DataType.BOOLEAN,
|
blocks: {
|
||||||
|
type: DataType.TINYINT,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
defaultValue: false,
|
defaultValue: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
discordid: {
|
discordid: {
|
||||||
|
@ -120,6 +121,10 @@ const RegUser = Model.define('User', {
|
||||||
mcVerified(): boolean {
|
mcVerified(): boolean {
|
||||||
return this.verified & 0x02;
|
return this.verified & 0x02;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
blockDm(): boolean {
|
||||||
|
return this.blocks & 0x01;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setterMethods: {
|
setterMethods: {
|
||||||
|
@ -133,6 +138,11 @@ const RegUser = Model.define('User', {
|
||||||
this.setDataValue('verified', val);
|
this.setDataValue('verified', val);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
blockDm(num: boolean) {
|
||||||
|
const val = (num) ? (this.blocks | 0x01) : (this.blocks & ~0x01);
|
||||||
|
this.setDataValue('blocks', val);
|
||||||
|
},
|
||||||
|
|
||||||
password(value: string) {
|
password(value: string) {
|
||||||
if (value) this.setDataValue('password', generateHash(value));
|
if (value) this.setDataValue('password', generateHash(value));
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,7 +29,6 @@ class User {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.ip = ip;
|
this.ip = ip;
|
||||||
this.channels = {};
|
this.channels = {};
|
||||||
this.channelIds = [];
|
|
||||||
this.blocked = [];
|
this.blocked = [];
|
||||||
this.ipSub = getIPv6Subnet(ip);
|
this.ipSub = getIPv6Subnet(ip);
|
||||||
this.wait = null;
|
this.wait = null;
|
||||||
|
@ -64,7 +63,6 @@ class User {
|
||||||
dmu1,
|
dmu1,
|
||||||
dmu2,
|
dmu2,
|
||||||
} = reguser.channel[i];
|
} = reguser.channel[i];
|
||||||
this.channelIds.push(id);
|
|
||||||
if (type === 1) {
|
if (type === 1) {
|
||||||
/* in DMs:
|
/* in DMs:
|
||||||
* the name is the name of the other user
|
* 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 name = (dmu1.id === this.id) ? dmu2.name : dmu1.name;
|
||||||
const dmu = (dmu1.id === this.id) ? dmu2.id : dmu1.id;
|
const dmu = (dmu1.id === this.id) ? dmu2.id : dmu1.id;
|
||||||
this.channels[id] = [
|
this.addChannel(id, [
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
lastTs,
|
lastTs,
|
||||||
dmu,
|
dmu,
|
||||||
];
|
]);
|
||||||
} else {
|
} else {
|
||||||
const { name } = reguser.channel[i];
|
const { name } = reguser.channel[i];
|
||||||
this.channels[id] = [
|
this.addChannel(id, [
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
lastTs,
|
lastTs,
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +97,14 @@ class User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addChannel(cid, channelArray) {
|
||||||
|
this.channels[cid] = channelArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeChannel(cid) {
|
||||||
|
delete this.channels[cid];
|
||||||
|
}
|
||||||
|
|
||||||
getName() {
|
getName() {
|
||||||
return (this.regUser) ? this.regUser.name : null;
|
return (this.regUser) ? this.regUser.name : null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default function chat(
|
||||||
const channels = { ...state.channels };
|
const channels = { ...state.channels };
|
||||||
const messages = { ...state.messages };
|
const messages = { ...state.messages };
|
||||||
const keys = Object.keys(channels);
|
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];
|
const cid = keys[i];
|
||||||
if (channels[cid][1] === 0) {
|
if (channels[cid][1] === 0) {
|
||||||
delete messages[cid];
|
delete messages[cid];
|
||||||
|
@ -81,7 +81,7 @@ export default function chat(
|
||||||
*/
|
*/
|
||||||
const channels = { ...state.channels };
|
const channels = { ...state.channels };
|
||||||
const chanKeys = Object.keys(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];
|
const cid = chanKeys[i];
|
||||||
if (channels[cid][1] === 1 && channels[cid][3] === userId) {
|
if (channels[cid][1] === 1 && channels[cid][3] === userId) {
|
||||||
delete channels[cid];
|
delete channels[cid];
|
||||||
|
@ -109,7 +109,10 @@ export default function chat(
|
||||||
|
|
||||||
case 'ADD_CHAT_CHANNEL': {
|
case 'ADD_CHAT_CHANNEL': {
|
||||||
const { channel } = action;
|
const { channel } = action;
|
||||||
console.log('adding channel', channel);
|
const [cid] = Object.keys(channel);
|
||||||
|
if (state.channels[cid]) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
channels: {
|
channels: {
|
||||||
|
@ -121,11 +124,17 @@ export default function chat(
|
||||||
|
|
||||||
case 'REMOVE_CHAT_CHANNEL': {
|
case 'REMOVE_CHAT_CHANNEL': {
|
||||||
const { cid } = action;
|
const { cid } = action;
|
||||||
|
if (!state.channels[cid]) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
const channels = { ...state.channels };
|
const channels = { ...state.channels };
|
||||||
|
const messages = { ...state.messages };
|
||||||
|
delete messages[cid];
|
||||||
delete channels[cid];
|
delete channels[cid];
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
channels,
|
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,
|
compactPalette: boolean,
|
||||||
paletteOpen: boolean,
|
paletteOpen: boolean,
|
||||||
menuOpen: boolean,
|
menuOpen: boolean,
|
||||||
chatChannel: number,
|
|
||||||
// timestamps of last read post per channel
|
|
||||||
// { 1: Date.now() }
|
|
||||||
chatRead: {},
|
|
||||||
style: string,
|
style: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,8 +29,6 @@ const initialState: GUIState = {
|
||||||
compactPalette: false,
|
compactPalette: false,
|
||||||
paletteOpen: true,
|
paletteOpen: true,
|
||||||
menuOpen: false,
|
menuOpen: false,
|
||||||
chatChannel: 1,
|
|
||||||
chatRead: {},
|
|
||||||
style: 'default',
|
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': {
|
case 'SELECT_COLOR': {
|
||||||
const {
|
const {
|
||||||
compactPalette,
|
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': {
|
case 'PLACE_PIXEL': {
|
||||||
let { pixelsPlaced } = state;
|
let { pixelsPlaced } = state;
|
||||||
pixelsPlaced += 1;
|
pixelsPlaced += 1;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import modal from './modal';
|
||||||
import user from './user';
|
import user from './user';
|
||||||
import chat from './chat';
|
import chat from './chat';
|
||||||
import contextMenu from './contextMenu';
|
import contextMenu from './contextMenu';
|
||||||
|
import chatRead from './chatRead';
|
||||||
import fetching from './fetching';
|
import fetching from './fetching';
|
||||||
|
|
||||||
import type { AudioState } from './audio';
|
import type { AudioState } from './audio';
|
||||||
|
@ -28,6 +29,7 @@ export type State = {
|
||||||
user: UserState,
|
user: UserState,
|
||||||
chat: ChatState,
|
chat: ChatState,
|
||||||
contextMenu: ContextMenuState,
|
contextMenu: ContextMenuState,
|
||||||
|
chatRead: ChatReadState,
|
||||||
fetching: FetchingState,
|
fetching: FetchingState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,5 +54,6 @@ export default persistCombineReducers(config, {
|
||||||
user,
|
user,
|
||||||
chat,
|
chat,
|
||||||
contextMenu,
|
contextMenu,
|
||||||
|
chatRead,
|
||||||
fetching,
|
fetching,
|
||||||
});
|
});
|
||||||
|
|
|
@ -108,8 +108,8 @@ async function block(req: Request, res: Response) {
|
||||||
if (channel) {
|
if (channel) {
|
||||||
const channelId = channel.id;
|
const channelId = channel.id;
|
||||||
channel.destroy();
|
channel.destroy();
|
||||||
webSockets.broadcastRemoveChatChannel(user.id, channelId, false);
|
webSockets.broadcastRemoveChatChannel(user.id, channelId);
|
||||||
webSockets.broadcastRemoveChatChannel(userId, channelId, true);
|
webSockets.broadcastRemoveChatChannel(userId, channelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
@ -45,10 +45,10 @@ async function blockdm(req: Request, res: Response) {
|
||||||
const channel = channels[i];
|
const channel = channels[i];
|
||||||
if (channel.type === 1) {
|
if (channel.type === 1) {
|
||||||
const channelId = channel.id;
|
const channelId = channel.id;
|
||||||
channel.destroy();
|
|
||||||
const { dmu1id, dmu2id } = channel;
|
const { dmu1id, dmu2id } = channel;
|
||||||
webSockets.broadcastRemoveChatChannel(dmu1id, channelId, true);
|
channel.destroy();
|
||||||
webSockets.broadcastRemoveChatChannel(dmu2id, channelId, true);
|
webSockets.broadcastRemoveChatChannel(dmu1id, channelId);
|
||||||
|
webSockets.broadcastRemoveChatChannel(dmu2id, channelId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ async function leaveChan(req: Request, res: Response) {
|
||||||
|
|
||||||
user.regUser.removeChannel(channel);
|
user.regUser.removeChannel(channel);
|
||||||
|
|
||||||
webSockets.broadcastRemoveChatChannel(user.id, channelId, false);
|
webSockets.broadcastRemoveChatChannel(user.id, channelId);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
|
|
|
@ -47,12 +47,6 @@ async function startDm(req: Request, res: Response) {
|
||||||
|
|
||||||
const targetUser = await RegUser.findOne({
|
const targetUser = await RegUser.findOne({
|
||||||
where: query,
|
where: query,
|
||||||
attributes: [
|
|
||||||
'id',
|
|
||||||
'name',
|
|
||||||
'blockDm',
|
|
||||||
],
|
|
||||||
raw: true,
|
|
||||||
});
|
});
|
||||||
if (!targetUser) {
|
if (!targetUser) {
|
||||||
res.status(401);
|
res.status(401);
|
||||||
|
@ -61,14 +55,15 @@ async function startDm(req: Request, res: Response) {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
userId = targetUser.id;
|
||||||
|
userName = targetUser.name;
|
||||||
if (targetUser.blockDm) {
|
if (targetUser.blockDm) {
|
||||||
res.status(401);
|
res.status(401);
|
||||||
res.json({
|
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
|
* check if blocked
|
||||||
|
@ -76,7 +71,7 @@ async function startDm(req: Request, res: Response) {
|
||||||
if (await isUserBlockedBy(user.id, userId)) {
|
if (await isUserBlockedBy(user.id, userId)) {
|
||||||
res.status(401);
|
res.status(401);
|
||||||
res.json({
|
res.json({
|
||||||
errors: ['You are blocked by this user'],
|
errors: [`${userName} has blocked you.`],
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -106,10 +101,19 @@ async function startDm(req: Request, res: Response) {
|
||||||
raw: true,
|
raw: true,
|
||||||
});
|
});
|
||||||
const ChannelId = channel[0].id;
|
const ChannelId = channel[0].id;
|
||||||
|
const curTime = Date.now();
|
||||||
|
|
||||||
const promises = [
|
const promises = [
|
||||||
ChatProvider.addUserToChannel(user.id, ChannelId, false),
|
ChatProvider.addUserToChannel(
|
||||||
ChatProvider.addUserToChannel(userId, ChannelId, true),
|
user.id,
|
||||||
|
ChannelId,
|
||||||
|
[userName, 1, curTime, userId],
|
||||||
|
),
|
||||||
|
ChatProvider.addUserToChannel(
|
||||||
|
userId,
|
||||||
|
ChannelId,
|
||||||
|
[user.getName(), 1, curTime, user.id],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ class SocketServer extends WebSocketEvents {
|
||||||
});
|
});
|
||||||
ws.on('message', (message) => {
|
ws.on('message', (message) => {
|
||||||
if (typeof message === 'string') {
|
if (typeof message === 'string') {
|
||||||
SocketServer.onTextMessage(message, ws);
|
this.onTextMessage(message, ws);
|
||||||
} else {
|
} else {
|
||||||
this.onBinaryMessage(message, ws);
|
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) {
|
findWsByUserId(userId) {
|
||||||
const { clients } = this.wss;
|
const it = this.wss.clients.keys();
|
||||||
for (let i = 0; i < clients.length; i += 1) {
|
let client = it.next();
|
||||||
const ws = clients[i];
|
while (!client.done) {
|
||||||
|
const ws = client.value;
|
||||||
if (ws.user.id === userId && ws.readyState === WebSocket.OPEN) {
|
if (ws.user.id === userId && ws.readyState === WebSocket.OPEN) {
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
client = it.next();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -190,35 +196,31 @@ class SocketServer extends WebSocketEvents {
|
||||||
userId: number,
|
userId: number,
|
||||||
channelId: number,
|
channelId: number,
|
||||||
channelArray: Array,
|
channelArray: Array,
|
||||||
notify: boolean,
|
|
||||||
) {
|
) {
|
||||||
const ws = this.findWsByUserId(userId);
|
this.wss.clients.forEach((ws) => {
|
||||||
if (ws) {
|
if (ws.user.id === userId && ws.readyState === WebSocket.OPEN) {
|
||||||
ws.user.channels[channelId] = channelArray;
|
ws.user.addChannel(channelId, channelArray);
|
||||||
const text = JSON.stringify([
|
const text = JSON.stringify([
|
||||||
'addch', {
|
'addch', {
|
||||||
[channelId]: channelArray,
|
[channelId]: channelArray,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
if (notify) {
|
|
||||||
ws.send(text);
|
ws.send(text);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastRemoveChatChannel(
|
broadcastRemoveChatChannel(
|
||||||
userId: number,
|
userId: number,
|
||||||
channelId: number,
|
channelId: number,
|
||||||
notify: boolean,
|
|
||||||
) {
|
) {
|
||||||
const ws = this.findWsByUserId(userId);
|
this.wss.clients.forEach((ws) => {
|
||||||
if (ws) {
|
if (ws.user.id === userId && ws.readyState === WebSocket.OPEN) {
|
||||||
delete ws.user.channels[channelId];
|
ws.user.removeChannel(channelId);
|
||||||
const text = JSON.stringify('remch', channelId);
|
const text = JSON.stringify(['remch', channelId]);
|
||||||
if (notify) {
|
|
||||||
ws.send(text);
|
ws.send(text);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastPixelBuffer(canvasId: number, chunkid, data: Buffer) {
|
broadcastPixelBuffer(canvasId: number, chunkid, data: Buffer) {
|
||||||
|
@ -286,7 +288,7 @@ class SocketServer extends WebSocketEvents {
|
||||||
webSockets.broadcastOnlineCounter(online);
|
webSockets.broadcastOnlineCounter(online);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async onTextMessage(text, ws) {
|
async onTextMessage(text, ws) {
|
||||||
/*
|
/*
|
||||||
* all client -> server text messages are
|
* all client -> server text messages are
|
||||||
* chat messages in [message, channelId] format
|
* chat messages in [message, channelId] format
|
||||||
|
@ -333,13 +335,11 @@ class SocketServer extends WebSocketEvents {
|
||||||
*/
|
*/
|
||||||
const dmUserId = chatProvider.checkIfDm(user, channelId);
|
const dmUserId = chatProvider.checkIfDm(user, channelId);
|
||||||
if (dmUserId) {
|
if (dmUserId) {
|
||||||
console.log('is dm');
|
|
||||||
const dmWs = this.findWsByUserId(dmUserId);
|
const dmWs = this.findWsByUserId(dmUserId);
|
||||||
if (!dmWs
|
if (!dmWs
|
||||||
|| !chatProvider.userHasChannelAccess(dmWs.user, channelId)
|
|| !chatProvider.userHasChannelAccess(dmWs.user, channelId)
|
||||||
) {
|
) {
|
||||||
console.log('adding channel')
|
await ChatProvider.addUserToChannel(
|
||||||
ChatProvider.addUserToChannel(
|
|
||||||
dmUserId,
|
dmUserId,
|
||||||
channelId,
|
channelId,
|
||||||
[ws.name, 1, Date.now(), user.id],
|
[ws.name, 1, Date.now(), user.id],
|
||||||
|
|
|
@ -28,14 +28,12 @@ class WebSocketEvents {
|
||||||
userId: number,
|
userId: number,
|
||||||
channelId: number,
|
channelId: number,
|
||||||
channelArray: Array,
|
channelArray: Array,
|
||||||
notify: boolean,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastRemoveChatChannel(
|
broadcastRemoveChatChannel(
|
||||||
userId: number,
|
userId: number,
|
||||||
channelId: number,
|
channelId: number,
|
||||||
notify: boolean,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,20 +94,17 @@ class WebSockets {
|
||||||
* @param userId numerical id of user
|
* @param userId numerical id of user
|
||||||
* @param channelId numerical id of chat channel
|
* @param channelId numerical id of chat channel
|
||||||
* @param channelArray array with channel info [name, type, lastTs]
|
* @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(
|
broadcastAddChatChannel(
|
||||||
userId: number,
|
userId: number,
|
||||||
channelId: number,
|
channelId: number,
|
||||||
channelArray: Array,
|
channelArray: Array,
|
||||||
notify: boolean = true,
|
|
||||||
) {
|
) {
|
||||||
this.listeners.forEach(
|
this.listeners.forEach(
|
||||||
(listener) => listener.broadcastAddChatChannel(
|
(listener) => listener.broadcastAddChatChannel(
|
||||||
userId,
|
userId,
|
||||||
|
channelId,
|
||||||
channelArray,
|
channelArray,
|
||||||
notify,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -116,19 +113,16 @@ class WebSockets {
|
||||||
* broadcast Removing chat channel from user
|
* broadcast Removing chat channel from user
|
||||||
* @param userId numerical id of user
|
* @param userId numerical id of user
|
||||||
* @param channelId numerical id of chat channel
|
* @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)
|
* (i.e. false if the user already gets it via api response)
|
||||||
*/
|
*/
|
||||||
broadcastRemoveChatChannel(
|
broadcastRemoveChatChannel(
|
||||||
userId: number,
|
userId: number,
|
||||||
channelId: number,
|
channelId: number,
|
||||||
notify: boolean = true,
|
|
||||||
) {
|
) {
|
||||||
this.listeners.forEach(
|
this.listeners.forEach(
|
||||||
(listener) => listener.broadcastRemoveChatChannel(
|
(listener) => listener.broadcastRemoveChatChannel(
|
||||||
userId,
|
userId,
|
||||||
channelId,
|
channelId,
|
||||||
notify,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,21 +200,31 @@ export default (store) => (next) => (action) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'RECEIVE_CHAT_MESSAGE': {
|
case 'RECEIVE_CHAT_MESSAGE': {
|
||||||
if (!chatNotify) break;
|
if (mute || !chatNotify) break;
|
||||||
|
|
||||||
const { isPing } = action;
|
const { isPing, channel } = action;
|
||||||
const { chatChannel } = state.gui;
|
const { mute: muteCh, chatChannel } = state.chatRead;
|
||||||
// eslint-disable-next-line eqeqeq
|
if (muteCh.includes(channel)) break;
|
||||||
if (!isPing && action.channel != chatChannel) {
|
if (muteCh.includes(`${channel}`)) break;
|
||||||
break;
|
const { channels } = state.chat;
|
||||||
}
|
|
||||||
|
|
||||||
const oscillatorNode = context.createOscillator();
|
const oscillatorNode = context.createOscillator();
|
||||||
const gainNode = context.createGain();
|
const gainNode = context.createGain();
|
||||||
|
|
||||||
oscillatorNode.type = 'sine';
|
oscillatorNode.type = 'sine';
|
||||||
oscillatorNode.frequency.setValueAtTime(310, context.currentTime);
|
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(
|
oscillatorNode.frequency.exponentialRampToValueAtTime(
|
||||||
freq,
|
freq,
|
||||||
context.currentTime + 0.025,
|
context.currentTime + 0.025,
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default (store) => (next) => (action) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_NAME':
|
case 'SET_NAME':
|
||||||
case 'LOGIN:':
|
case 'LOGIN':
|
||||||
case 'LOGOUT': {
|
case 'LOGOUT': {
|
||||||
ProtocolClient.reconnect();
|
ProtocolClient.reconnect();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -22,10 +22,14 @@ tr:nth-child(odd) {
|
||||||
color: #ff91a6;
|
color: #ff91a6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionbuttons:hover, .menu > div:hover {
|
.actionbuttons:hover, .menu > div:hover, .channeldd, .contextmenu {
|
||||||
background: linear-gradient(160deg, #61dcea , #ffb1e1, #ecffec, #ffb1e1, #61dcea);
|
background: linear-gradient(160deg, #61dcea , #ffb1e1, #ecffec, #ffb1e1, #61dcea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chn, .chntype, .contextmenu > div {
|
||||||
|
background-color: #ebebeb80;
|
||||||
|
}
|
||||||
|
|
||||||
#chatbutton {
|
#chatbutton {
|
||||||
background: linear-gradient(135deg, orange , yellow, green, aqua, blue, violet);
|
background: linear-gradient(135deg, orange , yellow, green, aqua, blue, violet);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,25 @@ tr:nth-child(even) {
|
||||||
border-radius: 8px;
|
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 {
|
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, #historyselect {
|
||||||
background-color: rgba(59, 59, 59, 0.8);
|
background-color: rgba(59, 59, 59, 0.8);
|
||||||
color: #f4f4f4;
|
color: #f4f4f4;
|
||||||
|
|
|
@ -65,6 +65,20 @@ tr:nth-child(even) {
|
||||||
background-color: hsla(216, 4%, 74%, .3);
|
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 {
|
.modalinfo {
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,15 +134,20 @@ tr:nth-child(even) {
|
||||||
background-color: rgba(226, 226, 226);
|
background-color: rgba(226, 226, 226);
|
||||||
border: solid black;
|
border: solid black;
|
||||||
border-width: thin;
|
border-width: thin;
|
||||||
|
color: #212121;
|
||||||
|
box-shadow: 0 0 2px 2px rgba(0,0,0,.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contextmenu > div {
|
.contextmenu > div {
|
||||||
border-width: thin;
|
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
|
height: 18px;
|
||||||
|
padding: 3px 2px 0px 0px;
|
||||||
|
background-color: #ebebeb;
|
||||||
|
border-top: thin solid #b1b1b2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contextmenu > div:hover {
|
.contextmenu > div:hover {
|
||||||
background-color: white;
|
background-color: #c9c9c9;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,29 +160,42 @@ tr:nth-child(even) {
|
||||||
}
|
}
|
||||||
|
|
||||||
.channelbtn {
|
.channelbtn {
|
||||||
background-color: #d0d0d0;
|
position: relative;
|
||||||
|
background-color: #ebebeb;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: thin;
|
border-width: thin;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
width: 50px;
|
||||||
|
height: 100%;
|
||||||
.channelbtn:hover {
|
white-space: nowrap;
|
||||||
cursor: pointer;
|
font-size: 14px;
|
||||||
background-color: white;
|
overflow-x: hidden;
|
||||||
|
color: #212121;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channeldd {
|
.channeldd {
|
||||||
background-color: rgba(226, 226, 226);
|
background-color: rgba(226, 226, 226);
|
||||||
|
color: #212121;
|
||||||
|
border: solid black;
|
||||||
|
border-width: thin;
|
||||||
width: 90px;
|
width: 90px;
|
||||||
|
box-shadow: 0 0 2px 2px rgba(0,0,0,.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.channeldds {
|
.channeldds {
|
||||||
height: 120px;
|
height: 120px;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chntop {
|
||||||
|
display: flex;
|
||||||
|
height: 24px;
|
||||||
|
border-bottom: solid thin;
|
||||||
|
}
|
||||||
|
|
||||||
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, #palettebox {
|
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, #palettebox {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background-color: rgba(226, 226, 226, 0.80);
|
background-color: rgba(226, 226, 226, 0.80);
|
||||||
|
@ -224,10 +242,6 @@ tr:nth-child(even) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#helpbutton {
|
#helpbutton {
|
||||||
left: 16px;
|
|
||||||
top: 221px;
|
|
||||||
}
|
|
||||||
#minecraftbutton {
|
|
||||||
left: 16px;
|
left: 16px;
|
||||||
top: 180px;
|
top: 180px;
|
||||||
}
|
}
|
||||||
|
@ -485,15 +499,43 @@ tr:nth-child(even) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chn.selected, .chnunread {
|
.chn {
|
||||||
font-weight: bold;
|
position: relative;
|
||||||
font-size: 17px;
|
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 {
|
.chnunread {
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
right: 1px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 12px;
|
||||||
color: red;
|
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 {
|
.usermessages {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
.chatbox {
|
.chatbox, .channeldd, .contextmenu {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chntop {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, #historyselect {
|
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, #historyselect {
|
||||||
border-radius: 21px;
|
border-radius: 21px;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user