Browse Source

Merge branch 'from/develop/tusooa/user-note' into 'develop'

User note

See merge request pleroma/pleroma-fe!1612
HJ 2 years ago
parent
commit
e009510c52

+ 11 - 2
src/components/user_card/user_card.js

@@ -4,6 +4,7 @@ import ProgressButton from '../progress_button/progress_button.vue'
 import FollowButton from '../follow_button/follow_button.vue'
 import ModerationTools from '../moderation_tools/moderation_tools.vue'
 import AccountActions from '../account_actions/account_actions.vue'
+import UserNote from '../user_note/user_note.vue'
 import Select from '../select/select.vue'
 import UserLink from '../user_link/user_link.vue'
 import RichContent from 'src/components/rich_content/rich_content.jsx'
@@ -39,7 +40,8 @@ export default {
     'rounded',
     'bordered',
     'avatarAction', // default - open profile, 'zoom' - zoom, function - call function
-    'onClose'
+    'onClose',
+    'hasNoteEditor'
   ],
   data () {
     return {
@@ -129,6 +131,12 @@ export default {
       const privileges = this.loggedIn.privileges
       return this.loggedIn.role === 'admin' || privileges.includes('users_manage_activation_state') || privileges.includes('users_delete') || privileges.includes('users_manage_tags')
     },
+    hasNote () {
+      return this.relationship.note
+    },
+    supportsNote () {
+      return 'note' in this.relationship
+    },
     ...mapGetters(['mergedConfig'])
   },
   components: {
@@ -140,7 +148,8 @@ export default {
     FollowButton,
     Select,
     RichContent,
-    UserLink
+    UserLink,
+    UserNote
   },
   methods: {
     muteUser () {

+ 4 - 0
src/components/user_card/user_card.scss

@@ -315,6 +315,10 @@
       margin: 0;
     }
   }
+
+  .user-note {
+    margin: 0 .75em .6em 0;
+  }
 }
 
 .sidebar .edit-profile-button {

+ 6 - 0
src/components/user_card/user_card.vue

@@ -268,6 +268,12 @@
         >
           <RemoteFollow :user="user" />
         </div>
+        <UserNote
+          v-if="loggedIn && isOtherUser && (hasNote || (hasNoteEditor && supportsNote))"
+          :user="user"
+          :relationship="relationship"
+          :editable="hasNoteEditor"
+        />
       </div>
     </div>
     <div

+ 45 - 0
src/components/user_note/user_note.js

@@ -0,0 +1,45 @@
+const UserNote = {
+  props: {
+    user: Object,
+    relationship: Object,
+    editable: Boolean
+  },
+  data () {
+    return {
+      localNote: '',
+      editing: false,
+      frozen: false
+    }
+  },
+  computed: {
+    shouldShow () {
+      return this.relationship.note || this.editing
+    }
+  },
+  methods: {
+    startEditing () {
+      this.localNote = this.relationship.note
+      this.editing = true
+    },
+    cancelEditing () {
+      this.editing = false
+    },
+    finalizeEditing () {
+      this.frozen = true
+
+      this.$store.dispatch('editUserNote', {
+        id: this.user.id,
+        comment: this.localNote
+      })
+        .then(() => {
+          this.frozen = false
+          this.editing = false
+        })
+        .catch(() => {
+          this.frozen = false
+        })
+    }
+  }
+}
+
+export default UserNote

+ 88 - 0
src/components/user_note/user_note.vue

@@ -0,0 +1,88 @@
+<template>
+  <div
+    class="user-note"
+  >
+    <div class="heading">
+      <span>{{ $t('user_card.note') }}</span>
+      <div class="buttons">
+        <button
+          v-show="!editing && editable"
+          class="button-default btn"
+          @click="startEditing"
+        >
+          {{ $t('user_card.edit_note') }}
+        </button>
+        <button
+          v-show="editing"
+          class="button-default btn"
+          :disabled="frozen"
+          @click="finalizeEditing"
+        >
+          {{ $t('user_card.edit_note_apply') }}
+        </button>
+        <button
+          v-show="editing"
+          class="button-default btn"
+          :disabled="frozen"
+          @click="cancelEditing"
+        >
+          {{ $t('user_card.edit_note_cancel') }}
+        </button>
+      </div>
+    </div>
+    <textarea
+      v-show="editing"
+      v-model="localNote"
+      class="note-text"
+    />
+    <span
+      v-show="!editing"
+      class="note-text"
+      :class="{ '-blank': !relationship.note }"
+    >
+      {{ relationship.note || $t('user_card.note_blank') }}
+    </span>
+  </div>
+</template>
+
+<script src="./user_note.js"></script>
+
+<style lang="scss">
+@import '../../variables';
+
+.user-note {
+  display: flex;
+  flex-direction: column;
+
+  .heading {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 0.75em;
+
+    .btn {
+      min-width: 95px;
+    }
+
+    .buttons {
+      display: flex;
+      flex-direction: row;
+      justify-content: right;
+
+      .btn {
+        margin-left: 0.5em;
+      }
+    }
+  }
+
+  .note-text {
+    align-self: stretch;
+  }
+
+  .note-text.-blank {
+    font-style: italic;
+    color: var(--faint, $fallback--faint);
+  }
+}
+</style>

+ 1 - 0
src/components/user_profile/user_profile.vue

@@ -10,6 +10,7 @@
         :selected="timeline.viewing"
         avatar-action="zoom"
         rounded="top"
+        :has-note-editor="true"
       />
       <div
         v-if="user.fields_html && user.fields_html.length > 0"

+ 6 - 1
src/i18n/en.json

@@ -957,7 +957,12 @@
       "solid": "Solid bg",
       "striped": "Striped bg",
       "side": "Side stripe"
-    }
+    },
+    "note": "Note",
+    "note_blank": "(None)",
+    "edit_note": "Edit note",
+    "edit_note_apply": "Apply",
+    "edit_note_cancel": "Cancel"
   },
   "user_profile": {
     "timeline_title": "User timeline",

+ 8 - 0
src/modules/users.js

@@ -56,6 +56,11 @@ const removeUserFromFollowers = (store, id) => {
     .then((relationship) => store.commit('updateUserRelationship', [relationship]))
 }
 
+const editUserNote = (store, { id, comment }) => {
+  return store.rootState.api.backendInteractor.editUserNote({ id, comment })
+    .then((relationship) => store.commit('updateUserRelationship', [relationship]))
+}
+
 const muteUser = (store, id) => {
   const predictedRelationship = store.state.relationships[id] || { id }
   predictedRelationship.muting = true
@@ -335,6 +340,9 @@ const users = {
     unblockUsers (store, ids = []) {
       return Promise.all(ids.map(id => unblockUser(store, id)))
     },
+    editUserNote (store, args) {
+      return editUserNote(store, args)
+    },
     fetchMutes (store) {
       return store.rootState.api.backendInteractor.fetchMutes()
         .then((mutes) => {

+ 13 - 0
src/services/api/api.service.js

@@ -70,6 +70,7 @@ const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
 const MASTODON_REMOVE_USER_FROM_FOLLOWERS = id => `/api/v1/accounts/${id}/remove_from_followers`
 const MASTODON_SUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/subscribe`
 const MASTODON_UNSUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/unsubscribe`
+const MASTODON_USER_NOTE_URL = id => `/api/v1/accounts/${id}/note`
 const MASTODON_BOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/bookmark`
 const MASTODON_UNBOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/unbookmark`
 const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
@@ -321,6 +322,17 @@ const removeUserFromFollowers = ({ id, credentials }) => {
   }).then((data) => data.json())
 }
 
+const editUserNote = ({ id, credentials, comment }) => {
+  return promisedRequest({
+    url: MASTODON_USER_NOTE_URL(id),
+    credentials,
+    payload: {
+      comment
+    },
+    method: 'POST'
+  })
+}
+
 const approveUser = ({ id, credentials }) => {
   const url = MASTODON_APPROVE_USER_URL(id)
   return fetch(url, {
@@ -1667,6 +1679,7 @@ const apiService = {
   blockUser,
   unblockUser,
   removeUserFromFollowers,
+  editUserNote,
   fetchUser,
   fetchUserByName,
   fetchUserRelationship,