1
0
mirror of https://github.com/flarum/core.git synced 2025-08-04 15:37:51 +02:00

feat: Display suspension to user (#41)

This commit is contained in:
Ian Morland
2021-12-12 23:48:10 +00:00
committed by GitHub
parent 984b553dc8
commit fc06dba1b3
17 changed files with 333 additions and 61 deletions

View File

@@ -0,0 +1,19 @@
import app from 'flarum/forum/app';
import SuspensionInfoModal from './components/SuspensionInfoModal';
import { localStorageKey } from './helpers/suspensionHelper';
export default function () {
return setTimeout(() => {
if (app.session.user) {
const message = app.session.user.suspendMessage();
const until = app.session.user.suspendedUntil();
const alreadyDisplayed = localStorage.getItem(localStorageKey()) === until.getTime().toString();
if (message && !alreadyDisplayed) {
app.modal.show(SuspensionInfoModal, { message, until });
} else if (!until && localStorage.getItem(localStorageKey())) {
localStorage.removeItem(localStorageKey());
}
}
}, 0);
}

View File

@@ -0,0 +1,13 @@
import SuspendUserModal from './components/SuspendUserModal';
import SuspensionInfoModal from './components/SuspensionInfoModal';
import UserSuspendedNotification from './components/UserSuspendedNotification';
import UserUnsuspendedNotification from './components/UserUnsuspendedNotification';
import checkForSuspension from './checkForSuspension';
export default {
'suspend/components/suspendUserModal': SuspendUserModal,
'suspend/components/suspensionInfoModal': SuspensionInfoModal,
'suspend/components/UserSuspendedNotification': UserSuspendedNotification,
'suspend/components/UserUnsuspendedNotification': UserUnsuspendedNotification,
'suspend/checkForSuspension': checkForSuspension,
};

View File

@@ -1,14 +1,19 @@
import app from 'flarum/forum/app';
import Modal from 'flarum/components/Modal';
import Button from 'flarum/components/Button';
import Stream from 'flarum/utils/Stream';
import withAttr from 'flarum/utils/withAttr';
import ItemList from 'flarum/common/utils/ItemList';
import { getPermanentSuspensionDate } from '../helpers/suspensionHelper';
export default class SuspendUserModal extends Modal {
oninit(vnode) {
super.oninit(vnode);
let until = this.attrs.user.suspendedUntil();
const reason = this.attrs.user.suspendReason();
const message = this.attrs.user.suspendMessage();
let status = null;
if (new Date() > until) until = null;
@@ -19,11 +24,13 @@ export default class SuspendUserModal extends Modal {
}
this.status = Stream(status);
this.reason = Stream(reason);
this.message = Stream(message);
this.daysRemaining = Stream(status === 'limited' && -dayjs().diff(until, 'days') + 1);
}
className() {
return 'SuspendUserModal Modal--small';
return 'SuspendUserModal Modal--medium';
}
title() {
@@ -36,53 +43,7 @@ export default class SuspendUserModal extends Modal {
<div className="Form">
<div className="Form-group">
<label>{app.translator.trans('flarum-suspend.forum.suspend_user.status_heading')}</label>
<div>
<label className="checkbox">
<input type="radio" name="status" checked={!this.status()} value="" onclick={withAttr('value', this.status)} />
{app.translator.trans('flarum-suspend.forum.suspend_user.not_suspended_label')}
</label>
<label className="checkbox">
<input
type="radio"
name="status"
checked={this.status() === 'indefinitely'}
value="indefinitely"
onclick={withAttr('value', this.status)}
/>
{app.translator.trans('flarum-suspend.forum.suspend_user.indefinitely_label')}
</label>
<label className="checkbox SuspendUserModal-days">
<input
type="radio"
name="status"
checked={this.status() === 'limited'}
value="limited"
onclick={(e) => {
this.status(e.target.value);
m.redraw.sync();
this.$('.SuspendUserModal-days-input input').select();
e.redraw = false;
}}
/>
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_label')}
{this.status() === 'limited' ? (
<div className="SuspendUserModal-days-input">
<input
type="number"
min="0"
value={this.daysRemaining()}
oninput={withAttr('value', this.daysRemaining)}
className="FormControl"
/>
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_days_text')}
</div>
) : (
''
)}
</label>
</div>
<div>{this.formItems().toArray()}</div>
</div>
<div className="Form-group">
@@ -95,6 +56,96 @@ export default class SuspendUserModal extends Modal {
);
}
radioItems() {
const items = new ItemList();
items.add(
'not-suspended',
<label className="checkbox">
<input type="radio" name="status" checked={!this.status()} value="" onclick={withAttr('value', this.status)} />
{app.translator.trans('flarum-suspend.forum.suspend_user.not_suspended_label')}
</label>,
100
);
items.add(
'indefinitely',
<label className="checkbox">
<input type="radio" name="status" checked={this.status() === 'indefinitely'} value="indefinitely" onclick={withAttr('value', this.status)} />
{app.translator.trans('flarum-suspend.forum.suspend_user.indefinitely_label')}
</label>,
90
);
items.add(
'time-suspension',
<label className="checkbox SuspendUserModal-days">
<input
type="radio"
name="status"
checked={this.status() === 'limited'}
value="limited"
onclick={(e) => {
this.status(e.target.value);
m.redraw.sync();
this.$('.SuspendUserModal-days-input input').select();
e.redraw = false;
}}
/>
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_label')}
{this.status() === 'limited' && (
<div className="SuspendUserModal-days-input">
<input type="number" min="0" value={this.daysRemaining()} oninput={withAttr('value', this.daysRemaining)} className="FormControl" />
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_days_text')}
</div>
)}
</label>,
80
);
return items;
}
formItems() {
const items = new ItemList();
items.add('radioItems', <div className="Form-group">{this.radioItems().toArray()}</div>, 100);
items.add(
'reason',
<div className="Form-group">
<label>
{app.translator.trans('flarum-suspend.forum.suspend_user.reason')}
<textarea
className="FormControl"
bidi={this.reason}
placeholder={app.translator.trans('flarum-suspend.forum.suspend_user.placeholder_optional')}
rows="2"
/>
</label>
</div>,
90
);
items.add(
'message',
<div className="Form-group">
<label>
{app.translator.trans('flarum-suspend.forum.suspend_user.display_message')}
<textarea
className="FormControl"
bidi={this.message}
placeholder={app.translator.trans('flarum-suspend.forum.suspend_user.placeholder_optional')}
rows="2"
/>
</label>
</div>,
80
);
return items;
}
onsubmit(e) {
e.preventDefault();
@@ -103,7 +154,7 @@ export default class SuspendUserModal extends Modal {
let suspendedUntil = null;
switch (this.status()) {
case 'indefinitely':
suspendedUntil = new Date('2038-01-01');
suspendedUntil = getPermanentSuspensionDate();
break;
case 'limited':
@@ -114,6 +165,8 @@ export default class SuspendUserModal extends Modal {
// no default
}
this.attrs.user.save({ suspendedUntil }).then(() => this.hide(), this.loaded.bind(this));
this.attrs.user
.save({ suspendedUntil, suspendReason: this.reason(), suspendMessage: this.message() })
.then(() => this.hide(), this.loaded.bind(this));
}
}

View File

@@ -0,0 +1,48 @@
import app from 'flarum/forum/app';
import Modal from 'flarum/common/components/Modal';
import Button from 'flarum/common/components/Button';
import fullTime from 'flarum/common/helpers/fullTime';
import { isPermanentSuspensionDate, localStorageKey } from '../helpers/suspensionHelper';
export default class SuspensionInfoModal extends Modal {
oninit(vnode) {
super.oninit(vnode);
this.message = this.attrs.message;
this.until = this.attrs.until;
}
className() {
return 'SuspensionInfoModal Modal';
}
title() {
return app.translator.trans('flarum-suspend.forum.suspension_info.title');
}
content() {
const timespan = isPermanentSuspensionDate(new Date(this.until))
? app.translator.trans('flarum-suspend.forum.suspension_info.indefinite')
: app.translator.trans('flarum-suspend.forum.suspension_info.limited', { date: fullTime(this.until) });
return (
<div className="Modal-body">
<div className="Form Form--centered">
<p className="helpText">{this.message}</p>
<p className="helpText">{timespan}</p>
<div className="Form-group">
<Button className="Button Button--primary Button--block" onclick={this.hide.bind(this)}>
{app.translator.trans('flarum-suspend.forum.suspension_info.dismiss_button')}
</Button>
</div>
</div>
</div>
);
}
hide() {
localStorage.setItem(localStorageKey(), this.attrs.until.getTime());
this.attrs.state.close();
}
}

View File

@@ -1,4 +1,6 @@
import app from 'flarum/forum/app';
import Notification from 'flarum/components/Notification';
import { isPermanentSuspensionDate } from '../helpers/suspensionHelper';
export default class UserSuspendedNotification extends Notification {
icon() {
@@ -14,8 +16,10 @@ export default class UserSuspendedNotification extends Notification {
const suspendedUntil = notification.content();
const timeReadable = dayjs(suspendedUntil).from(notification.createdAt(), true);
return app.translator.trans('flarum-suspend.forum.notifications.user_suspended_text', {
timeReadable,
});
return isPermanentSuspensionDate(suspendedUntil)
? app.translator.trans('flarum-suspend.forum.notifications.user_suspended_indefinite_text')
: app.translator.trans('flarum-suspend.forum.notifications.user_suspended_text', {
timeReadable,
});
}
}

View File

@@ -1,3 +1,4 @@
import app from 'flarum/forum/app';
import Notification from 'flarum/components/Notification';
export default class UserUnsuspendedNotification extends Notification {

View File

@@ -0,0 +1,16 @@
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);
export function getPermanentSuspensionDate(): Date {
return new Date('2038-01-01');
}
export function isPermanentSuspensionDate(date: Date): boolean {
return dayjs.utc(date).isSame(dayjs.utc('2038-01-01'));
}
export function localStorageKey(): string {
return 'flarum-suspend.acknowledge-suspension';
}

View File

@@ -9,6 +9,7 @@ import User from 'flarum/models/User';
import SuspendUserModal from './components/SuspendUserModal';
import UserSuspendedNotification from './components/UserSuspendedNotification';
import UserUnsuspendedNotification from './components/UserUnsuspendedNotification';
import checkForSuspension from './checkForSuspension';
app.initializers.add('flarum-suspend', () => {
app.notificationComponents.userSuspended = UserSuspendedNotification;
@@ -16,6 +17,8 @@ app.initializers.add('flarum-suspend', () => {
User.prototype.canSuspend = Model.attribute('canSuspend');
User.prototype.suspendedUntil = Model.attribute('suspendedUntil', Model.transformDate);
User.prototype.suspendReason = Model.attribute('suspendReason');
User.prototype.suspendMessage = Model.attribute('suspendMessage');
extend(UserControls, 'moderationControls', (items, user) => {
if (user.canSuspend()) {
@@ -46,4 +49,12 @@ app.initializers.add('flarum-suspend', () => {
);
}
});
checkForSuspension();
});
// Expose compat API
import suspendCompat from './compat';
import { compat } from '@flarum/core/forum';
Object.assign(compat, suspendCompat);