Merge branch 'develop' of git.pleroma.social:pleroma/pleroma-fe into feature/custom-theme
This commit is contained in:
commit
e13c8c3fd2
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
> A single column frontend for both Pleroma and GS servers.
|
> A single column frontend for both Pleroma and GS servers.
|
||||||
|
|
||||||
![screenshot](https://my.mixtape.moe/kjzioz.PNG)
|
![screenshot](https://i.imgur.com/DJVqSJ0.png)
|
||||||
|
|
||||||
# For Translators
|
# For Translators
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { reduce, find, filter, sortBy } from 'lodash'
|
import { reduce, filter, sortBy } from 'lodash'
|
||||||
import { statusType } from '../../modules/statuses.js'
|
import { statusType } from '../../modules/statuses.js'
|
||||||
import Status from '../status/status.vue'
|
import Status from '../status/status.vue'
|
||||||
|
|
||||||
|
@ -10,12 +10,7 @@ const sortAndFilterConversation = (conversation) => {
|
||||||
const conversation = {
|
const conversation = {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
highlight: null,
|
highlight: null
|
||||||
preview: {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
status: null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
|
@ -86,15 +81,6 @@ const conversation = {
|
||||||
},
|
},
|
||||||
setHighlight (id) {
|
setHighlight (id) {
|
||||||
this.highlight = Number(id)
|
this.highlight = Number(id)
|
||||||
},
|
|
||||||
setPreview (id, x, y) {
|
|
||||||
if (id) {
|
|
||||||
this.preview.x = x
|
|
||||||
this.preview.y = y
|
|
||||||
this.preview.status = find(this.conversation, { id: id })
|
|
||||||
} else {
|
|
||||||
this.preview.status = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,48 +8,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
<status v-for="status in conversation" @goto="setHighlight" :key="status.id" @preview="setPreview" :statusoid="status" :expandable='false' :focused="focused(status.id)" :inConversation='true' :highlight="highlight" :replies="getReplies(status.id)"></status>
|
<status v-for="status in conversation" @goto="setHighlight" :key="status.id" :statusoid="status" :expandable='false' :focused="focused(status.id)" :inConversation='true' :highlight="highlight" :replies="getReplies(status.id)"></status>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status-preview base00-background base03-border" :style="{ left: preview.x + 'px', top: preview.y + 'px'}" v-if="preview.status">
|
|
||||||
<img class="avatar" :src="preview.status.user.profile_image_url_original">
|
|
||||||
<div class="text">
|
|
||||||
<h4>
|
|
||||||
{{ preview.status.user.name }}
|
|
||||||
<small><a>{{ preview.status.user.screen_name}}</a></small>
|
|
||||||
</h4>
|
|
||||||
<div @click.prevent="linkClicked" class="status-content" v-html="preview.status.statusnet_html"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./conversation.js"></script>
|
<script src="./conversation.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.status-preview {
|
|
||||||
position: absolute;
|
|
||||||
max-width: 35em;
|
|
||||||
padding: 0.5em;
|
|
||||||
display: flex;
|
|
||||||
border-color: inherit;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 1px;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
|
||||||
.avatar {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.text {
|
|
||||||
h4 {
|
|
||||||
margin-bottom: 0.4em;
|
|
||||||
small {
|
|
||||||
font-weight: lighter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
padding: 0 0.5em 0.5em 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import RetweetButton from '../retweet_button/retweet_button.vue'
|
||||||
import DeleteButton from '../delete_button/delete_button.vue'
|
import DeleteButton from '../delete_button/delete_button.vue'
|
||||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
import UserCardContent from '../user_card_content/user_card_content.vue'
|
import UserCardContent from '../user_card_content/user_card_content.vue'
|
||||||
import { filter } from 'lodash'
|
import { filter, find } from 'lodash'
|
||||||
|
|
||||||
const Status = {
|
const Status = {
|
||||||
props: [
|
props: [
|
||||||
|
@ -20,7 +20,9 @@ const Status = {
|
||||||
replying: false,
|
replying: false,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
unmuted: false,
|
unmuted: false,
|
||||||
userExpanded: false
|
userExpanded: false,
|
||||||
|
preview: null,
|
||||||
|
showPreview: false
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
muteWords () {
|
muteWords () {
|
||||||
|
@ -90,7 +92,9 @@ const Status = {
|
||||||
},
|
},
|
||||||
gotoOriginal (id) {
|
gotoOriginal (id) {
|
||||||
// only handled by conversation, not status_or_conversation
|
// only handled by conversation, not status_or_conversation
|
||||||
|
if (this.inConversation) {
|
||||||
this.$emit('goto', id)
|
this.$emit('goto', id)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
toggleExpanded () {
|
toggleExpanded () {
|
||||||
this.$emit('toggleExpanded')
|
this.$emit('toggleExpanded')
|
||||||
|
@ -102,13 +106,25 @@ const Status = {
|
||||||
this.userExpanded = !this.userExpanded
|
this.userExpanded = !this.userExpanded
|
||||||
},
|
},
|
||||||
replyEnter (id, event) {
|
replyEnter (id, event) {
|
||||||
if (this.$store.state.config.hoverPreview) {
|
this.showPreview = true
|
||||||
let rect = event.target.getBoundingClientRect()
|
const targetId = Number(id)
|
||||||
this.$emit('preview', Number(id), rect.left + 20, rect.top + 20 + window.pageYOffset)
|
const statuses = this.$store.state.statuses.allStatuses
|
||||||
|
|
||||||
|
if (!this.preview) {
|
||||||
|
// if we have the status somewhere already
|
||||||
|
this.preview = find(statuses, { 'id': targetId })
|
||||||
|
// or if we have to fetch it
|
||||||
|
if (!this.preview) {
|
||||||
|
this.$store.state.api.backendInteractor.fetchStatus({id}).then((status) => {
|
||||||
|
this.preview = status
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (this.preview.id !== targetId) {
|
||||||
|
this.preview = find(statuses, { 'id': targetId })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replyLeave () {
|
replyLeave () {
|
||||||
this.$emit('preview', 0, 0, 0)
|
this.showPreview = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
{{status.in_reply_to_screen_name}}
|
{{status.in_reply_to_screen_name}}
|
||||||
</router-link>
|
</router-link>
|
||||||
</small>
|
</small>
|
||||||
<template v-if="isReply && !expandable">
|
<template v-if="isReply">
|
||||||
<small>
|
<small>
|
||||||
<a href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"><i class="icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i></a>
|
<a href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"><i class="icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i></a>
|
||||||
</small>
|
</small>
|
||||||
|
@ -83,6 +83,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="status-preview base00-background base03-border" v-if="showPreview && preview">
|
||||||
|
<img class="avatar" :src="preview.user.profile_image_url_original">
|
||||||
|
<div class="text">
|
||||||
|
<h4>
|
||||||
|
{{ preview.user.name }}
|
||||||
|
<small><a>{{ preview.user.screen_name}}</a></small>
|
||||||
|
</h4>
|
||||||
|
<div @click.prevent="linkClicked" class="status-content" v-html="preview.statusnet_html"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-preview status-preview-loading base00-background base03-border" v-else-if="showPreview">
|
||||||
|
<i class="fa icon-spin4 animate-spin"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
|
<div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
|
||||||
|
|
||||||
<div v-if='status.attachments' class='attachments'>
|
<div v-if='status.attachments' class='attachments'>
|
||||||
|
@ -122,6 +136,43 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-preview {
|
||||||
|
position: absolute;
|
||||||
|
max-width: 34em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: flex;
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-left: 1em;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 0.4em;
|
||||||
|
small {
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
padding: 0 0.5em 0.5em 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-preview-loading {
|
||||||
|
display: block;
|
||||||
|
font-size: 2em;
|
||||||
|
min-width: 8em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.status-el {
|
.status-el {
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
|
|
|
@ -60,6 +60,7 @@ const Timeline = {
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('scroll', this.scrollLoad)
|
window.removeEventListener('scroll', this.scrollLoad)
|
||||||
|
this.$store.commit('setLoading', { timeline: this.timelineName, value: false })
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showNewStatuses () {
|
showNewStatuses () {
|
||||||
|
|
|
@ -7,6 +7,7 @@ const UserFinder = {
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
findUser (username) {
|
findUser (username) {
|
||||||
|
username = username[0] === '@' ? username.slice(1) : username
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.$store.state.api.backendInteractor.externalProfile(username)
|
this.$store.state.api.backendInteractor.externalProfile(username)
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
|
|
|
@ -5,6 +5,9 @@ const UserProfile = {
|
||||||
created () {
|
created () {
|
||||||
this.$store.commit('clearTimeline', { timeline: 'user' })
|
this.$store.commit('clearTimeline', { timeline: 'user' })
|
||||||
this.$store.dispatch('startFetching', ['user', this.userId])
|
this.$store.dispatch('startFetching', ['user', this.userId])
|
||||||
|
if (!this.$store.state.users.usersObject[this.userId]) {
|
||||||
|
this.$store.dispatch('fetchUser', this.userId)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
this.$store.dispatch('stopFetching', 'user')
|
this.$store.dispatch('stopFetching', 'user')
|
||||||
|
@ -18,7 +21,7 @@ const UserProfile = {
|
||||||
if (this.timeline.statuses[0]) {
|
if (this.timeline.statuses[0]) {
|
||||||
return this.timeline.statuses[0].user
|
return this.timeline.statuses[0].user
|
||||||
} else {
|
} else {
|
||||||
return false
|
return this.$store.state.users.usersObject[this.userId] || false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,6 +9,8 @@ const de = {
|
||||||
follows_you: 'Folgt dir!',
|
follows_you: 'Folgt dir!',
|
||||||
following: 'Folgst du!',
|
following: 'Folgst du!',
|
||||||
follow: 'Folgen',
|
follow: 'Folgen',
|
||||||
|
blocked: 'Blockiert!',
|
||||||
|
block: 'Blockieren',
|
||||||
statuses: 'Beiträge',
|
statuses: 'Beiträge',
|
||||||
mute: 'Stummschalten',
|
mute: 'Stummschalten',
|
||||||
muted: 'Stummgeschaltet',
|
muted: 'Stummgeschaltet',
|
||||||
|
@ -20,7 +22,8 @@ const de = {
|
||||||
show_new: 'Zeige Neuere',
|
show_new: 'Zeige Neuere',
|
||||||
error_fetching: 'Error beim Laden',
|
error_fetching: 'Error beim Laden',
|
||||||
up_to_date: 'Aktuell',
|
up_to_date: 'Aktuell',
|
||||||
load_older: 'Lade ältere Beiträge'
|
load_older: 'Lade ältere Beiträge',
|
||||||
|
conversation: 'Unterhaltung'
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
user_settings: 'Benutzereinstellungen',
|
user_settings: 'Benutzereinstellungen',
|
||||||
|
@ -36,14 +39,15 @@ const de = {
|
||||||
profile_background: 'Profil Hintergrund',
|
profile_background: 'Profil Hintergrund',
|
||||||
set_new_profile_background: 'Setze neuen Profil Hintergrund',
|
set_new_profile_background: 'Setze neuen Profil Hintergrund',
|
||||||
settings: 'Einstellungen',
|
settings: 'Einstellungen',
|
||||||
theme: 'Theme',
|
theme: 'Farbschema',
|
||||||
filtering: 'Filter',
|
filtering: 'Filter',
|
||||||
filtering_explanation: 'Alle Beiträge die diese Wörter enthalten werden ausgeblendet. Ein Wort pro Zeile.',
|
filtering_explanation: 'Alle Beiträge die diese Wörter enthalten werden ausgeblendet. Ein Wort pro Zeile.',
|
||||||
attachments: 'Anhänge',
|
attachments: 'Anhänge',
|
||||||
hide_attachments_in_tl: 'Anhänge in der Timeline ausblenden',
|
hide_attachments_in_tl: 'Anhänge in der Timeline ausblenden',
|
||||||
hide_attachments_in_convo: 'Anhänge in Unterhaltungen ausblenden',
|
hide_attachments_in_convo: 'Anhänge in Unterhaltungen ausblenden',
|
||||||
nsfw_clickthrough: 'Aktiviere ausblendbares Overlay für als NSFW markierte Anhänge',
|
nsfw_clickthrough: 'Aktiviere ausblendbares Overlay für als NSFW markierte Anhänge',
|
||||||
autoload: 'Aktiviere automatisches Laden von Beiträgen beim scrollen',
|
autoload: 'Aktiviere automatisches Laden von älteren Beiträgen beim scrollen',
|
||||||
|
streaming: 'Aktiviere automatisches Laden (Streaming) von neuen Beiträgen',
|
||||||
reply_link_preview: 'Aktiviere reply-link Vorschau bei Maus-Hover'
|
reply_link_preview: 'Aktiviere reply-link Vorschau bei Maus-Hover'
|
||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
|
@ -51,6 +55,28 @@ const de = {
|
||||||
read: 'Gelesen!',
|
read: 'Gelesen!',
|
||||||
followed_you: 'folgt dir'
|
followed_you: 'folgt dir'
|
||||||
},
|
},
|
||||||
|
login: {
|
||||||
|
login: 'Anmelden',
|
||||||
|
username: 'Benutzername',
|
||||||
|
password: 'Passwort',
|
||||||
|
register: 'Registrieren',
|
||||||
|
logout: 'Abmelden'
|
||||||
|
},
|
||||||
|
registration: {
|
||||||
|
registration: 'Registrierung',
|
||||||
|
fullname: 'Angezeigter Name',
|
||||||
|
email: 'Email',
|
||||||
|
bio: 'Bio',
|
||||||
|
password_confirm: 'Passwort bestätigen'
|
||||||
|
},
|
||||||
|
post_status: {
|
||||||
|
posting: 'Veröffentlichen',
|
||||||
|
default: 'Sitze gerade im Hofbräuhaus.'
|
||||||
|
},
|
||||||
|
finder: {
|
||||||
|
find_user: 'Finde Benutzer',
|
||||||
|
error_fetching_user: 'Fehler beim Suchen des Benutzers'
|
||||||
|
},
|
||||||
general: {
|
general: {
|
||||||
submit: 'Absenden'
|
submit: 'Absenden'
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,10 @@ const users = {
|
||||||
state: defaultState,
|
state: defaultState,
|
||||||
mutations,
|
mutations,
|
||||||
actions: {
|
actions: {
|
||||||
|
fetchUser (store, id) {
|
||||||
|
store.rootState.api.backendInteractor.fetchUser({id})
|
||||||
|
.then((user) => store.commit('addNewUsers', user))
|
||||||
|
},
|
||||||
addNewStatuses (store, { statuses }) {
|
addNewStatuses (store, { statuses }) {
|
||||||
const users = map(statuses, 'user')
|
const users = map(statuses, 'user')
|
||||||
const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))
|
const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))
|
||||||
|
|
|
@ -28,7 +28,7 @@ const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json'
|
||||||
const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json'
|
const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json'
|
||||||
const BLOCKING_URL = '/api/blocks/create.json'
|
const BLOCKING_URL = '/api/blocks/create.json'
|
||||||
const UNBLOCKING_URL = '/api/blocks/destroy.json'
|
const UNBLOCKING_URL = '/api/blocks/destroy.json'
|
||||||
// const USER_URL = '/api/users/show.json'
|
const USER_URL = '/api/users/show.json'
|
||||||
|
|
||||||
import { each, map } from 'lodash'
|
import { each, map } from 'lodash'
|
||||||
import 'whatwg-fetch'
|
import 'whatwg-fetch'
|
||||||
|
@ -202,6 +202,12 @@ const unblockUser = ({id, credentials}) => {
|
||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchUser = ({id, credentials}) => {
|
||||||
|
let url = `${USER_URL}?user_id=${id}`
|
||||||
|
return fetch(url, { headers: authHeaders(credentials) })
|
||||||
|
.then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
const fetchFriends = ({id, credentials}) => {
|
const fetchFriends = ({id, credentials}) => {
|
||||||
let url = `${FRIENDS_URL}?user_id=${id}`
|
let url = `${FRIENDS_URL}?user_id=${id}`
|
||||||
return fetch(url, { headers: authHeaders(credentials) })
|
return fetch(url, { headers: authHeaders(credentials) })
|
||||||
|
@ -363,6 +369,7 @@ const apiService = {
|
||||||
unfollowUser,
|
unfollowUser,
|
||||||
blockUser,
|
blockUser,
|
||||||
unblockUser,
|
unblockUser,
|
||||||
|
fetchUser,
|
||||||
favorite,
|
favorite,
|
||||||
unfavorite,
|
unfavorite,
|
||||||
retweet,
|
retweet,
|
||||||
|
|
|
@ -22,6 +22,10 @@ const backendInteractorService = (credentials) => {
|
||||||
return apiService.fetchAllFollowing({username, credentials})
|
return apiService.fetchAllFollowing({username, credentials})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchUser = ({id}) => {
|
||||||
|
return apiService.fetchUser({id, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
const followUser = (id) => {
|
const followUser = (id) => {
|
||||||
return apiService.followUser({credentials, id})
|
return apiService.followUser({credentials, id})
|
||||||
}
|
}
|
||||||
|
@ -65,6 +69,7 @@ const backendInteractorService = (credentials) => {
|
||||||
unfollowUser,
|
unfollowUser,
|
||||||
blockUser,
|
blockUser,
|
||||||
unblockUser,
|
unblockUser,
|
||||||
|
fetchUser,
|
||||||
fetchAllFollowing,
|
fetchAllFollowing,
|
||||||
verifyCredentials: apiService.verifyCredentials,
|
verifyCredentials: apiService.verifyCredentials,
|
||||||
startFetching,
|
startFetching,
|
||||||
|
|
Loading…
Reference in New Issue