aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortaehoon <th.dev91@gmail.com>2019-03-19 04:53:11 -0400
committertaehoon <th.dev91@gmail.com>2019-05-03 11:40:05 -0400
commit2cda9010df6192b008515b2138a2e071473bc40b (patch)
treeb753369b2c9803fa5c0cae0e70895f5d0dfae0cc /src
parent0438031da44a70816716de40625541d569a49c85 (diff)
add user reporting modal
Diffstat (limited to 'src')
-rw-r--r--src/App.js4
-rw-r--r--src/App.vue1
-rw-r--r--src/components/checkbox/checkbox.js44
-rw-r--r--src/components/checkbox/checkbox.scss48
-rw-r--r--src/components/user_card/user_card.js3
-rw-r--r--src/components/user_card/user_card.vue10
-rw-r--r--src/components/user_reporting_modal/user_reporting_modal.js81
-rw-r--r--src/components/user_reporting_modal/user_reporting_modal.vue111
-rw-r--r--src/i18n/en.json1
-rw-r--r--src/main.js4
-rw-r--r--src/modules/reports.js33
11 files changed, 336 insertions, 4 deletions
diff --git a/src/App.js b/src/App.js
index 46145b16..e72c73e3 100644
--- a/src/App.js
+++ b/src/App.js
@@ -10,6 +10,7 @@ import MediaModal from './components/media_modal/media_modal.vue'
import SideDrawer from './components/side_drawer/side_drawer.vue'
import MobilePostStatusModal from './components/mobile_post_status_modal/mobile_post_status_modal.vue'
import MobileNav from './components/mobile_nav/mobile_nav.vue'
+import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'
import { windowWidth } from './services/window_utils/window_utils'
export default {
@@ -26,7 +27,8 @@ export default {
MediaModal,
SideDrawer,
MobilePostStatusModal,
- MobileNav
+ MobileNav,
+ UserReportingModal
},
data: () => ({
mobileActivePanel: 'timeline',
diff --git a/src/App.vue b/src/App.vue
index 3b8623ad..cb7e8d78 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -46,6 +46,7 @@
<media-modal></media-modal>
</div>
<chat-panel :floating="true" v-if="currentUser && chat" class="floating-chat mobile-hidden"></chat-panel>
+ <UserReportingModal />
</div>
</template>
diff --git a/src/components/checkbox/checkbox.js b/src/components/checkbox/checkbox.js
new file mode 100644
index 00000000..324a7597
--- /dev/null
+++ b/src/components/checkbox/checkbox.js
@@ -0,0 +1,44 @@
+// TODO: Template-based functional component is supported in vue-loader 13.3.0+.
+// Also, somehow, props are not provided through 'context' even though they are defined.
+// Need to upgrade vue-loader
+
+import './checkbox.scss'
+
+export default {
+ functional: true,
+ name: 'Checkbox',
+ model: {
+ prop: 'checked',
+ event: 'change'
+ },
+ render (createElement, { data, children }) {
+ const { props = {}, attrs = {}, on = {}, ...rest } = data
+ const { name, checked, disabled, readonly, ...restAttrs } = attrs
+ const { change, ...restListeners } = on
+ const wrapperProps = {
+ attrs: restAttrs,
+ on: restListeners,
+ ...rest
+ }
+ const inputProps = {
+ attrs: {
+ name,
+ checked,
+ disabled,
+ readonly,
+ ...props
+ },
+ on: {}
+ }
+ if (change) {
+ inputProps.on.change = e => change(e.target.checked)
+ }
+ return (
+ <label class="checkbox" {...wrapperProps}>
+ <input type="checkbox" {...inputProps} />
+ <i />
+ {children && <span>{children}</span>}
+ </label>
+ )
+ }
+}
diff --git a/src/components/checkbox/checkbox.scss b/src/components/checkbox/checkbox.scss
new file mode 100644
index 00000000..07b3f39e
--- /dev/null
+++ b/src/components/checkbox/checkbox.scss
@@ -0,0 +1,48 @@
+@import '../../_variables.scss';
+
+.checkbox {
+ position: relative;
+ display: inline-block;
+ padding-left: 1.2em;
+
+ input[type=checkbox] {
+ display: none;
+
+ & + i::before {
+ position: absolute;
+ left: 0;
+ top: 0;
+ display: block;
+ content: '✔';
+ transition: color 200ms;
+ width: 1.1em;
+ height: 1.1em;
+ border-radius: $fallback--checkboxRadius;
+ border-radius: var(--checkboxRadius, $fallback--checkboxRadius);
+ box-shadow: 0px 0px 2px black inset;
+ box-shadow: var(--inputShadow);
+ background-color: $fallback--fg;
+ background-color: var(--input, $fallback--fg);
+ vertical-align: top;
+ text-align: center;
+ line-height: 1.1em;
+ font-size: 1.1em;
+ color: transparent;
+ overflow: hidden;
+ box-sizing: border-box;
+ }
+
+ &:checked + i::before {
+ color: $fallback--text;
+ color: var(--text, $fallback--text);
+ }
+
+ &:disabled + i::before {
+ opacity: .5;
+ }
+ }
+
+ & > span {
+ margin-left: .5em;
+ }
+} \ No newline at end of file
diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js
index 1a100de3..7c6ffa89 100644
--- a/src/components/user_card/user_card.js
+++ b/src/components/user_card/user_card.js
@@ -151,6 +151,9 @@ export default {
},
userProfileLink (user) {
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
+ },
+ reportUser () {
+ this.$store.dispatch('openUserReportingModal', this.user.id)
}
}
}
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index e62b384d..2d02ca03 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -99,8 +99,14 @@
</button>
</span>
</div>
- <ModerationTools :user='user' v-if='loggedIn.role === "admin"'>
- </ModerationTools>
+ <div class='block' v-if='isOtherUser && loggedIn'>
+ <span>
+ <button @click="reportUser">
+ {{ $t('user_card.report') }}
+ </button>
+ </span>
+ </div>
+ <ModerationTools :user='user' v-if='loggedIn.role === "admin"'/>
</div>
</div>
</div>
diff --git a/src/components/user_reporting_modal/user_reporting_modal.js b/src/components/user_reporting_modal/user_reporting_modal.js
new file mode 100644
index 00000000..fb9ea16d
--- /dev/null
+++ b/src/components/user_reporting_modal/user_reporting_modal.js
@@ -0,0 +1,81 @@
+
+import Status from '../status/status.vue'
+import Checkbox from '../checkbox/checkbox.js'
+
+const UserReportingModal = {
+ components: {
+ Status,
+ Checkbox
+ },
+ data () {
+ return {
+ comment: '',
+ forward: false,
+ statusIdsToReport: []
+ }
+ },
+ computed: {
+ isLoggedIn () {
+ return !!this.$store.state.users.currentUser
+ },
+ isOpen () {
+ return this.isLoggedIn && this.$store.state.reports.modalActivated
+ },
+ userId () {
+ return this.$store.state.reports.userId
+ },
+ user () {
+ return this.$store.getters.findUser(this.userId)
+ },
+ remoteInstance () {
+ return !this.user.is_local && this.user.screen_name.substr(this.user.screen_name.indexOf('@') + 1)
+ },
+ statuses () {
+ return this.$store.state.reports.statuses
+ }
+ },
+ watch: {
+ userId (value) {
+ this.statusIdsToReport = []
+ }
+ },
+ methods: {
+ closeModal () {
+ this.$store.dispatch('closeUserReportingModal')
+ },
+ reportUser () {
+ const payload = {
+ comment: this.comment,
+ forward: this.forward,
+ statusIdsToReport: this.statusIdsToReport
+ }
+ this.$store.dispatch('reportUser', payload)
+ },
+ isChecked (statusId) {
+ return this.statusIdsToReport.indexOf(statusId) !== -1
+ },
+ toggleStatus (checked, statusId) {
+ if (checked === this.isChecked(statusId)) {
+ return
+ }
+
+ if (checked) {
+ this.statusIdsToReport.push(statusId)
+ } else {
+ this.statusIdsToReport.splice(this.statusIdsToReport.indexOf(statusId), 1)
+ }
+ },
+ resize (e) {
+ const target = e.target || e
+ if (!(target instanceof window.Element)) { return }
+ // Auto is needed to make textbox shrink when removing lines
+ target.style.height = 'auto'
+ target.style.height = `${target.scrollHeight}px`
+ if (target.value === '') {
+ target.style.height = null
+ }
+ }
+ }
+}
+
+export default UserReportingModal
diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue
new file mode 100644
index 00000000..49839da3
--- /dev/null
+++ b/src/components/user_reporting_modal/user_reporting_modal.vue
@@ -0,0 +1,111 @@
+<template>
+<div class="modal-view" @click="closeModal" v-if="isOpen">
+ <div class="user-reporting-panel panel" @click.stop="">
+ <div class="panel-heading">Reporting {{user.screen_name}}</div>
+ <div class="panel-body">
+ <div class="user-reporting-panel-left">
+ <div>
+ <p>The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:</p>
+ <textarea
+ v-model="comment"
+ class="form-control"
+ placeholder="Additional comments"
+ rows="1"
+ @input="resize"
+ />
+ </div>
+ <div v-if="!user.is_local">
+ <p>The account is from another server. Send an anonymized copy of the report there as well?</p>
+ <Checkbox v-model="forward">Forward to {{remoteInstance}}</Checkbox>
+ </div>
+ <div>
+ <button class="btn btn-default" @click="reportUser">Submit</button>
+ </div>
+ </div>
+ <div class="user-reporting-panel-right">
+ <div v-for="status in statuses" :key="status.id" class="status-fadein">
+ <Status :inConversation="false" :focused="false" :statusoid="status" />
+ <Checkbox :checked="isChecked(status.id)" @change="checked => toggleStatus(checked, status.id)" />
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</template>
+
+<script src="./user_reporting_modal.js"></script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.user-reporting-panel {
+ width: 90vw;
+ max-width: 700px;
+
+ .panel-body {
+ display: flex;
+ border-top: 1px solid;
+ border-color: $fallback--border;
+ border-color: var(--border, $fallback--border);
+ }
+
+ &-left {
+ width: 50%;
+ padding: 1.1em;
+ border-right: 1px solid;
+ border-color: $fallback--border;
+ border-color: var(--border, $fallback--border);
+ max-width: 320px;
+ line-height: 1.4em;
+ box-sizing: border-box;
+
+ > div {
+ margin-bottom: 2em;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ p {
+ margin-top: 0;
+ }
+
+ textarea.form-control {
+ line-height: 16px;
+ resize: none;
+ overflow: hidden;
+ transition: min-height 200ms 100ms;
+ min-height: 44px;
+ width: 100%;
+ }
+
+ .btn {
+ min-width: 10em;
+ padding: 0 2em;
+ }
+ }
+
+ &-right {
+ width: 50%;
+ flex: 1 1 auto;
+ min-height: 20vh;
+ max-height: 80vh;
+ overflow-y: auto;
+ overflow-x: hidden;
+
+ > div {
+ display: flex;
+ justify-content: space-between;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ border-color: $fallback--border;
+ border-color: var(--border, $fallback--border);
+
+ .checkbox {
+ margin: 0.75em;
+ }
+ }
+ }
+}
+</style>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index b07af5e5..8292c921 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -420,6 +420,7 @@
"muted": "Muted",
"per_day": "per day",
"remote_follow": "Remote follow",
+ "report": "Report",
"statuses": "Statuses",
"unblock": "Unblock",
"unblock_progress": "Unblocking...",
diff --git a/src/main.js b/src/main.js
index 725f5806..92f843b1 100644
--- a/src/main.js
+++ b/src/main.js
@@ -12,6 +12,7 @@ import chatModule from './modules/chat.js'
import oauthModule from './modules/oauth.js'
import mediaViewerModule from './modules/media_viewer.js'
import oauthTokensModule from './modules/oauth_tokens.js'
+import reportsModule from './modules/reports.js'
import VueTimeago from 'vue-timeago'
import VueI18n from 'vue-i18n'
@@ -75,7 +76,8 @@ const persistedStateOptions = {
chat: chatModule,
oauth: oauthModule,
mediaViewer: mediaViewerModule,
- oauthTokens: oauthTokensModule
+ oauthTokens: oauthTokensModule,
+ reports: reportsModule
},
plugins: [persistedState, pushNotifications],
strict: false // Socket modifies itself, let's ignore this for now.
diff --git a/src/modules/reports.js b/src/modules/reports.js
new file mode 100644
index 00000000..b712cfeb
--- /dev/null
+++ b/src/modules/reports.js
@@ -0,0 +1,33 @@
+import filter from 'lodash/filter'
+
+const reports = {
+ state: {
+ userId: null,
+ statuses: [],
+ modalActivated: false
+ },
+ mutations: {
+ openUserReportingModal (state, { userId, statuses }) {
+ state.userId = userId
+ state.statuses = statuses
+ state.modalActivated = true
+ },
+ closeUserReportingModal (state) {
+ state.modalActivated = false
+ }
+ },
+ actions: {
+ openUserReportingModal ({ rootState, commit }, userId) {
+ const statuses = filter(rootState.statuses.allStatuses, status => status.user.id === userId)
+ commit('openUserReportingModal', { userId, statuses })
+ },
+ closeUserReportingModal ({ commit }) {
+ commit('closeUserReportingModal')
+ },
+ reportUser ({ commit }, payload) {
+ console.log('payload', payload)
+ }
+ }
+}
+
+export default reports