Revert "Purge defunct settings (#6)"

This reverts commit fc98eea515.
This commit is contained in:
Sam Therapy 2022-11-30 23:05:31 +01:00
parent e018d26a5b
commit eeddb8e160
Signed by: sam
GPG key ID: 4D8B07C18F31ACBD
16 changed files with 1414 additions and 3 deletions

617
src/api/__mocks__/chat.js Normal file
View file

@ -0,0 +1,617 @@
export async function fetchChat(id, authHost, token) {
return Promise.resolve({ data: userChats[0] })
}
export async function fetchChatMessages(id, max_id, authHost, token) {
return Promise.resolve({ data: chatMessages })
}
export async function deleteChat(chat_id, message_id, authHost, token) {
return Promise.resolve({ data: userChats[0] })
}
export const userChats = [
{
id: '9y8e7ESoVUiKVMAoCW',
last_message: {
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'heyy',
created_at: '2020-09-11T00:07:13.000Z',
emojis: [],
id: '9z22Q9MzWiAk7GZnNY',
unread: false
},
receiver: {
acct: 'test10',
avatar: 'http://localhost:4000/images/avi.png',
avatar_static: 'http://localhost:4000/images/avi.png',
bot: false,
created_at: '2020-07-27T00:33:02.000Z',
display_name: 'test10',
emojis: [],
fields: [],
followers_count: 2,
following_count: 1,
header: 'http://localhost:4000/images/banner.png',
header_static: 'http://localhost:4000/images/banner.png',
id: '9xUj5WTmzSlFPN6OLg',
locked: false,
note: '',
pleroma: {
accepts_chat_messages: true,
ap_id: 'http://localhost:4000/users/test10',
background_image: null,
is_confirmed: true,
favicon: null,
hide_favorites: true,
hide_followers: false,
hide_followers_count: false,
hide_follows: false,
hide_follows_count: false,
is_admin: false,
is_moderator: true,
relationship: {},
skip_thread_containment: false,
tags: [
'mrf_tag:media-force-nsfw',
'mrf_tag:media-strip',
'mrf_tag:force-unlisted'
]
},
source: {
fields: [],
note: '',
pleroma: {
actor_type: 'Person',
discoverable: false
},
sensitive: false
},
statuses_count: 20,
url: 'http://localhost:4000/users/test10',
username: 'test10'
},
sender: {
acct: 'mk',
avatar: 'http://localhost:4000/media/9051960b674309674f2f1a3a0e05916013260af13f672df32c869cb841958a44.gif',
avatar_static: 'http://localhost:4000/media/9051960b674309674f2f1a3a0e05916013260af13f672df32c869cb841958a44.gif',
bot: false,
created_at: '2020-07-26T19:37:31.000Z',
display_name: 'mk',
emojis: [],
fields: [
{
name: 'website',
value: '<a href="http://marykatefain.com" rel="ugc">marykatefain.com</a>'
}
],
followers_count: 2,
following_count: 1,
header: 'http://localhost:4000/media/139889433c4085dcf219171fdcc48659fa3fb5090a748ca37513fda051226b12.gif',
header_static: 'http://localhost:4000/media/139889433c4085dcf219171fdcc48659fa3fb5090a748ca37513fda051226b12.gif',
id: '9xUIiQfGrdPJDZe39s',
locked: false,
note: 'a bio about me',
pleroma: {
accepts_chat_messages: true,
ap_id: 'https://localhost/users/mk',
background_image: null,
is_confirmed: true,
favicon: null,
hide_favorites: true,
hide_followers: false,
hide_followers_count: false,
hide_follows: false,
hide_follows_count: false,
is_admin: true,
is_moderator: false,
relationship: {},
skip_thread_containment: false,
tags: [
'verified'
]
},
source: {
fields: [
{
name: 'website',
value: 'marykatefain.com'
}
],
note: 'a bio about me',
pleroma: {
actor_type: 'Person',
discoverable: false
},
sensitive: false
},
statuses_count: 77,
url: 'https://localhost/users/mk',
username: 'mk'
},
unread: 0,
updated_at: '2020-09-11T00:07:13.000Z'
},
{
id: '9y8dwDAQzFBZIZJzEG',
last_message: {
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8dwDAQzFBZIZJzEG',
content: 'hiiiiiiiiiiiiiiiiiii',
created_at: '2020-08-15T06:45:13.000Z',
emojis: [],
id: '9y8e4EZndDmfzA0lGa',
unread: false
},
receiver: {
acct: 'user2',
avatar: 'http://localhost:4000/media/5a702176cd5181f81532a42fafa87953db1586ca39a3c83cd3df1bc9b5cb7d07.png',
avatar_static: 'http://localhost:4000/media/5a702176cd5181f81532a42fafa87953db1586ca39a3c83cd3df1bc9b5cb7d07.png',
bot: false,
created_at: '2020-08-01T19:23:57.000Z',
display_name: 'User2',
emojis: [],
fields: [],
followers_count: 0,
following_count: 2,
header: 'http://localhost:4000/media/1b1fc87d56e4f94b0ca8eb23100c92f67371bfa37b2898cd804341ed1a2d5c84.jpg',
header_static: 'http://localhost:4000/media/1b1fc87d56e4f94b0ca8eb23100c92f67371bfa37b2898cd804341ed1a2d5c84.jpg',
id: '9xfhRuCIyydruc0Sh6',
locked: false,
note: 'Just me!',
pleroma: {
accepts_chat_messages: true,
ap_id: 'http://localhost:4000/users/user2',
background_image: null,
is_confirmed: false,
favicon: null,
hide_favorites: true,
hide_followers: false,
hide_followers_count: false,
hide_follows: false,
hide_follows_count: false,
is_admin: false,
is_moderator: false,
relationship: {},
skip_thread_containment: false,
tags: [
'mrf_tag:force-unlisted',
'mrf_tag:media-strip'
]
},
source: {
fields: [],
note: 'Just me!',
pleroma: {
actor_type: 'Person',
discoverable: false
},
sensitive: false
},
statuses_count: 69,
url: 'http://localhost:4000/users/user2',
username: 'user2'
},
sender: {
acct: 'mk',
avatar: 'http://localhost:4000/media/9051960b674309674f2f1a3a0e05916013260af13f672df32c869cb841958a44.gif',
avatar_static: 'http://localhost:4000/media/9051960b674309674f2f1a3a0e05916013260af13f672df32c869cb841958a44.gif',
bot: false,
created_at: '2020-07-26T19:37:31.000Z',
display_name: 'mk',
emojis: [],
fields: [
{
name: 'website',
value: '<a href="http://marykatefain.com" rel="ugc">marykatefain.com</a>'
}
],
followers_count: 2,
following_count: 1,
header: 'http://localhost:4000/media/139889433c4085dcf219171fdcc48659fa3fb5090a748ca37513fda051226b12.gif',
header_static: 'http://localhost:4000/media/139889433c4085dcf219171fdcc48659fa3fb5090a748ca37513fda051226b12.gif',
id: '9xUIiQfGrdPJDZe39s',
locked: false,
note: 'a bio about me',
pleroma: {
accepts_chat_messages: true,
ap_id: 'https://localhost/users/mk',
background_image: null,
is_confirmed: true,
favicon: null,
hide_favorites: true,
hide_followers: false,
hide_followers_count: false,
hide_follows: false,
hide_follows_count: false,
is_admin: true,
is_moderator: false,
relationship: {},
skip_thread_containment: false,
tags: [
'verified'
]
},
source: {
fields: [
{
name: 'website',
value: 'marykatefain.com'
}
],
note: 'a bio about me',
pleroma: {
actor_type: 'Person',
discoverable: false
},
sensitive: false
},
statuses_count: 77,
url: 'https://localhost/users/mk',
username: 'mk'
},
unread: 0,
updated_at: '2020-08-15T06:45:13.000Z'
},
{
id: '9y8dubemxq32fkkoeu',
last_message: null,
receiver: {
acct: 'mk',
avatar: 'http://localhost:4000/media/9051960b674309674f2f1a3a0e05916013260af13f672df32c869cb841958a44.gif',
avatar_static: 'http://localhost:4000/media/9051960b674309674f2f1a3a0e05916013260af13f672df32c869cb841958a44.gif',
bot: false,
created_at: '2020-07-26T19:37:31.000Z',
display_name: 'mk',
emojis: [],
fields: [
{
name: 'website',
value: '<a href="http://marykatefain.com" rel="ugc">marykatefain.com</a>'
}
],
followers_count: 2,
following_count: 1,
header: 'http://localhost:4000/media/139889433c4085dcf219171fdcc48659fa3fb5090a748ca37513fda051226b12.gif',
header_static: 'http://localhost:4000/media/139889433c4085dcf219171fdcc48659fa3fb5090a748ca37513fda051226b12.gif',
id: '9xUIiQfGrdPJDZe39s',
locked: false,
note: 'a bio about me',
pleroma: {
accepts_chat_messages: true,
ap_id: 'https://localhost/users/mk',
background_image: null,
is_confirmed: true,
favicon: null,
hide_favorites: true,
hide_followers: false,
hide_followers_count: false,
hide_follows: false,
hide_follows_count: false,
is_admin: true,
is_moderator: false,
relationship: {},
skip_thread_containment: false,
tags: [
'verified'
]
},
source: {
fields: [
{
name: 'website',
value: 'marykatefain.com'
}
],
note: 'a bio about me',
pleroma: {
actor_type: 'Person',
discoverable: false
},
sensitive: false
},
statuses_count: 77,
url: 'https://localhost/users/mk',
username: 'mk'
},
sender: {
acct: 'mk',
avatar: 'http://localhost:4000/media/9051960b674309674f2f1a3a0e05916013260af13f672df32c869cb841958a44.gif',
avatar_static: 'http://localhost:4000/media/9051960b674309674f2f1a3a0e05916013260af13f672df32c869cb841958a44.gif',
bot: false,
created_at: '2020-07-26T19:37:31.000Z',
display_name: 'mk',
emojis: [],
fields: [
{
name: 'website',
value: '<a href="http://marykatefain.com" rel="ugc">marykatefain.com</a>'
}
],
followers_count: 2,
following_count: 1,
header: 'http://localhost:4000/media/139889433c4085dcf219171fdcc48659fa3fb5090a748ca37513fda051226b12.gif',
header_static: 'http://localhost:4000/media/139889433c4085dcf219171fdcc48659fa3fb5090a748ca37513fda051226b12.gif',
id: '9xUIiQfGrdPJDZe39s',
locked: false,
note: 'a bio about me',
pleroma: {
accepts_chat_messages: true,
ap_id: 'https://localhost/users/mk',
background_image: null,
is_confirmed: true,
favicon: null,
hide_favorites: true,
hide_followers: false,
hide_followers_count: false,
hide_follows: false,
hide_follows_count: false,
is_admin: true,
is_moderator: false,
relationship: {},
skip_thread_containment: false,
tags: [
'verified'
]
},
source: {
fields: [
{
name: 'website',
value: 'marykatefain.com'
}
],
note: 'a bio about me',
pleroma: {
actor_type: 'Person',
discoverable: false
},
sensitive: false
},
statuses_count: 77,
url: 'https://localhost/users/mk',
username: 'mk'
},
unread: 0,
updated_at: '2020-08-15T06:43:29.000Z'
}
]
export const chatMessages = [
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'heyy',
created_at: '2020-09-11T00:07:13.000Z',
emojis: [],
id: '9z22Q9MzWiAk7GZnNY',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: {
description: null,
id: '-1990764178',
pleroma: {
mime_type: 'image/png'
},
preview_url: 'http://localhost:4000/media/b087b890b39aa8301f736e8b45c02213183986c8a994c2c99e921ae85afa17e4.png',
remote_url: 'http://localhost:4000/media/b087b890b39aa8301f736e8b45c02213183986c8a994c2c99e921ae85afa17e4.png',
text_url: 'http://localhost:4000/media/b087b890b39aa8301f736e8b45c02213183986c8a994c2c99e921ae85afa17e4.png',
type: 'image',
url: 'http://localhost:4000/media/b087b890b39aa8301f736e8b45c02213183986c8a994c2c99e921ae85afa17e4.png'
},
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: null,
created_at: '2020-09-10T20:35:33.000Z',
emojis: [],
id: '9z1jWuQNaq8Ef6fdOS',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'probably',
created_at: '2020-09-10T16:57:33.000Z',
emojis: [],
id: '9z1Q4eXb9kBYA8rNT6',
unread: false
},
{
account_id: '9xUj5WTmzSlFPN6OLg',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'That has got to be 20 by now',
created_at: '2020-09-10T16:57:29.000Z',
emojis: [],
id: '9z1Q4LQAF9wuraEfk8',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'I miss bars :(',
created_at: '2020-09-10T16:57:22.000Z',
emojis: [],
id: '9z1Q3gmlKEfEpnUdm4',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'Yeah tying to!',
created_at: '2020-09-10T16:57:15.000Z',
emojis: [],
id: '9z1Q307VxCiKVhs6eO',
unread: false
},
{
account_id: '9xUj5WTmzSlFPN6OLg',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'staying safe with covid and such?',
created_at: '2020-09-10T16:57:10.000Z',
emojis: [],
id: '9z1Q2Z56U3k6ePXIe1',
unread: false
},
{
account_id: '9xUj5WTmzSlFPN6OLg',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'how have you been?',
created_at: '2020-09-10T16:57:06.000Z',
emojis: [],
id: '9z1Q29XvQHLfSGowxU',
unread: false
},
{
account_id: '9xUj5WTmzSlFPN6OLg',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'so what are you doing?',
created_at: '2020-09-10T16:57:02.000Z',
emojis: [],
id: '9z1Q1lPb9aQYbUjxeD',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'crazy',
created_at: '2020-09-10T16:56:55.000Z',
emojis: [],
id: '9z1Q19zQHwVMXbD2DA',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'omg yeah same',
created_at: '2020-09-10T16:56:54.000Z',
emojis: [],
id: '9z1Q12zWJBQIpsCVtI',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'hahahaha',
created_at: '2020-09-10T16:56:51.000Z',
emojis: [],
id: '9z1Q0pyKiHkWSQXsjg',
unread: false
},
{
account_id: '9xUj5WTmzSlFPN6OLg',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'I think this is 13',
created_at: '2020-09-10T16:56:45.000Z',
emojis: [],
id: '9z1Q0Ft6sEBGXnpCb3',
unread: false
},
{
account_id: '9xUj5WTmzSlFPN6OLg',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'We need to keep typing until we hit 20',
created_at: '2020-09-10T16:56:42.000Z',
emojis: [],
id: '9z1PzywjrTyy08OMOO',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'That&#39;s nice',
created_at: '2020-09-10T16:56:03.000Z',
emojis: [],
id: '9z1PwOkJzs8orKsJDU',
unread: false
},
{
account_id: '9xUj5WTmzSlFPN6OLg',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'I&#39;ve been good! Keeping busy',
created_at: '2020-09-10T16:56:00.000Z',
emojis: [],
id: '9z1Pw7qmoaDASSljQv',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: '*been',
created_at: '2020-09-10T16:55:55.000Z',
emojis: [],
id: '9z1PvcWVHwZFIa2b0y',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'How have you ben?',
created_at: '2020-09-10T16:55:52.000Z',
emojis: [],
id: '9z1PvK4BuTE03YejNw',
unread: false
},
{
account_id: '9xUIiQfGrdPJDZe39s',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'Oh it&#39;s okay! No worries!',
created_at: '2020-09-10T16:55:44.000Z',
emojis: [],
id: '9z1Puc4hTDUJcOYW4O',
unread: false
},
{
account_id: '9xUj5WTmzSlFPN6OLg',
attachment: null,
card: null,
chat_id: '9y8e7ESoVUiKVMAoCW',
content: 'didn&#39;t mean to ignore you',
created_at: '2020-09-10T16:55:32.000Z',
emojis: [],
id: '9z1PtWGNamQTIvUspk',
unread: false
}
]

View file

@ -1,3 +1,5 @@
import userChats from './chat'
export let users = [
{ is_confirmed: true, is_approved: true, is_active: true, id: '2', nickname: 'allis', local: true, external: false, roles: { admin: true, moderator: false }, tags: [], actor_type: 'Person' },
{ is_confirmed: true, is_approved: true, is_active: true, id: '10', nickname: 'bob', local: true, external: false, roles: { admin: false, moderator: false }, tags: ['mrf_tag:sandbox'], actor_type: 'Person' },
@ -35,6 +37,10 @@ export async function fetchUserStatuses(id, authHost, godmode, token) {
return Promise.resolve({ data: { activities: userStatuses }})
}
export async function fetchUserChats(id, authHost, godmode, token) {
return Promise.resolve({ data: userChats })
}
export async function getPasswordResetToken(nickname, authHost, token) {
return Promise.resolve({ data: { token: 'g05lxnBJQnL', link: 'http://url/api/pleroma/password_reset/g05lxnBJQnL' }})
}

35
src/api/chat.js Normal file
View file

@ -0,0 +1,35 @@
import request from '@/utils/request'
import { getToken } from '@/utils/auth'
import { baseName } from './utils'
export async function deleteChatMessage(chat_id, message_id, authHost, token) {
return await request({
baseURL: baseName(authHost),
url: `/api/pleroma/admin/chats/${chat_id}/messages/${message_id}`,
method: 'delete',
headers: authHeaders(token)
})
}
export async function fetchChat(id, authHost, token) {
return await request({
baseURL: baseName(authHost),
url: `/api/pleroma/admin/chats/${id}`,
method: 'get',
headers: authHeaders(token)
})
}
export async function fetchChatMessages(id, maxId, authHost, token) {
const url = maxId
? `/api/pleroma/admin/chats/${id}/messages?max_id=${maxId}`
: `/api/pleroma/admin/chats/${id}/messages`
return await request({
baseURL: baseName(authHost),
url,
method: 'get',
headers: authHeaders(token)
})
}
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}

View file

@ -180,6 +180,15 @@ export async function fetchUserStatuses(id, authHost, godmode, token) {
})
}
export async function fetchUserChats(id, authHost, token) {
return await request({
baseURL: baseName(authHost),
url: `/api/pleroma/admin/users/${id}/chats`,
method: 'get',
headers: authHeaders(token)
})
}
export async function approveUserAccount(nicknames, authHost, token) {
return await request({
baseURL: baseName(authHost),

View file

@ -0,0 +1,233 @@
<template>
<el-card v-if="!message.deleted" class="message-card">
<div slot="header">
<div class="message-header">
<div class="message-meta">
<router-link
v-if="propertyExists(author, 'id')"
:to="{ name: 'UsersShow', params: { id: author.id }}"
class="router-link"
@click.native.stop>
<div class="message-author">
<img v-if="propertyExists(author, 'avatar')" :src="author.avatar" class="message-author-avatar-img">
<span v-if="propertyExists(author, 'username')" class="message-author-name">{{ author.username }}</span>
<span v-else>
<span v-if="propertyExists(author, 'username')" class="message-author-name">
{{ author.username }}
</span>
<span v-else class="message-author-name deactivated">({{ $t('users.invalidNickname') }})</span>
</span>
</div>
</router-link>
<span class="message-timestamp">{{ parseTimestamp(message.created_at) }}</span>
</div>
<div class="message-actions">
<el-dropdown trigger="click" @click.native.stop>
<el-button plain size="small" icon="el-icon-edit" class="status-actions-button">
{{ $t('reports.messageModeration') }}<i class="el-icon-arrow-down el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
@click.native="deleteMessage()">
{{ $t('reports.deleteMessage') }}
</el-dropdown-item>
<el-dropdown-item
@click.native="handleRouteChange()">
{{ $t('users.moderateUser') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</div>
<div class="message-body">
<span class="message-content" v-html="message.content"/>
<div v-if="message.attachment" class="image">
<img :src="message.attachment.preview_url">
</div>
</div>
</el-card>
</template>
<script>
import moment from 'moment'
export default {
name: 'ChatMessage',
props: {
message: {
type: Object,
required: true
},
author: {
type: Object,
required: true
},
page: {
type: Number,
required: false,
default: 0
}
},
data() {
return {
}
},
methods: {
propertyExists(account, property) {
return account[property]
},
parseTimestamp(timestamp) {
return moment(timestamp).format('YYYY-MM-DD HH:mm')
},
deleteMessage() {
this.$confirm('Are you sure you want to delete this message?', 'Warning', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
this.$store.dispatch('DeleteMessage', {
chat_id: this.message.chat_id,
message_id: this.message.id
})
this.$message({
type: 'success',
message: 'Delete completed'
})
}).catch(() => {
this.$message({
type: 'info',
message: 'Delete canceled'
})
})
},
handleRouteChange() {
this.$router.push({ name: 'UsersShow', params: { id: this.author.id }})
}
}
}
</script>
<style rel='stylesheet/scss' lang='scss'>
.message-card {
margin-bottom: 10px;
.account {
line-height: 26px;
font-size: 13px;
color: #606266;
}
.account:hover {
text-decoration: underline;
}
.deactivated {
color: gray;
line-height: 28px;
vertical-align: middle;
}
.image {
width: 20%;
img {
width: 100%;
}
}
.router-link {
text-decoration: none;
}
.show-more-button {
margin-left: 5px;
}
.message-author {
display: flex;
align-items: center;
}
.message-author-avatar-img {
display: inline-block;
width: 15px;
height: 15px;
margin-right: 5px;
}
.message-author-name {
display: inline-block;
margin: 0;
font-size: 15px;
font-weight: 500;
}
.message-body {
display: flex;
flex-direction: column;
}
.message-card-header {
display: flex;
align-items: center;
}
.chat-content {
font-size: 15px;
line-height: 26px;
}
.message-timestamp {
font-size: 13px;
color: #606266;
margin-left: 20px;
}
.message-deleted {
font-style: italic;
margin-top: 3px;
}
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
.message-meta {
display: flex;
justify-content: flex-start;
align-items: flex-end;
}
}
}
@media only screen and (max-width:480px) {
.el-message {
min-width: 80%;
}
.el-message-box {
width: 80%;
}
.message-card {
.el-card__header {
padding: 10px 17px;
}
.el-tag {
margin: 3px 0;
}
.message-author-container {
margin-bottom: 5px;
}
.message-action-buttons {
margin: 3px 0 3px;
}
.message-actions {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.message-header {
display: flex;
flex-direction: column;
align-items: flex-start;
}
}
.message-actions-button {
margin: 3px 0 3px;
}
.message-actions {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
}
</style>

View file

@ -67,6 +67,7 @@ export default {
reports: 'Reports',
invites: 'Invites',
statuses: 'Statuses',
chats: 'Chats',
settings: 'Settings',
moderationLog: 'Moderation Log',
relays: 'Relays',
@ -301,6 +302,11 @@ export default {
unlisted: 'Unlisted',
openStatusInInstance: 'Open status in instance'
},
chats: {
chats: 'Chats',
loadMore: 'Load more',
chatHistory: 'Chat History'
},
userProfile: {
tags: 'Tags',
moderator: 'Moderator',
@ -311,6 +317,7 @@ export default {
actorType: 'Actor Type',
nickname: 'Nickname',
recentStatuses: 'Recent Statuses',
chats: 'Chats',
roles: 'Roles',
active: 'Active',
status: 'Status',
@ -318,6 +325,7 @@ export default {
deactivated: 'Deactivated',
pending: 'Pending',
noStatuses: 'No statuses to show',
noChats: 'No chats to show',
noMessages: 'No messages to show',
openAccountInInstance: 'Open account in instance',
securitySettings: {

View file

@ -232,5 +232,17 @@ export const asyncRouterMap = [
],
hidden: true
},
{
path: '/chats/:id',
component: Layout,
children: [
{
path: '',
name: 'ChatsShow',
component: () => import('@/views/chats/show')
}
],
hidden: true
},
{ path: '*', redirect: '/404', hidden: true }
]

View file

@ -13,6 +13,7 @@ import relays from './modules/relays'
import reports from './modules/reports'
import settings from './modules/settings'
import status from './modules/status'
import chat from './modules/chat'
import tagsView from './modules/tagsView'
import user from './modules/user'
import userProfile from './modules/userProfile'
@ -34,6 +35,7 @@ const store = new Vuex.Store({
reports,
settings,
status,
chat,
tagsView,
user,
userProfile,

56
src/store/modules/chat.js Normal file
View file

@ -0,0 +1,56 @@
import { fetchChat, fetchChatMessages, deleteChatMessage } from '@/api/chat'
const chat = {
state: {
fetchedChat: {},
fetchedChatMessages: {},
loading: false,
buttonLoading: false,
allLoaded: false,
maxId: null
},
mutations: {
SET_LOADING: (state, chat) => {
state.loading = chat
},
SET_ALL_LOADED: (state, chat) => {
state.allLoaded = chat
},
SET_BUTTON_LOADING: (state, chat) => {
state.buttonLoading = chat
},
SET_CHAT: (state, chat) => {
state.fetchedChat = chat
},
SET_CHAT_MESSAGES: (state, chatMessages) => {
state.fetchedChatMessages = chatMessages
},
CHANGE_MAX_ID: (state, max_id) => {
state.maxId = max_id
}
},
actions: {
async FetchChat({ commit, dispatch, getters, state }, id) {
commit('SET_LOADING', true)
const chat = await fetchChat(id, getters.authHost, getters.token)
commit('SET_CHAT', chat.data)
commit('SET_LOADING', false)
},
async FetchChatMessages({ commit, dispatch, getters, state }, id, max_id) {
commit('SET_LOADING', true)
const chat = await fetchChatMessages(id, state.maxId, getters.authHost, getters.token)
commit('SET_CHAT_MESSAGES', chat.data)
commit('SET_LOADING', false)
},
async DeleteMessage({ commit, dispatch, getters, state }, params) {
await deleteChatMessage(params.chat_id, params.message_id, getters.authHost, getters.token)
dispatch('FetchChatMessages', params.chat_id)
},
HandlePageChange({ commit }, max_id) {
commit('CHANGE_MAX_ID', max_id)
}
}
}
export default chat

View file

@ -1,9 +1,11 @@
import { fetchUser, fetchUserStatuses, fetchUserCredentials, updateUserCredentials } from '@/api/users'
import { fetchUser, fetchUserStatuses, fetchUserChats, fetchUserCredentials, updateUserCredentials } from '@/api/users'
const userProfile = {
state: {
statuses: [],
statusesLoading: true,
chats: [],
chatsLoading: true,
user: {},
userCredentials: {},
userProfileLoading: true
@ -15,6 +17,12 @@ const userProfile = {
SET_STATUSES_LOADING: (state, status) => {
state.statusesLoading = status
},
SET_CHATS: (state, chats) => {
state.chats = chats
},
SET_CHATS_LOADING: (state, chat) => {
state.chatsLoading = chat
},
SET_USER: (state, user) => {
state.user = user
},
@ -34,6 +42,7 @@ const userProfile = {
commit('SET_USER_PROFILE_LOADING', false)
dispatch('FetchUserStatuses', { userId, godmode })
dispatch('FetchUserChats', { userId })
},
FetchUserStatuses({ commit, dispatch, getters }, { userId, godmode }) {
commit('SET_STATUSES_LOADING', true)
@ -43,6 +52,14 @@ const userProfile = {
commit('SET_STATUSES_LOADING', false)
},
FetchUserChats({ commit, dispatch, getters }, { userId }) {
commit('SET_CHATS_LOADING', true)
fetchUserChats(userId, getters.authHost, getters.token)
.then(chats => dispatch('SetChats', chats.data))
commit('SET_CHATS_LOADING', false)
},
async FetchUserCredentials({ commit, getters }, { nickname }) {
const userResponse = await fetchUserCredentials(nickname, getters.authHost, getters.token)
commit('SET_USER_CREDENTIALS', userResponse.data)
@ -50,6 +67,9 @@ const userProfile = {
SetStatuses({ commit }, statuses) {
commit('SET_STATUSES', statuses)
},
SetChats({ commit }, chats) {
commit('SET_CHATS', chats)
},
async UpdateUserCredentials({ dispatch, getters }, { nickname, credentials }) {
await updateUserCredentials(nickname, credentials, getters.authHost, getters.token)
dispatch('FetchUserCredentials', { nickname })

250
src/views/chats/show.vue Normal file
View file

@ -0,0 +1,250 @@
<template>
<div v-if="!loading" class="chat-show-container">
<header class="chat-page-header">
<h1>
{{ $t('chats.chatHistory') }}:
</h1>
<div class="chat-card-participants">
<div class="chat-particiants-sender">
<div class="avatar-name-container">
<el-avatar v-if="propertyExists(chat.sender, 'avatar')" :src="chat.sender.avatar" size="large" />
<h1 v-if="propertyExists(chat.sender, 'display_name')" class="particiant-display-name">{{ chat.sender.display_name }}</h1>
<h1 v-else class="particiant-display-name invalid">({{ $t('users.invalidNickname') }})</h1>
<a v-if="propertyExists(chat.sender, 'url')" :href="chat.sender.url" target="_blank">
<i :title="$t('userProfile.openAccountInInstance')" class="el-icon-top-right"/>
</a>
</div>
</div>
<div class="chat-particiants-receiver">
<div class="avatar-name-container">
<el-avatar v-if="propertyExists(chat.receiver, 'avatar')" :src="chat.receiver.avatar" size="large" />
<h1 v-if="propertyExists(chat.receiver, 'display_name')" class="particiant-display-name">{{ chat.receiver.display_name }}</h1>
<h1 v-else class="particiant-display-name invalid">({{ $t('users.invalidNickname') }})</h1>
<a v-if="propertyExists(chat.receiver, 'url')" :href="chat.receiver.url" target="_blank">
<i :title="$t('userProfile.openAccountInInstance')" class="el-icon-top-right"/>
</a>
</div>
</div>
</div>
</header>
<div v-if="isMobile" class="chat-page-header-container">
<header class="chat-page-header">
<div class="avatar-name-container"/>
<reboot-button/>
</header>
</div>
<div class="chat-messages-container">
<el-timeline v-if="!loading" class="messages">
<el-timeline-item v-for="message in chatMessages" :key="message.id">
<chat-message :message="message" :author="getAuthor(message.account_id)"/>
</el-timeline-item>
<p v-if="chatMessages.length === 0" class="no-messages">{{ $t('userProfile.noMessages') }}</p>
<div v-if="chatMessages.length === 20" class="statuses-pagination">
<el-button v-if="!allLoaded" :loading="buttonLoading" @click="handleLoadMore">{{ $t('statuses.loadMore') }}</el-button>
<el-button v-else icon="el-icon-check" circle/>
</div>
</el-timeline>
</div>
</div>
</template>
<script>
import ChatMessage from '@/components/ChatMessage'
import RebootButton from '@/components/RebootButton'
export default {
name: 'ChatShow',
components: { RebootButton, ChatMessage },
computed: {
isDesktop() {
return this.$store.state.app.device === 'desktop'
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
isTablet() {
return this.$store.state.app.device === 'tablet'
},
allLoaded() {
return this.$store.state.chat.allLoaded
},
buttonLoading() {
return this.$store.state.chat.buttonLoading
},
loading() {
return this.$store.state.chat.loading
},
chat() {
return this.$store.state.chat.fetchedChat
},
chatMessages() {
return this.$store.state.chat.fetchedChatMessages
}
},
beforeMount: function() {
this.$store.dispatch('HandlePageChange', null)
this.$store.dispatch('NeedReboot')
this.$store.dispatch('GetNodeInfo')
this.$store.dispatch('FetchChat', this.$route.params.id)
this.$store.dispatch('FetchChatMessages', this.$route.params.id)
},
methods: {
propertyExists(account, property) {
return account[property]
},
getAuthor(account_id) {
const sender = this.chat.sender
const receiver = this.chat.receiver
return account_id === sender.id ? sender : receiver
},
handleLoadMore() {
const max_id = this.chatMessages.pop().id
this.$store.dispatch('HandlePageChange', max_id)
this.$store.dispatch('FetchChatMessages', this.$route.params.id)
}
}
}
</script>
<style rel='stylesheet/scss' lang='scss'>
.chat-page-header {
display: flex;
margin: 22px 15px 22px 20px;
justify-content: center;
padding: 0;
h1 {
display: inline
}
}
.chat-card-participants {
display: flex;
margin: 0 20px;
}
.avatar-name-container {
padding-right: 20px;
display: flex;
align-items: center;
.el-icon-top-right {
font-size: 2em;
line-height: 36px;
color: #606266;
}
.particiant-display-name {
padding-left: 5px;
}
}
.el-avatar h1 {
padding-right: 5px;
}
.chat-messages-container {
display: flex;
flex-direction: column;
align-items: center;
}
.messages.el-timeline {
padding: 0;
}
.no-chats {
margin-left: 28px;
color: #606266;
}
.reboot-button {
padding: 10px;
margin-left: 6px;
}
.router-link {
text-decoration: none;
}
.chat-container {
margin: 0 15px 0 20px;
}
.chats {
padding: 0 20px 0 0;
}
.statuses-pagination {
padding: 15px 0;
text-align: center;
}
@media only screen and (max-width:480px) {
.chat-page-header {
padding: 0;
margin: 7px 15px 15px 30px;
flex-direction: column;
align-items: baseline;
h1 {
margin-left: 0;
margin-bottom: 10px;
}
}
.chat-card-participants {
display: flex;
margin: 0;
flex-wrap: wrap;
}
.avatar-name-container {
margin-bottom: 10px;
}
.messages.el-timeline {
padding-left: 10px;
}
.el-timeline-item__wrapper {
padding-left: 18px;
}
.message-card .el-card__header .message-header {
display: flex;
flex-direction: row;
width: 100%;
justify-content: space-between;
align-items: center;
.message-actions {
width: auto;
}
}
.message-card .message-header .message-meta {
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
.message-timestamp {
margin-left: 0;
}
}
}
@media only screen and (max-width: 1051px) {
.messages.el-timeline {
max-width: 90%;
}
}
@media only screen and (max-width:801px) and (min-width: 481px) {
.chat-page-header {
padding: 0;
margin: 7px 15px 15px 30px;
flex-direction: column;
align-items: baseline;
h1 {
margin-left: 0;
margin-bottom: 10px;
}
}
.chat-card-participants {
display: flex;
margin: 0;
flex-wrap: wrap;
}
.avatar-name-container {
margin-bottom: 10px;
}
.messages.el-timeline {
padding-left: 10px;
max-width: 97%;
}
.el-timeline-item__wrapper {
padding-left: 18px;
}
}
</style>

View file

@ -18,6 +18,10 @@
<setting :setting-group="assets" :data="assetsData"/>
</el-form>
<el-divider v-if="assets" class="divider thick-line"/>
<el-form :model="chatData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="chat" :data="chatData"/>
</el-form>
<el-divider v-if="chat" class="divider thick-line"/>
<el-form :model="markupData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="markup" :data="markupData"/>
</el-form>
@ -51,6 +55,12 @@ export default {
assetsData() {
return _.get(this.settings.settings, [':pleroma', ':assets']) || {}
},
chat() {
return this.settings.description.find(setting => setting.key === ':chat')
},
chatData() {
return _.get(this.settings.settings, [':pleroma', ':chat']) || {}
},
frontend() {
return this.settings.description.find(setting => setting.key === ':frontend_configurations')
},

View file

@ -8,6 +8,10 @@ export const tabs = description => {
label: 'settings.auth',
settings: [':auth', ':ldap', ':oauth2', 'Pleroma.Web.Auth.Authenticator']
},
'esshd': {
label: 'settings.esshd',
settings: [':esshd']
},
'captcha': {
label: 'settings.captcha',
settings: ['Pleroma.Captcha', 'Pleroma.Captcha.Kocaptcha']
@ -18,7 +22,11 @@ export const tabs = description => {
},
'frontend': {
label: 'settings.frontend',
settings: [':assets', ':frontends', ':emoji', ':frontend_configurations', ':markup', ':static_fe', 'Pleroma.Web.Preload']
settings: [':assets', ':chat', ':frontends', ':emoji', ':frontend_configurations', ':markup', ':static_fe', 'Pleroma.Web.Preload']
},
'gopher': {
label: 'settings.gopher',
settings: [':gopher']
},
'http': {
label: 'settings.http',

View file

@ -110,6 +110,40 @@
:visible="securitySettingsModalVisible"
@close="securitySettingsModalVisible = false" />
</el-card>
<el-card class="user-chats-card">
<h2 class="chats">{{ $t('userProfile.chats') }}</h2>
<div class="el-table el-table--fit el-table--enable-row-hover el-table--enable-row-transition el-table--medium">
<table class="user-chats-table">
<tbody v-if="!chatsLoading" class="chats">
<tr v-if="chats.length === 0" class="no-statuses">
{{ $t('userProfile.noChats') }}
</tr>
<tr v-for="chat in chats" :key="chat.id" class="el-table__row chat-item">
<td>
<router-link
v-if="propertyExists(chat, 'id')"
:to="{ name: 'ChatsShow', params: { id: chat.id }}"
class="router-link">
<div class="chat-card-header">
<img v-if="propertyExists(chat.receiver, 'avatar')" :src="chat.receiver.avatar" class="chat-avatar-img">
<span v-if="propertyExists(chat.receiver, 'username')" class="chat-account-name">{{ chat.receiver.username }}</span>
<span v-else>
<span v-if="propertyExists(chat.receiver, 'username')" class="chat-account-name">
{{ chat.receiver.username }}
</span>
<span v-else class="chat-account-name deactivated">({{ $t('users.invalidNickname') }})</span>
</span>
</div>
<div class="chat-card-preview">
<span v-if="propertyExists(chat, 'last_message')" class="chat-preview">{{ chat.last_message.content }}</span>
</div>
</router-link>
</td>
</tr>
</tbody>
</table>
</div>
</el-card>
</div>
<div class="recent-statuses-container">
<h2 class="recent-statuses">{{ $t('userProfile.recentStatuses') }}</h2>
@ -163,6 +197,12 @@ export default {
statusesLoading() {
return this.$store.state.userProfile.statusesLoading
},
chats() {
return this.$store.state.userProfile.chats
},
chatsLoading() {
return this.$store.state.userProfile.chatsLoading
},
user() {
return this.$store.state.userProfile.user
},
@ -329,6 +369,11 @@ table {
width: auto;
margin-bottom: 20px;
}
.user-chats-card {
width: auto;
height: fit-content;
margin-bottom: 20px;
}
.user-profile-container {
display: flex;
}
@ -336,6 +381,9 @@ table {
margin: 0;
width: inherit;
}
.user-chats-table {
width: 100%;
}
.user-profile-tag {
margin: 0 4px 4px 0;
}
@ -344,6 +392,27 @@ table {
font-weight: bold;
margin: 5px 0;
}
.chat-card-header {
display: flex;
align-items: center;
}
.chat-avatar-img {
display: inline-block;
width: 15px;
height: 15px;
margin-right: 5px;
}
.chat-account-name {
display: inline-block;
margin: 0;
font-size: 15px;
font-weight: 500;
}
.chat-card-preview {
color: gray;
font-style: italic;
margin: 5px 0 0 20px;
}
@media only screen and (max-width:480px) {
.avatar-name-container {
@ -382,7 +451,7 @@ table {
margin: 0 15px 15px 10px;
}
}
.user-profile-card {
.user-profile-card, .user-chats-card {
margin: 0 10px 20px;
width: 95%;
td {

View file

@ -0,0 +1,51 @@
import Vuex from 'vuex'
import { mount, createLocalVue, config } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import Element from 'element-ui'
import ChatShow from '@/views/chats/show'
import { storeConfig } from './store.conf'
import { cloneDeep } from 'lodash'
config.mocks["$t"] = () => {}
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Element)
const $route = {
params: {
id: '9y8e7ESoVUiKVMAoCW'
}
}
jest.mock('@/api/nodeInfo')
jest.mock('@/api/chat')
describe('Chat view', () => {
let store
beforeEach(() => {
store = new Vuex.Store(cloneDeep(storeConfig))
})
it('fetches chat messages', async (done) => {
const wrapper = mount(ChatShow, {
store,
localVue,
sync: false,
stubs: ['router-link'],
mocks: {
$route
}
})
await flushPromises()
expect(wrapper.find('.message-card').isVisible()).toBe(true)
expect(wrapper.find('.chat-particiants-sender').isVisible()).toBe(true)
expect(wrapper.find('.chat-particiants-receiver').isVisible()).toBe(true)
expect(store.state.chat.fetchedChat.receiver.acct).toBe('test10')
expect(store.state.chat.fetchedChatMessages[0].chat_id).toBe('9y8e7ESoVUiKVMAoCW')
expect(store.state.chat.maxId).toBe(null)
done()
})
})

View file

@ -0,0 +1,25 @@
import app from '@/store/modules/app'
import settings from '@/store/modules/settings'
import user from '@/store/modules/user'
import chat from '@/store/modules/chat'
import getters from '@/store/getters'
export const storeConfig = {
modules: {
app,
settings,
user,
chat
},
getters
}
export const storeWithTagPolicy = {
modules: {
app,
settings,
user,
chat
},
getters
}