From 26ed411129a43f675fd59e45c0dfc2a8d763d638 Mon Sep 17 00:00:00 2001
From: HF
Date: Tue, 24 Nov 2020 16:44:33 +0100
Subject: [PATCH] add blocking of Users
---
src/actions/fetch.js | 120 +++++++++++++++++++++++
src/actions/index.js | 132 ++++++++++++++++++++------
src/actions/types.js | 7 +-
src/components/Chat.jsx | 147 ++++++++++++++++-------------
src/components/ChatModal.jsx | 9 +-
src/components/MdToggleButton.jsx | 2 +-
src/components/SocialSettings.jsx | 116 +++++++++++++++++++++++
src/components/UserArea.jsx | 26 +++++
src/components/UserContextMenu.jsx | 10 +-
src/data/models/RegUser.js | 6 ++
src/data/models/User.js | 1 +
src/reducers/canvas.js | 31 ------
src/reducers/chat.js | 39 +++++---
src/reducers/fetching.js | 74 +++++++++++++++
src/reducers/index.js | 5 +
src/reducers/user.js | 13 +++
src/routes/api/block.js | 61 ++++++++++--
src/routes/api/blockdm.js | 46 +++++++++
src/routes/api/index.js | 3 +
src/routes/api/startdm.js | 7 ++
src/styles/default.css | 23 +++++
21 files changed, 721 insertions(+), 157 deletions(-)
create mode 100644 src/actions/fetch.js
create mode 100644 src/components/SocialSettings.jsx
create mode 100644 src/reducers/fetching.js
create mode 100644 src/routes/api/blockdm.js
diff --git a/src/actions/fetch.js b/src/actions/fetch.js
new file mode 100644
index 0000000..7afdcc7
--- /dev/null
+++ b/src/actions/fetch.js
@@ -0,0 +1,120 @@
+/*
+ * Collect api fetch commands for actions here
+ * (chunk and tiles requests in ui/ChunkLoader*.js)
+ * (user settings requests in their components)
+ *
+ * @flow
+ */
+
+
+/*
+ * Adds customizeable timeout to fetch
+ * defaults to 8s
+ */
+async function fetchWithTimeout(resource, options) {
+ const { timeout = 8000 } = options;
+
+ const controller = new AbortController();
+ const id = setTimeout(() => controller.abort(), timeout);
+
+ const response = await fetch(resource, {
+ ...options,
+ signal: controller.signal,
+ });
+ clearTimeout(id);
+
+ return response;
+}
+
+/*
+ * block / unblock user
+ * userId id of user to block
+ * block true if block, false if unblock
+ * return error string or null if successful
+ */
+export async function requestBlock(userId: number, block: boolean) {
+ const response = await fetchWithTimeout('api/block', {
+ method: 'POST',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ userId,
+ block,
+ }),
+ });
+
+ try {
+ const res = await response.json();
+ if (res.errors) {
+ return res.errors[0];
+ }
+ if (response.ok && res.status === 'ok') {
+ return null;
+ }
+ return 'Unknown Error';
+ } catch {
+ return 'Connection Error';
+ }
+}
+
+/*
+ * start new DM channel with user
+ * query Object with either userId: number or userName: string
+ * return channel Array on success, error string if not
+ */
+export async function requestStartDm(query) {
+ const response = await fetchWithTimeout('api/startdm', {
+ method: 'POST',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(query),
+ });
+
+ try {
+ const res = await response.json();
+ if (res.errors) {
+ return res.errors[0];
+ }
+ if (response.ok && res.channel) {
+ const { channel } = res;
+ return channel;
+ }
+
+ return 'Unknown Error';
+ } catch {
+ return 'Connection Error';
+ }
+}
+
+/*
+ * set receiving of all DMs on/off
+ * block true if blocking all dms, false if unblocking
+ * return error string or null if successful
+ */
+export async function requestBlockDm(block: boolean) {
+ const response = await fetchWithTimeout('api/blockdm', {
+ method: 'POST',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ block }),
+ });
+
+ try {
+ const res = await response.json();
+ if (res.errors) {
+ return res.errors[0];
+ }
+ if (response.ok && res.status === 'ok') {
+ return null;
+ }
+ return 'Unknown Error';
+ } catch {
+ return 'Connection Error';
+ }
+}
diff --git a/src/actions/index.js b/src/actions/index.js
index 67d7928..6022355 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -7,6 +7,11 @@ import type {
} from './types';
import type { Cell } from '../core/Cell';
import type { ColorIndex } from '../core/Palette';
+import {
+ requestStartDm,
+ requestBlock,
+ requestBlockDm,
+} from './fetch';
export function sweetAlert(
title: string,
@@ -490,8 +495,10 @@ export function receiveMe(
ranking,
dailyRanking,
minecraftname,
+ blockDm,
canvases,
channels,
+ blocked,
userlvl,
} = me;
return {
@@ -504,8 +511,10 @@ export function receiveMe(
ranking,
dailyRanking,
minecraftname,
+ blockDm: !!blockDm,
canvases,
channels,
+ blocked,
userlvl,
};
}
@@ -599,6 +608,13 @@ function setChatFetching(fetching: boolean): Action {
};
}
+function setApiFetching(fetching: boolean): Action {
+ return {
+ type: 'SET_API_FETCHING',
+ fetching,
+ };
+}
+
export function fetchChatMessages(
cid: number,
): PromiseAction {
@@ -608,6 +624,9 @@ export function fetchChatMessages(
credentials: 'include',
});
+ /*
+ * timeout in order to not spam api requests and get rate limited
+ */
if (response.ok) {
setTimeout(() => { dispatch(setChatFetching(false)); }, 500);
const { history } = await response.json();
@@ -755,38 +774,95 @@ export function addChatChannel(channel: Array): Action {
};
}
+export function blockUser(userId: number, userName: string): Action {
+ return {
+ type: 'BLOCK_USER',
+ userId,
+ userName,
+ };
+}
+
+export function unblockUser(userId: number, userName: string): Action {
+ return {
+ type: 'UNBLOCK_USER',
+ userId,
+ userName,
+ };
+}
+
+export function blockingDm(blockDm: boolean): Action {
+ return {
+ type: 'SET_BLOCKING_DM',
+ blockDm,
+ };
+}
+
+/*
+ * query: Object with either userId: number or userName: string
+ */
export function startDm(query): PromiseAction {
return async (dispatch) => {
- const response = await fetch('api/startdm', {
- method: 'POST',
- credentials: 'include',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(query),
- });
-
- try {
- const res = await response.json();
- if (res.errors) {
- dispatch(sweetAlert(
- 'Direct Message Error',
- res.errors[0],
- 'error',
- 'OK',
- ));
+ dispatch(setApiFetching(true));
+ const res = await requestStartDm(query);
+ if (typeof res === 'string') {
+ dispatch(sweetAlert(
+ 'Direct Message Error',
+ res,
+ 'error',
+ 'OK',
+ ));
+ } else {
+ const channelId = res[0];
+ if (channelId) {
+ dispatch(addChatChannel(res));
+ dispatch(setChatChannel(channelId));
}
- if (response.ok) {
- const { channel } = res;
- const channelId = channel[0];
- if (channelId) {
- await dispatch(addChatChannel(channel));
- dispatch(setChatChannel(channelId));
- }
- }
- } catch {
- dispatch(notify('Couldn\'t start DM'));
}
+ dispatch(setApiFetching(false));
+ };
+}
+
+export function setUserBlock(
+ userId: number,
+ userName: string,
+ block: boolean,
+) {
+ return async (dispatch) => {
+ dispatch(setApiFetching(true));
+ const res = await requestBlock(userId, block);
+ if (res) {
+ dispatch(sweetAlert(
+ 'User Block Error',
+ res,
+ 'error',
+ 'OK',
+ ));
+ } else if (block) {
+ dispatch(blockUser(userId, userName));
+ } else {
+ dispatch(unblockUser(userId, userName));
+ }
+ dispatch(setApiFetching(false));
+ };
+}
+
+export function setBlockingDm(
+ block: boolean,
+) {
+ return async (dispatch) => {
+ dispatch(setApiFetching(true));
+ const res = await requestBlockDm(block);
+ if (res) {
+ dispatch(sweetAlert(
+ 'Blocking DMs Error',
+ res,
+ 'error',
+ 'OK',
+ ));
+ } else {
+ dispatch(blockingDm(block));
+ }
+ dispatch(setApiFetching(false));
};
}
diff --git a/src/actions/types.js b/src/actions/types.js
index 5a086d7..982f819 100644
--- a/src/actions/types.js
+++ b/src/actions/types.js
@@ -74,6 +74,9 @@ export type Action =
| { type: 'SET_CHAT_FETCHING', fetching: boolean }
| { type: 'SET_CHAT_INPUT_MSG', message: string }
| { type: 'ADD_CHAT_INPUT_MSG', message: string }
+ | { type: 'BLOCK_USER', userId: number, userName: string }
+ | { type: 'UNBLOCK_USER', userId: number, userName: string }
+ | { type: 'SET_BLOCKING_DM', blockDm: boolean }
| { type: 'RECEIVE_ME',
name: string,
waitSeconds: number,
@@ -84,8 +87,10 @@ export type Action =
ranking: number,
dailyRanking: number,
minecraftname: string,
+ blockDm: boolean,
canvases: Object,
- channels: Object,
+ channels: Array,
+ blocked: Array,
userlvl: number,
}
| { type: 'RECEIVE_STATS', totalRanking: Object, totalDailyRanking: Object }
diff --git a/src/components/Chat.jsx b/src/components/Chat.jsx
index 16cf684..57eea03 100644
--- a/src/components/Chat.jsx
+++ b/src/components/Chat.jsx
@@ -39,10 +39,12 @@ const Chat = ({
setChannel,
fetchMessages,
fetching,
+ blocked,
}) => {
const listRef = useRef();
const [selection, setSelection] = useState(null);
const [nameRegExp, setNameRegExp] = useState(null);
+ const [blockedIds, setBlockedIds] = useState([]);
const { stayScrolled } = useStayScrolled(listRef, {
initialScroll: Infinity,
@@ -75,6 +77,14 @@ const Chat = ({
setNameRegExp(regExp);
}, [ownName]);
+ useEffect(() => {
+ const bl = [];
+ for (let i = 0; i < blocked.length; i += 1) {
+ bl.push(blocked[i][0]);
+ }
+ setBlockedIds(bl);
+ }, [blocked.length]);
+
function handleSubmit(e) {
e.preventDefault();
const msg = inputMessage.trim();
@@ -102,71 +112,74 @@ const Chat = ({
}
return (
-
-
{ setSelection(saveSelection); }}
- role="presentation"
- >
- {
- (!channelMessages.length)
- && (
-
- )
- }
- {
- channelMessages.map((message) => (
-
- ))
- }
-
- {(ownName) ? (
-
-
-
- ) : (
-
+
+
{ setSelection(saveSelection); }}
+ role="presentation"
>
- You must be logged in to chat
-
- )}
+ {
+ (!channelMessages.length)
+ && (
+
+ )
+ }
+ {
+ channelMessages.map((message) => ((blockedIds.includes(message[3]))
+ ? null : (
+
+ )))
+ }
+
+ {(ownName) ? (
+
+
+
+ ) : (
+
+ You must be logged in to chat
+
+ )}
+
);
};
@@ -177,13 +190,17 @@ function mapStateToProps(state: State) {
const {
channels,
messages,
- fetching,
inputMessage,
+ blocked,
} = state.chat;
+ const {
+ fetchingChat: fetching,
+ } = state.fetching;
return {
channels,
messages,
fetching,
+ blocked,
inputMessage,
chatChannel,
ownName: name,
diff --git a/src/components/ChatModal.jsx b/src/components/ChatModal.jsx
index c0c955a..12d1e05 100644
--- a/src/components/ChatModal.jsx
+++ b/src/components/ChatModal.jsx
@@ -18,17 +18,10 @@ const ChatModal = () => (
right: 10,
}}
>
-
-
Chat with other people here
-
diff --git a/src/components/MdToggleButton.jsx b/src/components/MdToggleButton.jsx
index 6fa2e5c..4971fe2 100644
--- a/src/components/MdToggleButton.jsx
+++ b/src/components/MdToggleButton.jsx
@@ -10,10 +10,10 @@ const MdToggleButton = ({ value, onToggle }) => (
}
activeLabel={
}
- thumbAnimateRange={[-10, 36]}
value={value}
onToggle={onToggle}
/>
);
+// thumbAnimateRange={[-10, 36]}
export default MdToggleButton;
diff --git a/src/components/SocialSettings.jsx b/src/components/SocialSettings.jsx
new file mode 100644
index 0000000..4d64dec
--- /dev/null
+++ b/src/components/SocialSettings.jsx
@@ -0,0 +1,116 @@
+/*
+ * Change Mail Form
+ * @flow
+ */
+
+import React from 'react';
+import { connect } from 'react-redux';
+
+import {
+ setBlockingDm,
+ setUserBlock,
+} from '../actions';
+import MdToggleButtonHover from './MdToggleButtonHover';
+
+const SocialSettings = ({
+ blocked,
+ fetching,
+ blockDm,
+ setBlockDm,
+ unblock,
+ done,
+}) => (
+
+
+
+ Block all Private Messages
+
+ {
+ if (!fetching) {
+ setBlockDm(!blockDm);
+ }
+ }}
+ />
+
+
+
Unblock Users
+ {
+ (blocked.length) ? (
+
+ {
+ blocked.map((bl) => (
+ {
+ if (!fetching) {
+ unblock(bl[0], bl[1]);
+ }
+ }}
+ >
+ {bl[1]}
+
+ ))
+ }
+
+ )
+ : (
+
You have no users blocked
+ )
+ }
+
+
+
+);
+
+function mapStateToProps(state: State) {
+ const { blocked } = state.chat;
+ const { blockDm } = state.user;
+ const { fetchingApi: fetching } = state.fetching;
+ return {
+ blocked,
+ blockDm,
+ fetching,
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ setBlockDm(block) {
+ dispatch(setBlockingDm(block));
+ },
+ unblock(userId, userName) {
+ dispatch(setUserBlock(userId, userName, false));
+ },
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(SocialSettings);
diff --git a/src/components/UserArea.jsx b/src/components/UserArea.jsx
index 26c9f9f..30f11d9 100644
--- a/src/components/UserArea.jsx
+++ b/src/components/UserArea.jsx
@@ -13,6 +13,7 @@ import ChangePassword from './ChangePassword';
import ChangeName from './ChangeName';
import ChangeMail from './ChangeMail';
import DeleteAccount from './DeleteAccount';
+import SocialSettings from './SocialSettings';
import { numberToString } from '../core/utils';
@@ -45,6 +46,7 @@ class UserArea extends React.Component {
changeMailExtended,
changePasswdExtended,
deleteAccountExtended,
+ socialSettingsExtended,
} = this.state;
return (
@@ -84,6 +86,7 @@ class UserArea extends React.Component {
changeMailExtended: false,
changePasswdExtended: false,
deleteAccountExtended: false,
+ socialSettingsExtended: false,
})}
> Change Username |
{(mailreg)
@@ -98,6 +101,7 @@ class UserArea extends React.Component {
changeMailExtended: true,
changePasswdExtended: false,
deleteAccountExtended: false,
+ socialSettingsExtended: false,
})}
> Change Mail |
@@ -111,6 +115,7 @@ class UserArea extends React.Component {
changeMailExtended: false,
changePasswdExtended: true,
deleteAccountExtended: false,
+ socialSettingsExtended: false,
})}
> Change Password |
Delete Account )
+
(
+ this.setState({
+ changeNameExtended: false,
+ changeMailExtended: false,
+ changePasswdExtended: false,
+ deleteAccountExtended: false,
+ socialSettingsExtended: true,
+ })}
+ > Social Settings )
+
{(changePasswdExtended)
&& (
{ this.setState({ deleteAccountExtended: false }); }}
/>
)}
+ {(socialSettingsExtended)
+ && (
+ { this.setState({ socialSettingsExtended: false }); }}
+ />
+ )}
{(typeof window.hcaptcha !== 'undefined')
&& (
{
const wrapperRef = useRef(null);
@@ -53,6 +54,10 @@ const UserContextMenu = ({
>
{
+ block(uid, name);
+ close();
+ }}
>
Block
@@ -113,6 +118,9 @@ function mapDispatchToProps(dispatch) {
dm(userId) {
dispatch(startDm({ userId }));
},
+ block(userId, userName) {
+ dispatch(setUserBlock(userId, userName, true));
+ },
close() {
dispatch(hideContextMenu());
},
diff --git a/src/data/models/RegUser.js b/src/data/models/RegUser.js
index 4d94786..bd745aa 100644
--- a/src/data/models/RegUser.js
+++ b/src/data/models/RegUser.js
@@ -64,6 +64,12 @@ const RegUser = Model.define('User', {
defaultValue: false,
},
+ blockDm: {
+ type: DataType.BOOLEAN,
+ allowNull: false,
+ defaultValue: false,
+ },
+
discordid: {
type: DataType.CHAR(18),
allowNull: true,
diff --git a/src/data/models/User.js b/src/data/models/User.js
index 07d96dc..8531f8d 100644
--- a/src/data/models/User.js
+++ b/src/data/models/User.js
@@ -197,6 +197,7 @@ class User {
mailVerified: regUser.mailVerified,
mcVerified: regUser.mcVerified,
minecraftname: regUser.minecraftname,
+ blockDm: regUser.blockDm,
totalPixels: regUser.totalPixels,
dailyTotalPixels: regUser.dailyTotalPixels,
ranking: regUser.ranking,
diff --git a/src/reducers/canvas.js b/src/reducers/canvas.js
index db8f6ad..7838c7c 100644
--- a/src/reducers/canvas.js
+++ b/src/reducers/canvas.js
@@ -32,7 +32,6 @@ export type CanvasState = {
view: Cell,
scale: number,
viewscale: number,
- fetchs: number,
isHistoricalView: boolean,
historicalDate: string,
historicalTime: string,
@@ -137,7 +136,6 @@ function getViewFromURL(canvases: Object) {
const initialState: CanvasState = {
...getViewFromURL(DEFAULT_CANVASES),
- fetchs: 0,
isHistoricalView: false,
historicalDate: null,
historicalTime: null,
@@ -242,35 +240,6 @@ export default function canvasReducer(
};
}
- case 'REQUEST_BIG_CHUNK': {
- const {
- fetchs,
- } = state;
-
- return {
- ...state,
- fetchs: fetchs + 1,
- };
- }
-
- case 'RECEIVE_BIG_CHUNK': {
- const { fetchs } = state;
-
- return {
- ...state,
- fetchs: fetchs + 1,
- };
- }
-
- case 'RECEIVE_BIG_CHUNK_FAILURE': {
- const { fetchs } = state;
-
- return {
- ...state,
- fetchs: fetchs + 1,
- };
- }
-
case 'SELECT_COLOR': {
return {
...state,
diff --git a/src/reducers/chat.js b/src/reducers/chat.js
index 707eb57..8fe3230 100644
--- a/src/reducers/chat.js
+++ b/src/reducers/chat.js
@@ -6,19 +6,19 @@ import type { Action } from '../actions/types';
export type ChatState = {
inputMessage: string,
- // [[cid, name], [cid2, name2],...]
+ // [[cid, name, type, lastMessage], [cid2, name2, type2, lastMessage2],...]
channels: Array,
- // { cid: [message1,message2,message2,...]}
+ // [[userId, userName], [userId2, userName2],...]
+ blocked: Array,
+ // { cid: [message1,message2,message3,...]}
messages: Object,
- // if currently fetching messages
- fetching: boolean,
}
const initialState: ChatState = {
inputMessage: '',
channels: [],
+ blocked: [],
messages: {},
- fetching: false,
};
export default function chat(
@@ -30,6 +30,27 @@ export default function chat(
return {
...state,
channels: action.channels,
+ blocked: action.blocked,
+ };
+ }
+
+ case 'BLOCK_USER': {
+ const { userId, userName } = action;
+ return {
+ ...state,
+ blocked: [
+ ...state.blocked,
+ [userId, userName],
+ ],
+ };
+ }
+
+ case 'UNBLOCK_USER': {
+ const { userId } = action;
+ const blocked = state.blocked.filter((bl) => (bl[0] !== userId));
+ return {
+ ...state,
+ blocked,
};
}
@@ -45,14 +66,6 @@ export default function chat(
};
}
- case 'SET_CHAT_FETCHING': {
- const { fetching } = action;
- return {
- ...state,
- fetching,
- };
- }
-
case 'SET_CHAT_INPUT_MSG': {
const { message } = action;
return {
diff --git a/src/reducers/fetching.js b/src/reducers/fetching.js
new file mode 100644
index 0000000..2d6d798
--- /dev/null
+++ b/src/reducers/fetching.js
@@ -0,0 +1,74 @@
+/*
+ * keeps track of some api fetching states
+ *
+ * @flow
+ */
+
+import type { Action } from '../actions/types';
+
+export type FetchingState = {
+ fetchingChunks: number,
+ fetchingChat: boolean,
+ fetchinApi: boolean,
+}
+
+const initialState: FetchingState = {
+ fetchingChunks: 0,
+ fetchingChat: false,
+ fetchinApi: false,
+};
+
+export default function fetching(
+ state: FetchingState = initialState,
+ action: Action,
+): FetchingState {
+ switch (action.type) {
+ case 'SET_CHAT_FETCHING': {
+ const { fetching: fetchingChat } = action;
+ return {
+ ...state,
+ fetchingChat,
+ };
+ }
+
+ case 'SET_API_FETCHING': {
+ const { fetching: fetchinApi } = action;
+ return {
+ ...state,
+ fetchinApi,
+ };
+ }
+
+ case 'REQUEST_BIG_CHUNK': {
+ const {
+ fetchingChunks,
+ } = state;
+
+ return {
+ ...state,
+ fetchingChunks: fetchingChunks + 1,
+ };
+ }
+
+ case 'RECEIVE_BIG_CHUNK': {
+ const { fetchingChunks } = state;
+
+ return {
+ ...state,
+ fetchingChunks: fetchingChunks - 1,
+ };
+ }
+
+ case 'RECEIVE_BIG_CHUNK_FAILURE': {
+ const { fetchingChunks } = state;
+
+ return {
+ ...state,
+ fetchingChunks: fetchingChunks - 1,
+ };
+ }
+
+ default:
+ return state;
+ }
+}
diff --git a/src/reducers/index.js b/src/reducers/index.js
index a7812be..ff068f3 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -9,6 +9,7 @@ import modal from './modal';
import user from './user';
import chat from './chat';
import contextMenu from './contextMenu';
+import fetching from './fetching';
import type { AudioState } from './audio';
import type { CanvasState } from './canvas';
@@ -17,6 +18,7 @@ import type { ModalState } from './modal';
import type { UserState } from './user';
import type { ChatState } from './chat';
import type { ContextMenuState } from './contextMenu';
+import type { FetchingState } from './fetching';
export type State = {
audio: AudioState,
@@ -26,6 +28,7 @@ export type State = {
user: UserState,
chat: ChatState,
contextMenu: ContextMenuState,
+ fetching: FetchingState,
};
const config = {
@@ -37,6 +40,7 @@ const config = {
'modal',
'chat',
'contextMenu',
+ 'fetching',
],
};
@@ -48,4 +52,5 @@ export default persistCombineReducers(config, {
user,
chat,
contextMenu,
+ fetching,
});
diff --git a/src/reducers/user.js b/src/reducers/user.js
index e662fc9..64fdf66 100644
--- a/src/reducers/user.js
+++ b/src/reducers/user.js
@@ -24,6 +24,8 @@ export type UserState = {
totalDailyRanking: Object,
// minecraft
minecraftname: string,
+ // blocking all Dms
+ blockDm: boolean,
// if user is using touchscreen
isOnMobile: boolean,
// small notifications for received cooldown
@@ -45,6 +47,7 @@ const initialState: UserState = {
totalRanking: {},
totalDailyRanking: {},
minecraftname: null,
+ blockDm: false,
isOnMobile: false,
notification: null,
userlvl: 0,
@@ -139,6 +142,7 @@ export default function user(
ranking,
dailyRanking,
minecraftname,
+ blockDm,
userlvl,
} = action;
const messages = (action.messages) ? action.messages : [];
@@ -152,6 +156,7 @@ export default function user(
ranking,
dailyRanking,
minecraftname,
+ blockDm,
userlvl,
};
}
@@ -173,6 +178,14 @@ export default function user(
};
}
+ case 'SET_BLOCKING_DM': {
+ const { blockDm } = action;
+ return {
+ ...state,
+ blockDm,
+ };
+ }
+
case 'SET_MINECRAFT_NAME': {
const { minecraftname } = action;
return {
diff --git a/src/routes/api/block.js b/src/routes/api/block.js
index d84fc2e..f5f1659 100644
--- a/src/routes/api/block.js
+++ b/src/routes/api/block.js
@@ -8,11 +8,12 @@
import type { Request, Response } from 'express';
import logger from '../../core/logger';
-import { RegUser, UserBlock } from '../../data/models';
+import { RegUser, UserBlock, Channel } from '../../data/models';
async function block(req: Request, res: Response) {
let userId = parseInt(req.body.userId, 10);
let { userName } = req.body;
+ const { block: blocking } = req.body;
const { user } = req;
const errors = [];
@@ -23,6 +24,9 @@ async function block(req: Request, res: Response) {
}
query.id = userId;
}
+ if (typeof blocking !== 'boolean') {
+ errors.push('Not defined if blocking or unblocking');
+ }
if (userName) {
query.name = userName;
}
@@ -33,7 +37,7 @@ async function block(req: Request, res: Response) {
errors.push('You are not logged in');
}
if (user && userId && user.id === userId) {
- errors.push('You can not DM yourself.');
+ errors.push('You can not block yourself.');
}
if (errors.length) {
res.status(400);
@@ -61,14 +65,53 @@ async function block(req: Request, res: Response) {
userId = targetUser.id;
userName = targetUser.name;
- const ret = await UserBlock.findOrCreate({
+ let ret = null;
+ if (blocking) {
+ ret = await UserBlock.findOrCreate({
+ where: {
+ uid: user.id,
+ buid: userId,
+ },
+ raw: true,
+ attributes: ['uid'],
+ });
+ } else {
+ ret = await UserBlock.destroy({
+ where: {
+ uid: user.id,
+ buid: userId,
+ },
+ });
+ }
+
+ /*
+ * delete possible dm channel
+ */
+ let dmu1id = null;
+ let dmu2id = null;
+ if (user.id > userId) {
+ dmu1id = userId;
+ dmu2id = user.id;
+ } else {
+ dmu1id = user.id;
+ dmu2id = userId;
+ }
+
+ // TODO test if this removes association too
+ const channel = await Channel.findOne({
where: {
- uid: user.id,
- buid: userId,
+ type: 1,
+ dmu1id,
+ dmu2id,
},
- raw: true,
- attributes: ['uid'],
});
+ if (channel) {
+ const channelId = channel.id;
+ channel.destroy();
+ }
+
+ // TODO notify websocket
+
if (ret) {
res.json({
status: 'ok',
@@ -76,10 +119,10 @@ async function block(req: Request, res: Response) {
} else {
res.status(502);
res.json({
- errors: ['Could not block user'],
+ errors: ['Could not (un)block user'],
});
logger.info(
- `User ${user.getName()} blocked ${userName}`,
+ `User ${user.getName()} (un)blocked ${userName}`,
);
}
}
diff --git a/src/routes/api/blockdm.js b/src/routes/api/blockdm.js
new file mode 100644
index 0000000..f135eaa
--- /dev/null
+++ b/src/routes/api/blockdm.js
@@ -0,0 +1,46 @@
+/*
+ *
+ * block all private messages
+ *
+ * @flow
+ */
+
+import type { Request, Response } from 'express';
+
+import logger from '../../core/logger';
+
+async function blockdm(req: Request, res: Response) {
+ const { block } = req.body;
+ const { user } = req;
+
+ const errors = [];
+ if (typeof block !== 'boolean') {
+ errors.push('Not defined if blocking or unblocking');
+ }
+ if (!user || !user.regUser) {
+ errors.push('You are not logged in');
+ }
+ if (errors.length) {
+ res.status(400);
+ res.json({
+ errors,
+ });
+ return;
+ }
+
+ logger.info(
+ `User ${user.getName()} (un)blocked all dms`,
+ );
+
+ await user.regUser.update({
+ blockDm: block,
+ });
+
+ // TODO notify websocket
+
+ res.json({
+ status: 'ok',
+ });
+}
+
+export default blockdm;
diff --git a/src/routes/api/index.js b/src/routes/api/index.js
index 6030b6c..42fa401 100644
--- a/src/routes/api/index.js
+++ b/src/routes/api/index.js
@@ -20,6 +20,7 @@ import history from './history';
import chatHistory from './chathistory';
import startDm from './startdm';
import block from './block';
+import blockdm from './blockdm';
const router = express.Router();
@@ -86,6 +87,8 @@ router.post('/startdm', startDm);
router.post('/block', block);
+router.post('/blockdm', blockdm);
+
router.use('/auth', auth(passport));
export default router;
diff --git a/src/routes/api/startdm.js b/src/routes/api/startdm.js
index 5f69bec..67160b4 100644
--- a/src/routes/api/startdm.js
+++ b/src/routes/api/startdm.js
@@ -49,6 +49,7 @@ async function startDm(req: Request, res: Response) {
attributes: [
'id',
'name',
+ 'blockDm',
],
raw: true,
});
@@ -59,6 +60,12 @@ async function startDm(req: Request, res: Response) {
});
return;
}
+ if (blockDm) {
+ res.status(401);
+ res.json({
+ errors: ['Target user doesn\'t allo DMs'],
+ });
+ }
userId = targetUser.id;
userName = targetUser.name;
diff --git a/src/styles/default.css b/src/styles/default.css
index 95967c8..0ff8afb 100644
--- a/src/styles/default.css
+++ b/src/styles/default.css
@@ -74,6 +74,8 @@ a:hover {
padding: 4px;
margin-top: 4px;
border-width: 1px;
+ margin-left: 10px;
+ margin-right: 10px;
}
.tab-list {
@@ -552,6 +554,27 @@ tr:nth-child(even) {
opacity: 0;
}
+.unblocklist {
+ display: inline-block;
+ margin: 5px 0px 15px 0px;
+ width: 95%;
+ max-height: 250px;
+ overflow-y: auto;
+ max-width: 340px;
+ border: thin solid;
+}
+
+.unblocklist > div {
+ padding: 4px;
+ font-weight: bold;
+}
+
+.unblocklist > div:hover {
+ cursor: pointer;
+ font-weight: bold;
+ background-color: #db0000;
+}
+
.actionbuttons {
vertical-align: text-bottom;
cursor: pointer;