move context menus and rewrite some components from react classes to hooks
This commit is contained in:
parent
c11976cdca
commit
a60242617d
|
@ -7,11 +7,13 @@
|
|||
|
||||
import { t } from 'ttag';
|
||||
|
||||
import { dateToString } from '../core/utils';
|
||||
|
||||
/*
|
||||
* Adds customizeable timeout to fetch
|
||||
* defaults to 8s
|
||||
*/
|
||||
async function fetchWithTimeout(resource, options) {
|
||||
async function fetchWithTimeout(resource, options = {}) {
|
||||
const { timeout = 8000 } = options;
|
||||
|
||||
const controller = new AbortController();
|
||||
|
@ -189,6 +191,26 @@ export async function requestSolveCaptcha(text) {
|
|||
return res;
|
||||
}
|
||||
|
||||
export async function requestHistoricalTimes(day, canvasId) {
|
||||
try {
|
||||
const date = dateToString(day);
|
||||
const url = `api/history?day=${date}&id=${canvasId}`;
|
||||
const response = await fetchWithTimeout(url);
|
||||
if (response.status !== 200) {
|
||||
return [];
|
||||
}
|
||||
const times = await response.json();
|
||||
const parsedTimes = times
|
||||
.map((a) => `${a.substr(0, 2)}:${a.substr(-2, 2)}`);
|
||||
return [
|
||||
'00:00',
|
||||
...parsedTimes,
|
||||
];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function requestPasswordChange(newPassword, password) {
|
||||
return makeAPIPOSTRequest(
|
||||
'api/auth/change_passwd',
|
||||
|
@ -202,13 +224,6 @@ export async function requestResendVerify() {
|
|||
);
|
||||
}
|
||||
|
||||
export function requestMcLink(accepted) {
|
||||
return makeAPIPOSTRequest(
|
||||
'api/auth/mclink',
|
||||
{ accepted },
|
||||
);
|
||||
}
|
||||
|
||||
export function requestNameChange(name) {
|
||||
return makeAPIPOSTRequest(
|
||||
'api/auth/change_name',
|
||||
|
|
|
@ -421,15 +421,6 @@ export function setName(
|
|||
};
|
||||
}
|
||||
|
||||
export function setMinecraftName(
|
||||
minecraftname: string,
|
||||
): Action {
|
||||
return {
|
||||
type: 'SET_MINECRAFT_NAME',
|
||||
minecraftname,
|
||||
};
|
||||
}
|
||||
|
||||
export function setMailreg(
|
||||
mailreg: boolean,
|
||||
): Action {
|
||||
|
|
|
@ -132,7 +132,6 @@ export type Action =
|
|||
| { type: 'LOGOUT' }
|
||||
| { type: 'RECEIVE_STATS', totalRanking: Object, totalDailyRanking: Object }
|
||||
| { type: 'SET_NAME', name: string }
|
||||
| { type: 'SET_MINECRAFT_NAME', minecraftname: string }
|
||||
| { type: 'SET_MAILREG', mailreg: boolean }
|
||||
| { type: 'REM_FROM_MESSAGES', message: string }
|
||||
| { type: 'SHOW_CONTEXT_MENU',
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
/* eslint-disable jsx-a11y/no-autofocus */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { t } from 'ttag';
|
||||
|
||||
|
@ -96,6 +98,7 @@ const Captcha = ({ callback, close }) => {
|
|||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
autoFocus
|
||||
style={{
|
||||
width: '6em',
|
||||
fontSize: 21,
|
||||
|
|
|
@ -2,276 +2,159 @@
|
|||
* LogIn Form
|
||||
* @flow
|
||||
*/
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import React, {
|
||||
useState, useCallback, useRef,
|
||||
} from 'react';
|
||||
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
||||
import { t } from 'ttag';
|
||||
|
||||
import type { State } from '../reducers';
|
||||
import { dateToString } from '../core/utils';
|
||||
import { selectHistoricalTime } from '../actions';
|
||||
import { requestHistoricalTimes } from '../actions/fetch';
|
||||
|
||||
function dateToString(date) {
|
||||
// YYYY-MM-DD
|
||||
const timeString = date.substr(0, 4) + date.substr(5, 2) + date.substr(8, 2);
|
||||
// YYYYMMDD
|
||||
return timeString;
|
||||
}
|
||||
function stringToDate(timeString) {
|
||||
|
||||
function stringToDate(dateString) {
|
||||
if (!dateString) return null;
|
||||
// YYYYMMDD
|
||||
// eslint-disable-next-line max-len
|
||||
const date = `${timeString.substr(0, 4)}-${timeString.substr(4, 2)}-${timeString.substr(6, 2)}`;
|
||||
// YYYY-MM-DD
|
||||
return date;
|
||||
return `${dateString.substr(0, 4)}-${dateString.substr(4, 2)}-${dateString.substr(6, 2)}`;
|
||||
}
|
||||
|
||||
async function getTimes(day, canvasId) {
|
||||
try {
|
||||
const date = dateToString(day);
|
||||
const response = await fetch(`./api/history?day=${date}&id=${canvasId}`);
|
||||
if (response.status !== 200) {
|
||||
return [];
|
||||
}
|
||||
const times = await response.json();
|
||||
const parsedTimes = times
|
||||
.map((a) => `${a.substr(0, 2)}:${a.substr(-2, 2)}`);
|
||||
return [
|
||||
'00:00',
|
||||
...parsedTimes,
|
||||
];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
function stringToTime(timeString) {
|
||||
if (!timeString) return null;
|
||||
return `${timeString.substr(0, 2)}:${timeString.substr(2, 2)}`;
|
||||
}
|
||||
|
||||
class HistorySelect extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const date = new Date();
|
||||
let day = date.getDate();
|
||||
let month = date.getMonth() + 1;
|
||||
if (month < 10) month = `0${month}`;
|
||||
if (day < 10) day = `0${day}`;
|
||||
const max = `${date.getFullYear()}-${month}-${day}`;
|
||||
|
||||
this.state = {
|
||||
submitting: false,
|
||||
selectedDate: null,
|
||||
selectedTime: null,
|
||||
times: [],
|
||||
max,
|
||||
};
|
||||
this.dateSelect = null;
|
||||
|
||||
this.handleDateChange = this.handleDateChange.bind(this);
|
||||
this.handleTimeChange = this.handleTimeChange.bind(this);
|
||||
this.changeTime = this.changeTime.bind(this);
|
||||
}
|
||||
|
||||
async handleDateChange(evt) {
|
||||
const {
|
||||
submitting,
|
||||
} = this.state;
|
||||
|
||||
if (submitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
submitting: true,
|
||||
times: [],
|
||||
selectedTime: null,
|
||||
});
|
||||
const {
|
||||
canvasId,
|
||||
setTime,
|
||||
} = this.props;
|
||||
const date = evt.target.value;
|
||||
const times = await getTimes(date, canvasId);
|
||||
if (times.length === 0) {
|
||||
this.setState({
|
||||
submitting: false,
|
||||
selectedDate: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setTime(date, times[0]);
|
||||
this.setState({
|
||||
submitting: false,
|
||||
selectedDate: date,
|
||||
selectedTime: (times) ? times[0] : null,
|
||||
times,
|
||||
});
|
||||
}
|
||||
|
||||
handleTimeChange(evt) {
|
||||
const {
|
||||
setTime,
|
||||
} = this.props;
|
||||
const {
|
||||
selectedDate,
|
||||
} = this.state;
|
||||
|
||||
const selectedTime = evt.target.value;
|
||||
this.setState({
|
||||
selectedTime,
|
||||
});
|
||||
setTime(selectedDate, selectedTime);
|
||||
}
|
||||
|
||||
async changeTime(diff) {
|
||||
let {
|
||||
times,
|
||||
selectedDate,
|
||||
selectedTime,
|
||||
} = this.state;
|
||||
if (!selectedTime || times.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
setTime,
|
||||
canvasId,
|
||||
} = this.props;
|
||||
|
||||
let newPos = times.indexOf(selectedTime) + diff;
|
||||
if (newPos >= times.length || newPos < 0) {
|
||||
if (newPos < 0) {
|
||||
this.dateSelect.stepDown(1);
|
||||
} else {
|
||||
this.dateSelect.stepUp(1);
|
||||
}
|
||||
selectedDate = this.dateSelect.value;
|
||||
this.setState({
|
||||
submitting: true,
|
||||
times: [],
|
||||
selectedTime: null,
|
||||
});
|
||||
times = await getTimes(selectedDate, canvasId);
|
||||
if (times.length === 0) {
|
||||
this.setState({
|
||||
submitting: false,
|
||||
selectedDate: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
submitting: false,
|
||||
selectedDate,
|
||||
});
|
||||
newPos = (newPos < 0) ? (times.length - 1) : 0;
|
||||
}
|
||||
|
||||
selectedTime = times[newPos];
|
||||
this.setState({
|
||||
times,
|
||||
selectedTime,
|
||||
});
|
||||
setTime(selectedDate, selectedTime);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
canvasStartDate,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
submitting,
|
||||
max,
|
||||
} = this.state;
|
||||
let {
|
||||
times,
|
||||
selectedDate,
|
||||
selectedTime,
|
||||
} = this.state;
|
||||
|
||||
if (!selectedDate) {
|
||||
const {
|
||||
historicalDate,
|
||||
historicalTime,
|
||||
} = this.props;
|
||||
|
||||
if (historicalDate && historicalTime) {
|
||||
selectedDate = stringToDate(historicalDate);
|
||||
selectedTime = historicalTime;
|
||||
times = [historicalTime];
|
||||
|
||||
this.setState({
|
||||
selectedDate,
|
||||
selectedTime,
|
||||
times,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="historyselect">
|
||||
<input
|
||||
type="date"
|
||||
requiredPattern="\d{4}-\d{2}-\d{2}"
|
||||
value={selectedDate}
|
||||
min={canvasStartDate}
|
||||
max={max}
|
||||
ref={(ref) => { this.dateSelect = ref; }}
|
||||
onChange={this.handleDateChange}
|
||||
/>
|
||||
<div>
|
||||
{ (selectedTime)
|
||||
? (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="hsar"
|
||||
onClick={() => this.changeTime(-1)}
|
||||
>←</button>
|
||||
<select
|
||||
value={selectedTime}
|
||||
onChange={this.handleTimeChange}
|
||||
>
|
||||
{times.map((value) => (
|
||||
<option
|
||||
value={value}
|
||||
selected={value === selectedTime}
|
||||
>
|
||||
{value}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
className="hsar"
|
||||
onClick={() => this.changeTime(+1)}
|
||||
>→</button>
|
||||
</div>
|
||||
)
|
||||
: null }
|
||||
{ (submitting) ? <p>Loading...</p> : null }
|
||||
{ (!selectedDate && !submitting) ? <p>Select Date above</p> : null }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function getToday() {
|
||||
const date = new Date();
|
||||
let day = date.getDate();
|
||||
let month = date.getMonth() + 1;
|
||||
if (month < 10) month = `0${month}`;
|
||||
if (day < 10) day = `0${day}`;
|
||||
return `${date.getFullYear()}-${month}-${day}`;
|
||||
}
|
||||
|
||||
const HistorySelect = () => {
|
||||
const dateSelect = useRef(null);
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setTime(date: string, time: string) {
|
||||
const timeString = time.substr(0, 2) + time.substr(-2, 2);
|
||||
const dateString = dateToString(date);
|
||||
dispatch(selectHistoricalTime(dateString, timeString));
|
||||
},
|
||||
};
|
||||
}
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [times, setTimes] = useState([]);
|
||||
const [max] = useState(getToday());
|
||||
|
||||
function mapStateToProps(state: State) {
|
||||
const {
|
||||
const [
|
||||
canvasId,
|
||||
canvasStartDate,
|
||||
historicalDate,
|
||||
historicalTime,
|
||||
} = state.canvas;
|
||||
return {
|
||||
canvasId, canvasStartDate, historicalDate, historicalTime,
|
||||
};
|
||||
}
|
||||
] = useSelector((state) => [
|
||||
state.canvas.canvasId,
|
||||
state.canvas.canvasStartDate,
|
||||
state.canvas.historicalDate,
|
||||
state.canvas.historicalTime,
|
||||
], shallowEqual);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(HistorySelect);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const setTime = useCallback((date, time) => {
|
||||
const timeString = time.substr(0, 2) + time.substr(-2, 2);
|
||||
const dateString = dateToString(date);
|
||||
dispatch(selectHistoricalTime(dateString, timeString));
|
||||
}, [dispatch]);
|
||||
|
||||
const handleDateChange = useCallback(async (evt) => {
|
||||
if (submitting) {
|
||||
return;
|
||||
}
|
||||
setSubmitting(true);
|
||||
const date = evt.target.value;
|
||||
const newTimes = await requestHistoricalTimes(date, canvasId);
|
||||
if (newTimes && newTimes.length) {
|
||||
setTimes(newTimes);
|
||||
setTime(date, newTimes[0]);
|
||||
}
|
||||
setSubmitting(false);
|
||||
}, [submitting, times]);
|
||||
|
||||
const changeTime = useCallback(async (diff) => {
|
||||
if (!times.length
|
||||
|| !dateSelect || !dateSelect.current || !dateSelect.current.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newTimes = times;
|
||||
let newPos = times.indexOf(stringToTime(historicalTime)) + diff;
|
||||
let newSelectedDate = dateSelect.current.value;
|
||||
if (newPos >= times.length || newPos < 0) {
|
||||
setSubmitting(true);
|
||||
if (newPos < 0) {
|
||||
dateSelect.current.stepDown(1);
|
||||
} else {
|
||||
dateSelect.current.stepUp(1);
|
||||
}
|
||||
newSelectedDate = dateSelect.current.value;
|
||||
newTimes = await requestHistoricalTimes(
|
||||
newSelectedDate,
|
||||
canvasId,
|
||||
);
|
||||
setSubmitting(false);
|
||||
if (!newTimes || !newTimes.length) {
|
||||
return;
|
||||
}
|
||||
newPos = (newPos < 0) ? (newTimes.length - 1) : 0;
|
||||
}
|
||||
|
||||
setTimes(newTimes);
|
||||
setTime(newSelectedDate, newTimes[newPos]);
|
||||
}, [historicalTime, times, submitting]);
|
||||
|
||||
const selectedDate = stringToDate(historicalDate);
|
||||
const selectedTime = stringToTime(historicalTime);
|
||||
|
||||
return (
|
||||
<div id="historyselect">
|
||||
<input
|
||||
type="date"
|
||||
requiredPattern="\d{4}-\d{2}-\d{2}"
|
||||
value={selectedDate}
|
||||
min={canvasStartDate}
|
||||
max={max}
|
||||
ref={dateSelect}
|
||||
onChange={handleDateChange}
|
||||
/>
|
||||
<div>
|
||||
{ (!!times.length && historicalTime && !submitting)
|
||||
&& (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="hsar"
|
||||
onClick={() => changeTime(-1)}
|
||||
>←</button>
|
||||
<select
|
||||
value={selectedTime}
|
||||
onChange={(evt) => setTime(selectedDate, evt.target.value)}
|
||||
>
|
||||
{times.map((value) => (
|
||||
<option
|
||||
value={value}
|
||||
selected={value === selectedTime}
|
||||
>
|
||||
{value}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
className="hsar"
|
||||
onClick={() => changeTime(+1)}
|
||||
>→</button>
|
||||
</div>
|
||||
)}
|
||||
{ (submitting) && <p>{`${t`Loading`}...`}</p> }
|
||||
{ (!times.length && !submitting) && <p>{t`Select Date above`}</p> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(HistorySelect);
|
||||
|
|
|
@ -15,8 +15,8 @@ import Palette from './Palette';
|
|||
import Alert from './Alert';
|
||||
import HistorySelect from './HistorySelect';
|
||||
import Mobile3DControls from './Mobile3DControls';
|
||||
import UserContextMenu from './UserContextMenu';
|
||||
import ChannelContextMenu from './ChannelContextMenu';
|
||||
import UserContextMenu from './contextmenus/UserContextMenu';
|
||||
import ChannelContextMenu from './contextmenus/ChannelContextMenu';
|
||||
|
||||
|
||||
const CONTEXT_MENUS = {
|
||||
|
|
|
@ -2,170 +2,65 @@
|
|||
* Messages on top of UserArea
|
||||
* @flow
|
||||
*/
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import React, { useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { t } from 'ttag';
|
||||
|
||||
import { setMinecraftName, remFromMessages } from '../actions';
|
||||
import { requestResendVerify, requestMcLink } from '../actions/fetch';
|
||||
import { requestResendVerify } from '../actions/fetch';
|
||||
|
||||
|
||||
class UserMessages extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
resentVerify: false,
|
||||
sentLink: false,
|
||||
verifyAnswer: null,
|
||||
linkAnswer: null,
|
||||
};
|
||||
const UserMessages = () => {
|
||||
const [resentVerify, setResentVerify] = useState(false);
|
||||
const [verifyAnswer, setVerifyAnswer] = useState(null);
|
||||
|
||||
this.submitResendVerify = this.submitResendVerify.bind(this);
|
||||
this.submitMcLink = this.submitMcLink.bind(this);
|
||||
const messages = useSelector((state) => state.user.messages);
|
||||
|
||||
if (!messages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
async submitResendVerify() {
|
||||
const { resentVerify } = this.state;
|
||||
if (resentVerify) return;
|
||||
this.setState({
|
||||
resentVerify: true,
|
||||
});
|
||||
return (
|
||||
<div style={{ paddingLeft: '5%', paddingRight: '5%' }}>
|
||||
{messages.includes('not_verified')
|
||||
&& messages.splice(messages.indexOf('not_verified'), 1)
|
||||
&& (
|
||||
<p className="usermessages">
|
||||
{t`Please verify your mail address
|
||||
or your account could get deleted after a few days.`}
|
||||
{(verifyAnswer)
|
||||
? (
|
||||
<span
|
||||
className="modallink"
|
||||
>
|
||||
{verifyAnswer}
|
||||
</span>
|
||||
)
|
||||
: (
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
className="modallink"
|
||||
onClick={async () => {
|
||||
if (resentVerify) return;
|
||||
setResentVerify(true);
|
||||
const { errors } = await requestResendVerify();
|
||||
const answer = (errors)
|
||||
? errors[0]
|
||||
: t`A new verification mail is getting sent to you.`;
|
||||
setVerifyAnswer(answer);
|
||||
}}
|
||||
>
|
||||
{t`Click here to request a new verification mail.`}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{messages.map((message) => {
|
||||
if (message === 'not_verified') return null;
|
||||
return <p className="usermessages" key={message}>{message}</p>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const { errors } = await requestResendVerify();
|
||||
|
||||
const verifyAnswer = (errors)
|
||||
? errors[0]
|
||||
: t`A new verification mail is getting sent to you.`;
|
||||
this.setState({
|
||||
verifyAnswer,
|
||||
});
|
||||
}
|
||||
|
||||
async submitMcLink(accepted) {
|
||||
const { sentLink } = this.state;
|
||||
if (sentLink) return;
|
||||
this.setState({
|
||||
sentLink: true,
|
||||
});
|
||||
|
||||
const { errors } = await requestMcLink(accepted);
|
||||
|
||||
if (errors) {
|
||||
this.setState({
|
||||
linkAnswer: errors[0],
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { setMCName, remFromUserMessages } = this.props;
|
||||
if (!accepted) {
|
||||
setMCName(null);
|
||||
}
|
||||
remFromUserMessages('not_mc_verified');
|
||||
this.setState({
|
||||
linkAnswer: (accepted)
|
||||
? t`You successfully linked your mc account.`
|
||||
: t`You denied.`,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { messages: messagesr } = this.props;
|
||||
if (!messagesr) return null;
|
||||
// state variable is not allowed to be changed, make copy
|
||||
const messages = [...messagesr];
|
||||
const { verifyAnswer, linkAnswer } = this.state;
|
||||
const { minecraftname } = this.props;
|
||||
|
||||
return (
|
||||
<div style={{ paddingLeft: '5%', paddingRight: '5%' }}>
|
||||
{(messages.includes('not_verified')
|
||||
&& messages.splice(messages.indexOf('not_verified'), 1))
|
||||
? (
|
||||
<p className="usermessages">
|
||||
{t`Please verify your mail address
|
||||
or your account could get deleted after a few days.`}
|
||||
{(verifyAnswer)
|
||||
? (
|
||||
<span
|
||||
className="modallink"
|
||||
>
|
||||
{verifyAnswer}
|
||||
</span>
|
||||
)
|
||||
: (
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
className="modallink"
|
||||
onClick={this.submitResendVerify}
|
||||
>
|
||||
{t`Click here to request a new verification mail.`}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
{(messages.includes('not_mc_verified')
|
||||
&& messages.splice(messages.indexOf('not_mc_verified'), 1))
|
||||
? (
|
||||
<p className="usermessages">
|
||||
{t`You requested to link your mc account ${minecraftname}.`}
|
||||
|
||||
{(linkAnswer)
|
||||
? (
|
||||
<span
|
||||
className="modallink"
|
||||
>
|
||||
{linkAnswer}
|
||||
</span>
|
||||
)
|
||||
: (
|
||||
<span>
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
className="modallink"
|
||||
onClick={() => {
|
||||
this.submitMcLink(true);
|
||||
}}
|
||||
>
|
||||
{t`Accept`}
|
||||
</span> or
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
className="modallink"
|
||||
onClick={() => {
|
||||
this.submitMcLink(false);
|
||||
}}
|
||||
>
|
||||
{t`Deny`}
|
||||
</span>.
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
{messages.map((message) => (
|
||||
<p className="usermessages" key={message}>{message}</p>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setMCName(minecraftname) {
|
||||
dispatch(setMinecraftName(minecraftname));
|
||||
},
|
||||
remFromUserMessages(message) {
|
||||
dispatch(remFromMessages(message));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function mapStateToProps(state: State) {
|
||||
const { messages, minecraftname } = state.user;
|
||||
return { messages, minecraftname };
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserMessages);
|
||||
export default React.memo(UserMessages);
|
||||
|
|
|
@ -14,8 +14,8 @@ import {
|
|||
setLeaveChannel,
|
||||
muteChatChannel,
|
||||
unmuteChatChannel,
|
||||
} from '../actions';
|
||||
import type { State } from '../reducers';
|
||||
} from '../../actions';
|
||||
import type { State } from '../../reducers';
|
||||
|
||||
const ChannelContextMenu = ({
|
||||
xPos,
|
|
@ -15,7 +15,7 @@ import {
|
|||
startDm,
|
||||
setUserBlock,
|
||||
setChatChannel,
|
||||
} from '../actions';
|
||||
} from '../../actions';
|
||||
|
||||
const UserContextMenu = () => {
|
||||
const wrapperRef = useRef(null);
|
|
@ -11,7 +11,7 @@ import { useSelector, useDispatch } from 'react-redux';
|
|||
import { t } from 'ttag';
|
||||
|
||||
import ChatMessage from '../ChatMessage';
|
||||
import ChannelDropDown from '../ChannelDropDown';
|
||||
import ChannelDropDown from '../contextmenus/ChannelDropDown';
|
||||
|
||||
import {
|
||||
showUserAreaModal,
|
||||
|
|
|
@ -38,6 +38,14 @@ export function clamp(n: number, min: number, max: number): number {
|
|||
return Math.max(min, Math.min(n, max));
|
||||
}
|
||||
|
||||
/*
|
||||
* convert YYYY-MM-DD to YYYYMMDD
|
||||
*/
|
||||
export function dateToString(date: string) {
|
||||
// YYYY-MM-DD
|
||||
return date.substr(0, 4) + date.substr(5, 2) + date.substr(8, 2);
|
||||
}
|
||||
|
||||
// z is assumed to be height here
|
||||
// in ui and rendeer, y is height
|
||||
export function getChunkOfPixel(
|
||||
|
|
|
@ -501,7 +501,7 @@ export default function windows(
|
|||
const yMax = height - SCREEN_MARGIN_S;
|
||||
let modified = false;
|
||||
|
||||
const newWindows = [];
|
||||
let newWindows = [];
|
||||
for (let i = 0; i < state.windows.length; i += 1) {
|
||||
const win = state.windows[i];
|
||||
const {
|
||||
|
@ -525,6 +525,26 @@ export default function windows(
|
|||
}
|
||||
}
|
||||
|
||||
if (action.type === 'RECEIVE_ME') {
|
||||
const args = { ...state.args };
|
||||
newWindows = newWindows.filter((win) => {
|
||||
if (win.open) return true;
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`Cleaning up window from previous session: ${win.windowId}`,
|
||||
);
|
||||
delete args[win.windowId];
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
showWindows: true,
|
||||
windows: newWindows,
|
||||
args,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
showWindows: true,
|
||||
|
|
Loading…
Reference in New Issue