Merge remote-tracking branch 'upstream/develop' into shigusegubu
* upstream/develop: Update CHANGELOG.md Update CHANGELOG.md Apply suggestion to src/modules/instance.js Fix/popover performance make theme loading work with source-only presets remove bloat from themes update using variables Apply suggestion to static/themes/pleroma-dark.json Apply suggestion to static/themes/pleroma-dark.json Apply suggestion to static/themes/pleroma-dark.json Apply suggestion to static/themes/pleroma-dark.json make panel header highlight less harsh update pleroma-dark and pleroma-light stop using customTheme in user card, instead use color slots. fix for opacity inheritance polluting inheritors Fix user activation/deactivation, deletion, and role assignment in the moderation menu
This commit is contained in:
commit
6595083003
12 changed files with 459 additions and 29 deletions
|
@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.0.0] - 2020-02-28
|
||||
### Added
|
||||
- Tons of color slots including ones for hover/pressed/toggled buttons
|
||||
- Experimental `--variable[,mod]` syntax support for color slots in themes. the `mod` makes color brighter/darker depending on background color (makes darker color brighter/darker depending on background color)
|
||||
|
@ -16,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Emoji reactions for statuses
|
||||
- MRF keyword policy disclosure
|
||||
### Changed
|
||||
- Updated Pleroma default themes
|
||||
- theme engine update to 3 (themes v2.1 introduction)
|
||||
- massive internal changes in theme engine - slowly away from "generate things separately with spaghetti code" towards "feed all data into single 'generateTheme' function and declare slot inheritance and all in a separate file"
|
||||
- Breezy theme updates to make it closer to actual Breeze in some aspects
|
||||
|
|
|
@ -88,6 +88,8 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
--btnText: var(--popoverText, $fallback--text);
|
||||
|
||||
&-icon {
|
||||
padding-left: 0.5rem;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ 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 { hex2rgb } from '../../services/color_convert/color_convert.js'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
|
@ -30,21 +29,11 @@ export default {
|
|||
}]
|
||||
},
|
||||
style () {
|
||||
const color = this.$store.getters.mergedConfig.customTheme.colors
|
||||
? this.$store.getters.mergedConfig.customTheme.colors.bg // v2
|
||||
: this.$store.getters.mergedConfig.colors.bg // v1
|
||||
|
||||
if (color) {
|
||||
const rgb = (typeof color === 'string') ? hex2rgb(color) : color
|
||||
const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)`
|
||||
|
||||
return {
|
||||
backgroundColor: `rgb(${Math.floor(rgb.r * 0.53)}, ${Math.floor(rgb.g * 0.56)}, ${Math.floor(rgb.b * 0.59)})`,
|
||||
backgroundImage: [
|
||||
`linear-gradient(to bottom, ${tintColor}, ${tintColor})`,
|
||||
`url(${this.user.cover_photo})`
|
||||
].join(', ')
|
||||
}
|
||||
return {
|
||||
backgroundImage: [
|
||||
`linear-gradient(to bottom, var(--profileTint), var(--profileTint))`,
|
||||
`url(${this.user.cover_photo})`
|
||||
].join(', ')
|
||||
}
|
||||
},
|
||||
isOtherUser () {
|
||||
|
|
|
@ -286,6 +286,7 @@
|
|||
mask-size: 100% 60%;
|
||||
border-top-left-radius: calc(var(--panelRadius) - 1px);
|
||||
border-top-right-radius: calc(var(--panelRadius) - 1px);
|
||||
background-color: var(--profileBg);
|
||||
|
||||
&.hide-bio {
|
||||
mask-size: 100% 40px;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { set } from 'vue'
|
||||
import { getPreset, applyTheme } from '../services/style_setter/style_setter.js'
|
||||
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
|
||||
import { instanceDefaultProperties } from './config.js'
|
||||
|
||||
const defaultState = {
|
||||
|
@ -160,7 +161,14 @@ const instance = {
|
|||
// No need to apply theme if there's user theme already
|
||||
const { customTheme } = rootState.config
|
||||
if (customTheme) return
|
||||
applyTheme(themeData.theme)
|
||||
|
||||
// New theme presets don't have 'theme' property, they use 'source'
|
||||
const themeSource = themeData.source
|
||||
if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) {
|
||||
applyTheme(themeSource)
|
||||
} else {
|
||||
applyTheme(themeData.theme)
|
||||
}
|
||||
})
|
||||
},
|
||||
fetchEmoji ({ dispatch, state }) {
|
||||
|
|
|
@ -374,9 +374,9 @@ const users = {
|
|||
return rootState.api.backendInteractor.unsubscribeUser({ id })
|
||||
.then((relationship) => commit('updateUserRelationship', [relationship]))
|
||||
},
|
||||
toggleActivationStatus ({ rootState, commit }, user) {
|
||||
toggleActivationStatus ({ rootState, commit }, { user }) {
|
||||
const api = user.deactivated ? rootState.api.backendInteractor.activateUser : rootState.api.backendInteractor.deactivateUser
|
||||
api(user)
|
||||
api({ user })
|
||||
.then(({ deactivated }) => commit('updateActivationStatus', { user, deactivated }))
|
||||
},
|
||||
registerPushNotifications (store) {
|
||||
|
|
|
@ -402,8 +402,8 @@ const fetchStatus = ({ id, credentials }) => {
|
|||
.then((data) => parseStatus(data))
|
||||
}
|
||||
|
||||
const tagUser = ({ tag, credentials, ...options }) => {
|
||||
const screenName = options.screen_name
|
||||
const tagUser = ({ tag, credentials, user }) => {
|
||||
const screenName = user.screen_name
|
||||
const form = {
|
||||
nicknames: [screenName],
|
||||
tags: [tag]
|
||||
|
@ -419,8 +419,8 @@ const tagUser = ({ tag, credentials, ...options }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const untagUser = ({ tag, credentials, ...options }) => {
|
||||
const screenName = options.screen_name
|
||||
const untagUser = ({ tag, credentials, user }) => {
|
||||
const screenName = user.screen_name
|
||||
const body = {
|
||||
nicknames: [screenName],
|
||||
tags: [tag]
|
||||
|
@ -436,7 +436,7 @@ const untagUser = ({ tag, credentials, ...options }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const addRight = ({ right, credentials, ...user }) => {
|
||||
const addRight = ({ right, credentials, user }) => {
|
||||
const screenName = user.screen_name
|
||||
|
||||
return fetch(PERMISSION_GROUP_URL(screenName, right), {
|
||||
|
@ -446,7 +446,7 @@ const addRight = ({ right, credentials, ...user }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const deleteRight = ({ right, credentials, ...user }) => {
|
||||
const deleteRight = ({ right, credentials, user }) => {
|
||||
const screenName = user.screen_name
|
||||
|
||||
return fetch(PERMISSION_GROUP_URL(screenName, right), {
|
||||
|
@ -478,7 +478,7 @@ const deactivateUser = ({ credentials, user: { screen_name: nickname } }) => {
|
|||
}).then(response => get(response, 'users.0'))
|
||||
}
|
||||
|
||||
const deleteUser = ({ credentials, ...user }) => {
|
||||
const deleteUser = ({ credentials, user }) => {
|
||||
const screenName = user.screen_name
|
||||
const headers = authHeaders(credentials)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ export const LAYERS = {
|
|||
undelay: null, // root
|
||||
topBar: null, // no transparency support
|
||||
badge: null, // no transparency support
|
||||
profileTint: null, // doesn't matter
|
||||
fg: null,
|
||||
bg: 'underlay',
|
||||
highlight: 'bg',
|
||||
|
@ -29,6 +30,7 @@ export const LAYERS = {
|
|||
* this allows redefining it to something else
|
||||
*/
|
||||
export const DEFAULT_OPACITY = {
|
||||
profileTint: 0.5,
|
||||
alert: 0.5,
|
||||
input: 0.5,
|
||||
faint: 0.5,
|
||||
|
@ -119,6 +121,20 @@ export const SLOT_INHERITANCE = {
|
|||
cGreen: '#00FF00',
|
||||
cOrange: '#E3FF00',
|
||||
|
||||
profileBg: {
|
||||
depends: ['bg'],
|
||||
color: (mod, bg) => ({
|
||||
r: Math.floor(bg.r * 0.53),
|
||||
g: Math.floor(bg.g * 0.56),
|
||||
b: Math.floor(bg.b * 0.59)
|
||||
})
|
||||
},
|
||||
profileTint: {
|
||||
depends: ['bg'],
|
||||
layer: 'profileTint',
|
||||
opacity: 'profileTint'
|
||||
},
|
||||
|
||||
highlight: {
|
||||
depends: ['bg'],
|
||||
color: (mod, bg) => brightness(5 * mod, bg).rgb
|
||||
|
|
|
@ -351,7 +351,8 @@ export const getColors = (sourceColors, sourceOpacity) => SLOT_ORDERED.reduce(({
|
|||
throw new Error('Couldn\'t generate color for ' + key)
|
||||
}
|
||||
const opacitySlot = getOpacitySlot(key)
|
||||
if (opacitySlot && outputColor.a === undefined) {
|
||||
const ownOpacitySlot = value.opacity
|
||||
if (opacitySlot && (outputColor.a === undefined || ownOpacitySlot)) {
|
||||
const dependencySlot = deps[0]
|
||||
if (dependencySlot && colors[dependencySlot] === 'transparent') {
|
||||
outputColor.a = 0
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"sigsegv": [ "シグセグV", "#100f32", "#221548", "#e6fcff", "#fe9df8", "#fd3f3f", "#cafeb8", "#9fd3fe", "#ffe96b" ],
|
||||
"sigsegv2": [ "SigSeg部", "#003238", "#00616c", "#e8f9fb", "#81ffff", "#ff7b66", "#4ae619", "#00ddff", "#ccef53" ],
|
||||
"pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"pleroma-dark": "/static/themes/pleroma-dark.json",
|
||||
"pleroma-light": "/static/themes/pleroma-light.json",
|
||||
"pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"],
|
||||
"classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
|
||||
|
|
191
static/themes/pleroma-dark.json
Normal file
191
static/themes/pleroma-dark.json
Normal file
|
@ -0,0 +1,191 @@
|
|||
{
|
||||
"_pleroma_theme_version": 2,
|
||||
"name": "Pleroma Dark",
|
||||
"source": {
|
||||
"themeEngineVersion": 3,
|
||||
"fonts": {},
|
||||
"shadows": {
|
||||
"buttonHover": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"blur": "1",
|
||||
"spread": "2",
|
||||
"color": "#b9b9ba",
|
||||
"alpha": "0.4",
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#FFFFFF",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
}
|
||||
],
|
||||
"buttonPressed": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"blur": 4,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": 1,
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#FFFFFF",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"blur": "2",
|
||||
"spread": 0,
|
||||
"inset": false,
|
||||
"color": "#000000",
|
||||
"alpha": 1
|
||||
}
|
||||
],
|
||||
"panelHeader": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": "1",
|
||||
"blur": "3",
|
||||
"spread": 0,
|
||||
"inset": false,
|
||||
"color": "#000000",
|
||||
"alpha": "0.4"
|
||||
},
|
||||
{
|
||||
"x": "0",
|
||||
"y": "1",
|
||||
"blur": "0",
|
||||
"spread": 0,
|
||||
"inset": true,
|
||||
"color": "#ffffff",
|
||||
"alpha": "0.2"
|
||||
}
|
||||
],
|
||||
"panel": [
|
||||
{
|
||||
"x": "0",
|
||||
"y": "0",
|
||||
"blur": "3",
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": "0.5"
|
||||
},
|
||||
{
|
||||
"x": "0",
|
||||
"y": "4",
|
||||
"blur": "6",
|
||||
"spread": "3",
|
||||
"inset": false,
|
||||
"color": "#000000",
|
||||
"alpha": "0.3"
|
||||
}
|
||||
],
|
||||
"button": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"blur": 2,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": 1
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#FFFFFF",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
}
|
||||
],
|
||||
"topBar": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": "1",
|
||||
"blur": 4,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": "0.4"
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": "2",
|
||||
"blur": "7",
|
||||
"spread": 0,
|
||||
"inset": false,
|
||||
"color": "#000000",
|
||||
"alpha": "0.3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"opacity": {
|
||||
"underlay": "0.6"
|
||||
},
|
||||
"colors": {
|
||||
"bg": "#0f161e",
|
||||
"fg": "#151e2b",
|
||||
"text": "#b9b9ba",
|
||||
"underlay": "#090e14",
|
||||
"accent": "#e2b188",
|
||||
"cBlue": "#81beea",
|
||||
"cRed": "#d31014",
|
||||
"cGreen": "#5dc94a",
|
||||
"cOrange": "#ffc459",
|
||||
"border": "--fg,3",
|
||||
"topBarText": "--text,-9.75",
|
||||
"topBarLink": "--topBarText",
|
||||
"btnToggled": "--accent,-24.2",
|
||||
"alertErrorText": "--text,21.2",
|
||||
"badgeNotification": "#e15932",
|
||||
"badgeNotificationText": "#ffffff"
|
||||
},
|
||||
"radii": {
|
||||
"btn": "3",
|
||||
"input": "3",
|
||||
"panel": "3",
|
||||
"avatar": "3",
|
||||
"attachment": "3"
|
||||
}
|
||||
}
|
||||
}
|
219
static/themes/pleroma-light.json
Normal file
219
static/themes/pleroma-light.json
Normal file
|
@ -0,0 +1,219 @@
|
|||
{
|
||||
"_pleroma_theme_version": 2,
|
||||
"name": "Pleroma Light",
|
||||
"source": {
|
||||
"themeEngineVersion": 3,
|
||||
"fonts": {},
|
||||
"shadows": {
|
||||
"button": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"blur": 2,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": "0.2"
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#FFFFFF",
|
||||
"alpha": "0.5",
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
}
|
||||
],
|
||||
"buttonHover": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"blur": "2",
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": "0.2"
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": "0",
|
||||
"blur": "1",
|
||||
"spread": "2",
|
||||
"color": "#ffc39f",
|
||||
"alpha": "1",
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
}
|
||||
],
|
||||
"input": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#FFFFFF",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"blur": "2",
|
||||
"inset": true,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": "0.15"
|
||||
}
|
||||
],
|
||||
"panel": [
|
||||
{
|
||||
"x": "0",
|
||||
"y": 1,
|
||||
"blur": "3",
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": "0.5"
|
||||
},
|
||||
{
|
||||
"x": "0",
|
||||
"y": "3",
|
||||
"blur": "6",
|
||||
"spread": "1",
|
||||
"inset": false,
|
||||
"color": "#000000",
|
||||
"alpha": "0.2"
|
||||
}
|
||||
],
|
||||
"panelHeader": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": "1",
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"inset": true,
|
||||
"color": "#ffffff",
|
||||
"alpha": "0.5"
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": "1",
|
||||
"blur": "3",
|
||||
"spread": 0,
|
||||
"inset": false,
|
||||
"color": "#000000",
|
||||
"alpha": "0.3"
|
||||
}
|
||||
],
|
||||
"buttonPressed": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"blur": 4,
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": "0.2"
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"blur": "1",
|
||||
"spread": "2",
|
||||
"color": "#000000",
|
||||
"alpha": "0.3",
|
||||
"inset": true
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": -1,
|
||||
"blur": 0,
|
||||
"spread": 0,
|
||||
"color": "#FFFFFF",
|
||||
"alpha": 0.2,
|
||||
"inset": true
|
||||
}
|
||||
],
|
||||
"popup": [
|
||||
{
|
||||
"x": "1",
|
||||
"y": "2",
|
||||
"blur": "2",
|
||||
"spread": 0,
|
||||
"color": "#000000",
|
||||
"alpha": "0.2"
|
||||
},
|
||||
{
|
||||
"x": "1",
|
||||
"y": "3",
|
||||
"blur": "7",
|
||||
"spread": "0",
|
||||
"inset": false,
|
||||
"color": "#000000",
|
||||
"alpha": "0.2"
|
||||
}
|
||||
],
|
||||
"avatarStatus": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": "1",
|
||||
"blur": "4",
|
||||
"spread": "0",
|
||||
"inset": false,
|
||||
"color": "#000000",
|
||||
"alpha": "0.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"opacity": {
|
||||
"underlay": "0.4"
|
||||
},
|
||||
"colors": {
|
||||
"bg": "#f2f6f9",
|
||||
"fg": "#d6dfed",
|
||||
"text": "#304055",
|
||||
"underlay": "#5d6086",
|
||||
"accent": "#f55b1b",
|
||||
"cBlue": "#0095ff",
|
||||
"cRed": "#d31014",
|
||||
"cGreen": "#0fa00f",
|
||||
"cOrange": "#ffa500",
|
||||
"border": "#d8e6f9",
|
||||
"topBarText": "#304055",
|
||||
"topBarLink": "--topBarText",
|
||||
"btnToggled": "--accent,-24.2",
|
||||
"input": "#dee3ed",
|
||||
"badgeNotification": "#e83802"
|
||||
},
|
||||
"radii": {
|
||||
"btn": "3",
|
||||
"input": "3",
|
||||
"panel": "3",
|
||||
"avatar": "3",
|
||||
"attachment": "3"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue