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 @@
+
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 (
+
+ )
+ }
+}
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 @@
-
-
+
+
+
+
+
+
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 @@
+
+
+
+
Reporting {{user.screen_name}}
+
+
+
+
The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:
+
+
+
+
The account is from another server. Send an anonymized copy of the report there as well?
+
Forward to {{remoteInstance}}
+
+
+
+
+
+
+
+
+ toggleStatus(checked, status.id)" />
+
+
+
+
+
+
+
+
+
+
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