Skip to content

Commit 0b79d74

Browse files
authored
Allow banning for specified duration (#170)
1 parent c0cf882 commit 0b79d74

File tree

3 files changed

+45
-17
lines changed

3 files changed

+45
-17
lines changed

src/components/ProfileName.js

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import MoreVertIcon from "@material-ui/icons/MoreVert";
99

1010
import { UserContext } from "../context";
1111
import firebase from "../firebase";
12-
import { generateColor, generateName } from "../util";
12+
import { generateColor, generateName, parseDuration } from "../util";
1313
import ElapsedTime from "./ElapsedTime";
14+
import PromptDialog from "./PromptDialog";
1415
import User from "./User";
1516

1617
const useStyles = makeStyles((theme) => ({
@@ -35,8 +36,8 @@ function ProfileName({ userId }) {
3536
const theme = useTheme();
3637
const user = useContext(UserContext);
3738
const classes = useStyles();
38-
3939
const [anchorEl, setAnchorEl] = useState(null);
40+
const [banUser, setBanUser] = useState(false);
4041

4142
const handleClickVertIcon = (event) => {
4243
setAnchorEl(event.currentTarget);
@@ -52,9 +53,13 @@ function ProfileName({ userId }) {
5253
handleClose();
5354
};
5455

55-
const handleBan = (minutes) => {
56-
const endTime = Date.now() + minutes * 60000;
57-
firebase.database().ref(`users/${userId}/banned`).set(endTime);
56+
const handleBan = (duration) => {
57+
setBanUser(false);
58+
const seconds = parseDuration((duration || "").trim());
59+
if (seconds) {
60+
const endTime = Date.now() + seconds * 1000;
61+
firebase.database().ref(`users/${userId}/banned`).set(endTime);
62+
}
5863
};
5964

6065
const handleUnban = () => {
@@ -96,17 +101,7 @@ function ProfileName({ userId }) {
96101
{player.banned && Date.now() < player.banned ? (
97102
<MenuItem onClick={() => handleUnban()}>Unban</MenuItem>
98103
) : (
99-
[
100-
<MenuItem key={1} onClick={() => handleBan(30)}>
101-
Ban for 30 minutes
102-
</MenuItem>,
103-
<MenuItem key={2} onClick={() => handleBan(24 * 60)}>
104-
Ban for 1 day
105-
</MenuItem>,
106-
<MenuItem key={3} onClick={() => handleBan(7 * 24 * 60)}>
107-
Ban for 1 week
108-
</MenuItem>,
109-
]
104+
<MenuItem onClick={() => setBanUser(true)}>Ban</MenuItem>
110105
)}
111106
</Menu>
112107
</div>
@@ -132,6 +127,14 @@ function ProfileName({ userId }) {
132127
)}
133128
</span>
134129
</Typography>
130+
<PromptDialog
131+
open={banUser}
132+
onClose={handleBan}
133+
title="Ban User"
134+
message="Enter ban duration (examples: 1w, 3d, 1.5h, 1h20m, 30m)."
135+
label="Duration"
136+
maxLength={25}
137+
/>
135138
</section>
136139
);
137140
}}

src/util.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,15 @@ export function formatDateTime(timestamp) {
156156
const opts = { timeStyle: "short", hour12: false };
157157
return `${d.toLocaleDateString()} ${d.toLocaleTimeString(undefined, opts)}`;
158158
}
159+
160+
export function parseDuration(spec) {
161+
const units = [7 * 24 * 3600, 24 * 3600, 3600, 60, 1];
162+
const re =
163+
/^(?:(\d+(?:\.\d+)?)w)?(?:(\d+(?:\.\d+)?)d)?(?:(\d+(?:\.\d+)?)h)?(?:(\d+(?:\.\d+)?)m)?(?:(\d+(?:\.\d+)?)s)?$/i;
164+
const m = re.exec(spec);
165+
return !m || !m[0]
166+
? null
167+
: units
168+
.map((v, i) => (m[i + 1] ? parseFloat(m[i + 1]) * v : 0))
169+
.reduce((a, c) => a + c);
170+
}

src/util.test.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { badWords } from "./util";
1+
import { badWords, parseDuration } from "./util";
22

33
describe("bad words filter", () => {
44
it("sort of works", () => {
@@ -16,3 +16,16 @@ describe("bad words filter", () => {
1616
expect(badWords.hasMatch("a\u200dsshole")).toBe(true);
1717
});
1818
});
19+
20+
it("parseDuration works", () => {
21+
expect(parseDuration("2w")).toBe(2 * 7 * 24 * 3600);
22+
expect(parseDuration("3d")).toBe(3 * 24 * 3600);
23+
expect(parseDuration("1.5h")).toBe(1.5 * 3600);
24+
expect(parseDuration("20m")).toBe(20 * 60);
25+
expect(parseDuration("300s")).toBe(300);
26+
expect(parseDuration("1h30m")).toBe(1.5 * 3600);
27+
expect(parseDuration("1w1d1h1m")).toBe((8 * 24 + 1) * 3600 + 60);
28+
expect(parseDuration("1d1w")).toBe(null);
29+
expect(parseDuration("300")).toBe(null);
30+
expect(parseDuration("")).toBe(null);
31+
});

0 commit comments

Comments
 (0)