Merge remote-tracking branch 'upstream/develop' into shigusegubu
* upstream/develop: (33 commits) #383: content type error #376: update status timeline when it's empty #377: no exteral profile link for local users #371: show notification when user setting's saved Clean up CSS a bit #364: update ap_id error with username Rename: instanceSpecificPanelPresent Hide isp option if instance has panel disabled Take over branch and fix some issues Fix lint errors Better error handling Remove cropped image size restriction Remove modal component Make embedded image cropper Revert eslintrc changes Check if variable exists before using Remove event listeners when destory ImageCropper Localization of ImageCropper component Remove event listener when modal is destroyed Crop avatar image using minWidth/minHeight ...
This commit is contained in:
commit
90a82b7dec
33 changed files with 490 additions and 1214 deletions
|
@ -11,7 +11,7 @@ module.exports = {
|
||||||
'html'
|
'html'
|
||||||
],
|
],
|
||||||
// add your custom rules here
|
// add your custom rules here
|
||||||
'rules': {
|
rules: {
|
||||||
// allow paren-less arrow functions
|
// allow paren-less arrow functions
|
||||||
'arrow-parens': 0,
|
'arrow-parens': 0,
|
||||||
// allow async-await
|
// allow async-await
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"babel-plugin-add-module-exports": "^0.2.1",
|
"babel-plugin-add-module-exports": "^0.2.1",
|
||||||
"babel-plugin-lodash": "^3.2.11",
|
"babel-plugin-lodash": "^3.2.11",
|
||||||
"chromatism": "^3.0.0",
|
"chromatism": "^3.0.0",
|
||||||
|
"cropperjs": "^1.4.3",
|
||||||
"diff": "^3.0.1",
|
"diff": "^3.0.1",
|
||||||
"karma-mocha-reporter": "^2.2.1",
|
"karma-mocha-reporter": "^2.2.1",
|
||||||
"localforage": "^1.5.0",
|
"localforage": "^1.5.0",
|
||||||
|
|
11
src/App.scss
11
src/App.scss
|
@ -181,8 +181,7 @@ input, textarea, .select {
|
||||||
color: $fallback--text;
|
color: $fallback--text;
|
||||||
color: var(--text, $fallback--text);
|
color: var(--text, $fallback--text);
|
||||||
}
|
}
|
||||||
&:disabled,
|
&:disabled {
|
||||||
{
|
|
||||||
&,
|
&,
|
||||||
& + label,
|
& + label,
|
||||||
& + label::before {
|
& + label::before {
|
||||||
|
@ -649,10 +648,6 @@ nav {
|
||||||
color: var(--lightText, $fallback--lightText);
|
color: var(--lightText, $fallback--lightText);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-format {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
div {
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
}
|
}
|
||||||
|
@ -739,3 +734,7 @@ nav {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn.btn-default {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading timeline-heading" :class="{ 'chat-heading': floating }" @click.stop.prevent="togglePanel">
|
<div class="panel-heading timeline-heading" :class="{ 'chat-heading': floating }" @click.stop.prevent="togglePanel">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
{{$t('chat.title')}}
|
<span>{{$t('chat.title')}}</span>
|
||||||
<i class="icon-cancel" style="float: right;" v-if="floating"></i>
|
<i class="icon-cancel" v-if="floating"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-window" v-chat-scroll>
|
<div class="chat-window" v-chat-scroll>
|
||||||
|
@ -98,4 +98,11 @@
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-panel {
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -26,7 +26,9 @@ const FollowList = {
|
||||||
entries () {
|
entries () {
|
||||||
return this.showFollowers ? this.user.followers : this.user.friends
|
return this.showFollowers ? this.user.followers : this.user.friends
|
||||||
},
|
},
|
||||||
showActions () { return this.$store.state.users.currentUser.id === this.userId }
|
showFollowsYou () {
|
||||||
|
return !this.showFollowers || (this.showFollowers && this.userId !== this.$store.state.users.currentUser.id)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchEntries () {
|
fetchEntries () {
|
||||||
|
@ -55,6 +57,9 @@ const FollowList = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
'user': 'fetchEntries'
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
UserCard
|
UserCard
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
<user-card
|
<user-card
|
||||||
v-for="entry in entries"
|
v-for="entry in entries"
|
||||||
:key="entry.id" :user="entry"
|
:key="entry.id" :user="entry"
|
||||||
:showFollows="!showFollowers"
|
:noFollowsYou="!showFollowsYou"
|
||||||
:showActions="showActions"
|
|
||||||
/>
|
/>
|
||||||
<div class="text-center panel-footer">
|
<div class="text-center panel-footer">
|
||||||
<a v-if="error" @click="fetchEntries" class="alert error">
|
<a v-if="error" @click="fetchEntries" class="alert error">
|
||||||
|
|
128
src/components/image_cropper/image_cropper.js
Normal file
128
src/components/image_cropper/image_cropper.js
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import Cropper from 'cropperjs'
|
||||||
|
import 'cropperjs/dist/cropper.css'
|
||||||
|
|
||||||
|
const ImageCropper = {
|
||||||
|
props: {
|
||||||
|
trigger: {
|
||||||
|
type: [String, window.Element],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
submitHandler: {
|
||||||
|
type: Function,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
cropperOptions: {
|
||||||
|
type: Object,
|
||||||
|
default () {
|
||||||
|
return {
|
||||||
|
aspectRatio: 1,
|
||||||
|
autoCropArea: 1,
|
||||||
|
viewMode: 1,
|
||||||
|
movable: false,
|
||||||
|
zoomable: false,
|
||||||
|
guides: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mimes: {
|
||||||
|
type: String,
|
||||||
|
default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'
|
||||||
|
},
|
||||||
|
saveButtonLabel: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
cancelButtonLabel: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
cropper: undefined,
|
||||||
|
dataUrl: undefined,
|
||||||
|
filename: undefined,
|
||||||
|
submitting: false,
|
||||||
|
submitError: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
saveText () {
|
||||||
|
return this.saveButtonLabel || this.$t('image_cropper.save')
|
||||||
|
},
|
||||||
|
cancelText () {
|
||||||
|
return this.cancelButtonLabel || this.$t('image_cropper.cancel')
|
||||||
|
},
|
||||||
|
submitErrorMsg () {
|
||||||
|
return this.submitError && this.submitError instanceof Error ? this.submitError.toString() : this.submitError
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
destroy () {
|
||||||
|
if (this.cropper) {
|
||||||
|
this.cropper.destroy()
|
||||||
|
}
|
||||||
|
this.$refs.input.value = ''
|
||||||
|
this.dataUrl = undefined
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
submit () {
|
||||||
|
this.submitting = true
|
||||||
|
this.avatarUploadError = null
|
||||||
|
this.submitHandler(this.cropper, this.filename)
|
||||||
|
.then(() => this.destroy())
|
||||||
|
.catch((err) => {
|
||||||
|
this.submitError = err
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.submitting = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
pickImage () {
|
||||||
|
this.$refs.input.click()
|
||||||
|
},
|
||||||
|
createCropper () {
|
||||||
|
this.cropper = new Cropper(this.$refs.img, this.cropperOptions)
|
||||||
|
},
|
||||||
|
getTriggerDOM () {
|
||||||
|
return typeof this.trigger === 'object' ? this.trigger : document.querySelector(this.trigger)
|
||||||
|
},
|
||||||
|
readFile () {
|
||||||
|
const fileInput = this.$refs.input
|
||||||
|
if (fileInput.files != null && fileInput.files[0] != null) {
|
||||||
|
let reader = new window.FileReader()
|
||||||
|
reader.onload = (e) => {
|
||||||
|
this.dataUrl = e.target.result
|
||||||
|
this.$emit('open')
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(fileInput.files[0])
|
||||||
|
this.filename = fileInput.files[0].name || 'unknown'
|
||||||
|
this.$emit('changed', fileInput.files[0], reader)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearError () {
|
||||||
|
this.submitError = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
// listen for click event on trigger
|
||||||
|
const trigger = this.getTriggerDOM()
|
||||||
|
if (!trigger) {
|
||||||
|
this.$emit('error', 'No image make trigger found.', 'user')
|
||||||
|
} else {
|
||||||
|
trigger.addEventListener('click', this.pickImage)
|
||||||
|
}
|
||||||
|
// listen for input file changes
|
||||||
|
const fileInput = this.$refs.input
|
||||||
|
fileInput.addEventListener('change', this.readFile)
|
||||||
|
},
|
||||||
|
beforeDestroy: function () {
|
||||||
|
// remove the event listeners
|
||||||
|
const trigger = this.getTriggerDOM()
|
||||||
|
if (trigger) {
|
||||||
|
trigger.removeEventListener('click', this.pickImage)
|
||||||
|
}
|
||||||
|
const fileInput = this.$refs.input
|
||||||
|
fileInput.removeEventListener('change', this.readFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageCropper
|
42
src/components/image_cropper/image_cropper.vue
Normal file
42
src/components/image_cropper/image_cropper.vue
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<div class="image-cropper">
|
||||||
|
<div v-if="dataUrl">
|
||||||
|
<div class="image-cropper-image-container">
|
||||||
|
<img ref="img" :src="dataUrl" alt="" @load.stop="createCropper" />
|
||||||
|
</div>
|
||||||
|
<div class="image-cropper-buttons-wrapper">
|
||||||
|
<button class="btn" type="button" :disabled="submitting" @click="submit" v-text="saveText"></button>
|
||||||
|
<button class="btn" type="button" :disabled="submitting" @click="destroy" v-text="cancelText"></button>
|
||||||
|
<i class="icon-spin4 animate-spin" v-if="submitting"></i>
|
||||||
|
</div>
|
||||||
|
<div class="alert error" v-if="submitError">
|
||||||
|
{{submitErrorMsg}}
|
||||||
|
<i class="button-icon icon-cancel" @click="clearError"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input ref="input" type="file" class="image-cropper-img-input" :accept="mimes">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./image_cropper.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.image-cropper {
|
||||||
|
&-img-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image-container {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-buttons-wrapper {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -11,27 +11,62 @@ const MediaModal = {
|
||||||
showing () {
|
showing () {
|
||||||
return this.$store.state.mediaViewer.activated
|
return this.$store.state.mediaViewer.activated
|
||||||
},
|
},
|
||||||
|
media () {
|
||||||
|
return this.$store.state.mediaViewer.media
|
||||||
|
},
|
||||||
currentIndex () {
|
currentIndex () {
|
||||||
return this.$store.state.mediaViewer.currentIndex
|
return this.$store.state.mediaViewer.currentIndex
|
||||||
},
|
},
|
||||||
currentMedia () {
|
currentMedia () {
|
||||||
return this.$store.state.mediaViewer.media[this.currentIndex]
|
return this.media[this.currentIndex]
|
||||||
|
},
|
||||||
|
canNavigate () {
|
||||||
|
return this.media.length > 1
|
||||||
},
|
},
|
||||||
type () {
|
type () {
|
||||||
return this.currentMedia ? fileTypeService.fileType(this.currentMedia.mimetype) : null
|
return this.currentMedia ? fileTypeService.fileType(this.currentMedia.mimetype) : null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
document.addEventListener('keyup', e => {
|
|
||||||
if (e.keyCode === 27 && this.showing) { // escape
|
|
||||||
this.hide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
hide () {
|
hide () {
|
||||||
this.$store.dispatch('closeMediaViewer')
|
this.$store.dispatch('closeMediaViewer')
|
||||||
|
},
|
||||||
|
goPrev () {
|
||||||
|
if (this.canNavigate) {
|
||||||
|
const prevIndex = this.currentIndex === 0 ? this.media.length - 1 : (this.currentIndex - 1)
|
||||||
|
this.$store.dispatch('setCurrent', this.media[prevIndex])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goNext () {
|
||||||
|
if (this.canNavigate) {
|
||||||
|
const nextIndex = this.currentIndex === this.media.length - 1 ? 0 : (this.currentIndex + 1)
|
||||||
|
this.$store.dispatch('setCurrent', this.media[nextIndex])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleKeyupEvent (e) {
|
||||||
|
if (this.showing && e.keyCode === 27) { // escape
|
||||||
|
this.hide()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleKeydownEvent (e) {
|
||||||
|
if (!this.showing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.keyCode === 39) { // arrow right
|
||||||
|
this.goNext()
|
||||||
|
} else if (e.keyCode === 37) { // arrow left
|
||||||
|
this.goPrev()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
document.addEventListener('keyup', this.handleKeyupEvent)
|
||||||
|
document.addEventListener('keydown', this.handleKeydownEvent)
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
document.removeEventListener('keyup', this.handleKeyupEvent)
|
||||||
|
document.removeEventListener('keydown', this.handleKeydownEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,22 @@
|
||||||
:controls="true"
|
:controls="true"
|
||||||
@click.stop.native="">
|
@click.stop.native="">
|
||||||
</VideoAttachment>
|
</VideoAttachment>
|
||||||
|
<button
|
||||||
|
:title="$t('media_modal.previous')"
|
||||||
|
class="modal-view-button-arrow modal-view-button-arrow--prev"
|
||||||
|
v-if="canNavigate"
|
||||||
|
@click.stop.prevent="goPrev"
|
||||||
|
>
|
||||||
|
<i class="icon-left-open arrow-icon" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:title="$t('media_modal.next')"
|
||||||
|
class="modal-view-button-arrow modal-view-button-arrow--next"
|
||||||
|
v-if="canNavigate"
|
||||||
|
@click.stop.prevent="goNext"
|
||||||
|
>
|
||||||
|
<i class="icon-right-open arrow-icon" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -19,15 +35,29 @@
|
||||||
.modal-view {
|
.modal-view {
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
cursor: pointer;
|
|
||||||
|
&:hover {
|
||||||
|
.modal-view-button-arrow {
|
||||||
|
opacity: 0.75;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-image {
|
.modal-image {
|
||||||
|
@ -35,4 +65,49 @@
|
||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5);
|
box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-view-button-arrow {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -50px;
|
||||||
|
width: 70px;
|
||||||
|
height: 100px;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
opacity: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
background: none;
|
||||||
|
appearance: none;
|
||||||
|
overflow: visible;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity 333ms cubic-bezier(.4,0,.22,1);
|
||||||
|
|
||||||
|
.arrow-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 35px;
|
||||||
|
height: 30px;
|
||||||
|
width: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 30px;
|
||||||
|
color: #FFF;
|
||||||
|
text-align: center;
|
||||||
|
background-color: rgba(0,0,0,.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--prev {
|
||||||
|
left: 0;
|
||||||
|
.arrow-icon {
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--next {
|
||||||
|
right: 0;
|
||||||
|
.arrow-icon {
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
</li>
|
</li>
|
||||||
<li v-if='currentUser && currentUser.locked'>
|
<li v-if='currentUser && currentUser.locked'>
|
||||||
<router-link :to="{ name: 'friend-requests' }">
|
<router-link :to="{ name: 'friend-requests' }">
|
||||||
{{ $t("nav.friend_requests") }}
|
{{ $t("nav.friend_requests")}}
|
||||||
|
<span v-if='currentUser.follow_request_count > 0' class="badge follow-request-count">
|
||||||
|
{{currentUser.follow_request_count}}
|
||||||
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -52,6 +55,12 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.follow-request-count {
|
||||||
|
margin: -6px 10px;
|
||||||
|
background-color: $fallback--bg;
|
||||||
|
background-color: var(--input, $fallback--faint);
|
||||||
|
}
|
||||||
|
|
||||||
.nav-panel li {
|
.nav-panel li {
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
border-color: $fallback--border;
|
border-color: $fallback--border;
|
||||||
|
|
|
@ -103,6 +103,7 @@
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
.name-and-action {
|
.name-and-action {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -123,8 +124,8 @@
|
||||||
object-fit: contain
|
object-fit: contain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeago {
|
.timeago {
|
||||||
float: right;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,10 @@ const PostStatusForm = {
|
||||||
? this.copyMessageScope
|
? this.copyMessageScope
|
||||||
: this.$store.state.users.currentUser.default_scope
|
: this.$store.state.users.currentUser.default_scope
|
||||||
|
|
||||||
|
const contentType = typeof this.$store.state.config.postContentType === 'undefined'
|
||||||
|
? this.$store.state.instance.postContentType
|
||||||
|
: this.$store.state.config.postContentType
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dropFiles: [],
|
dropFiles: [],
|
||||||
submitDisabled: false,
|
submitDisabled: false,
|
||||||
|
@ -67,7 +71,8 @@ const PostStatusForm = {
|
||||||
status: statusText,
|
status: statusText,
|
||||||
nsfw: false,
|
nsfw: false,
|
||||||
files: [],
|
files: [],
|
||||||
visibility: scope
|
visibility: scope,
|
||||||
|
contentType
|
||||||
},
|
},
|
||||||
caret: 0
|
caret: 0
|
||||||
}
|
}
|
||||||
|
@ -166,11 +171,6 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
formattingOptionsEnabled () {
|
formattingOptionsEnabled () {
|
||||||
return this.$store.state.instance.formattingOptionsEnabled
|
return this.$store.state.instance.formattingOptionsEnabled
|
||||||
},
|
|
||||||
defaultPostContentType () {
|
|
||||||
return typeof this.$store.state.config.postContentType === 'undefined'
|
|
||||||
? this.$store.state.instance.postContentType
|
|
||||||
: this.$store.state.config.postContentType
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
<div class="visibility-tray">
|
<div class="visibility-tray">
|
||||||
<span class="text-format" v-if="formattingOptionsEnabled">
|
<span class="text-format" v-if="formattingOptionsEnabled">
|
||||||
<label for="post-content-type" class="select">
|
<label for="post-content-type" class="select">
|
||||||
<select id="post-content-type" v-model="defaultPostContentType" class="form-control">
|
<select id="post-content-type" v-model="newStatus.contentType" class="form-control">
|
||||||
<option value="text/plain">{{$t('post_status.content_type.plain_text')}}</option>
|
<option value="text/plain">{{$t('post_status.content_type.plain_text')}}</option>
|
||||||
<option value="text/html">HTML</option>
|
<option value="text/html">HTML</option>
|
||||||
<option value="text/markdown">Markdown</option>
|
<option value="text/markdown">Markdown</option>
|
||||||
|
@ -118,6 +118,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post-status-form {
|
||||||
|
.visibility-tray {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.post-status-form, .login {
|
.post-status-form, .login {
|
||||||
.form-bottom {
|
.form-bottom {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -91,7 +91,8 @@ const settings = {
|
||||||
},
|
},
|
||||||
currentSaveStateNotice () {
|
currentSaveStateNotice () {
|
||||||
return this.$store.state.interface.settings.currentSaveStateNotice
|
return this.$store.state.interface.settings.currentSaveStateNotice
|
||||||
}
|
},
|
||||||
|
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
hideAttachmentsLocal (value) {
|
hideAttachmentsLocal (value) {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<li>
|
<li>
|
||||||
<interface-language-switcher />
|
<interface-language-switcher />
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li v-if="instanceSpecificPanelPresent">
|
||||||
<input type="checkbox" id="hideISP" v-model="hideISPLocal">
|
<input type="checkbox" id="hideISP" v-model="hideISPLocal">
|
||||||
<label for="hideISP">{{$t('settings.hide_isp')}}</label>
|
<label for="hideISP">{{$t('settings.hide_isp')}}</label>
|
||||||
</li>
|
</li>
|
||||||
|
@ -311,20 +311,6 @@
|
||||||
color: $fallback--cRed;
|
color: $fallback--cRed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.old-avatar {
|
|
||||||
width: 128px;
|
|
||||||
border-radius: $fallback--avatarRadius;
|
|
||||||
border-radius: var(--avatarRadius, $fallback--avatarRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-avatar {
|
|
||||||
object-fit: cover;
|
|
||||||
width: 128px;
|
|
||||||
height: 128px;
|
|
||||||
border-radius: $fallback--avatarRadius;
|
|
||||||
border-radius: var(--avatarRadius, $fallback--avatarRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
min-height: 28px;
|
min-height: 28px;
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
|
|
|
@ -45,6 +45,10 @@
|
||||||
<li v-if="currentUser && currentUser.locked" @click="toggleDrawer">
|
<li v-if="currentUser && currentUser.locked" @click="toggleDrawer">
|
||||||
<router-link to='/friend-requests'>
|
<router-link to='/friend-requests'>
|
||||||
{{ $t("nav.friend_requests") }}
|
{{ $t("nav.friend_requests") }}
|
||||||
|
<span v-if='currentUser.follow_request_count > 0' class="badge follow-request-count">
|
||||||
|
{{currentUser.follow_request_count}}
|
||||||
|
</span>
|
||||||
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li @click="toggleDrawer">
|
<li @click="toggleDrawer">
|
||||||
|
|
|
@ -554,7 +554,7 @@ a.unmute {
|
||||||
|
|
||||||
.timeline > {
|
.timeline > {
|
||||||
.status-el:last-child {
|
.status-el:last-child {
|
||||||
border-bottom-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;;
|
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
|
||||||
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
|
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ const Timeline = {
|
||||||
'title',
|
'title',
|
||||||
'userId',
|
'userId',
|
||||||
'tag',
|
'tag',
|
||||||
'embedded'
|
'embedded',
|
||||||
|
'count'
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -20,7 +20,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="classes.footer">
|
<div :class="classes.footer">
|
||||||
<div v-if="bottomedOut" class="new-status-notification text-center panel-footer faint">
|
<div v-if="count===0" class="new-status-notification text-center panel-footer faint">
|
||||||
|
{{$t('timeline.no_statuses')}}
|
||||||
|
</div>
|
||||||
|
<div v-else-if="bottomedOut" class="new-status-notification text-center panel-footer faint">
|
||||||
{{$t('timeline.no_more_statuses')}}
|
{{$t('timeline.no_more_statuses')}}
|
||||||
</div>
|
</div>
|
||||||
<a v-else-if="!timeline.loading" href="#" v-on:click.prevent='fetchOlderStatuses()'>
|
<a v-else-if="!timeline.loading" href="#" v-on:click.prevent='fetchOlderStatuses()'>
|
||||||
|
|
|
@ -6,9 +6,8 @@ import { requestFollow, requestUnfollow } from '../../services/follow_manipulate
|
||||||
const UserCard = {
|
const UserCard = {
|
||||||
props: [
|
props: [
|
||||||
'user',
|
'user',
|
||||||
'showFollows',
|
'noFollowsYou',
|
||||||
'showApproval',
|
'showApproval'
|
||||||
'showActions'
|
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -26,7 +25,7 @@ const UserCard = {
|
||||||
currentUser () { return this.$store.state.users.currentUser },
|
currentUser () { return this.$store.state.users.currentUser },
|
||||||
following () { return this.updated ? this.updated.following : this.user.following },
|
following () { return this.updated ? this.updated.following : this.user.following },
|
||||||
showFollow () {
|
showFollow () {
|
||||||
return this.showActions && (!this.showFollows && !this.following || this.updated && !this.updated.following)
|
return !this.showApproval && (!this.following || this.updated && !this.updated.following)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -1,27 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<router-link :to="userProfileLink(user)">
|
<router-link :to="userProfileLink(user)">
|
||||||
<UserAvatar class="avatar" :compact="true" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/>
|
<UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="usercard" v-if="userExpanded">
|
<div class="user-card-main-content">
|
||||||
<user-card-content :user="user" :switcher="false"></user-card-content>
|
<div class="usercard" v-if="userExpanded">
|
||||||
</div>
|
<user-card-content :user="user" :switcher="false"></user-card-content>
|
||||||
<div class="name-and-screen-name" v-else>
|
</div>
|
||||||
<div :title="user.name" class="user-name">
|
<div class="name-and-screen-name" v-if="!userExpanded">
|
||||||
<span v-if="user.name_html" v-html="user.name_html"></span>
|
<div :title="user.name" class="user-name">
|
||||||
<span v-else>{{ user.name }}</span>
|
<span v-if="user.name_html" v-html="user.name_html"></span>
|
||||||
<span class="follows-you" v-if="!userExpanded && showFollows && user.follows_you">
|
<span v-else>{{ user.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="user-link-action">
|
||||||
|
<router-link class='user-screen-name' :to="userProfileLink(user)">
|
||||||
|
@{{user.screen_name}}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="follow-box" v-if="!userExpanded">
|
||||||
|
<span class="faint" v-if="!noFollowsYou && user.follows_you">
|
||||||
{{ currentUser.id == user.id ? $t('user_card.its_you') : $t('user_card.follows_you') }}
|
{{ currentUser.id == user.id ? $t('user_card.its_you') : $t('user_card.follows_you') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<button
|
||||||
<div class="user-link-action">
|
v-if="showFollow"
|
||||||
<router-link class='user-screen-name' :to="userProfileLink(user)">
|
class="btn btn-default"
|
||||||
@{{user.screen_name}}
|
@click="followUser"
|
||||||
</router-link>
|
|
||||||
<button
|
|
||||||
v-if="showFollow"
|
|
||||||
class="btn btn-default"
|
|
||||||
@click="followUser"
|
|
||||||
:disabled="followRequestInProgress"
|
:disabled="followRequestInProgress"
|
||||||
:title="followRequestSent ? $t('user_card.follow_again') : ''"
|
:title="followRequestSent ? $t('user_card.follow_again') : ''"
|
||||||
>
|
>
|
||||||
|
@ -35,7 +39,7 @@
|
||||||
{{ $t('user_card.follow') }}
|
{{ $t('user_card.follow') }}
|
||||||
</template>
|
</template>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="showActions && showFollows && following" class="btn btn-default" @click="unfollowUser" :disabled="followRequestInProgress">
|
<button v-if="following" class="btn btn-default pressed" @click="unfollowUser" :disabled="followRequestInProgress">
|
||||||
<template v-if="followRequestInProgress">
|
<template v-if="followRequestInProgress">
|
||||||
{{ $t('user_card.follow_progress') }}
|
{{ $t('user_card.follow_progress') }}
|
||||||
</template>
|
</template>
|
||||||
|
@ -44,10 +48,10 @@
|
||||||
</template>
|
</template>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="approval" v-if="showApproval">
|
||||||
<div class="approval" v-if="showApproval">
|
<button class="btn btn-default" @click="approveUser">{{ $t('user_card.approve') }}</button>
|
||||||
<button class="btn btn-default" @click="approveUser">{{ $t('user_card.approve') }}</button>
|
<button class="btn btn-default" @click="denyUser">{{ $t('user_card.deny') }}</button>
|
||||||
<button class="btn btn-default" @click="denyUser">{{ $t('user_card.deny') }}</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -57,15 +61,19 @@
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.name-and-screen-name {
|
.user-card-main-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 100%;
|
||||||
margin-left: 0.7em;
|
margin-left: 0.7em;
|
||||||
margin-top:0.0em;
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-and-screen-name {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.user-name {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
|
.user-name {
|
||||||
img {
|
img {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
@ -73,21 +81,14 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-link-action {
|
.user-link-action {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
button {
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.follows-you {
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -99,16 +100,31 @@
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-bottom-color: $fallback--border;
|
border-bottom-color: $fallback--border;
|
||||||
border-bottom-color: var(--border, $fallback--border);
|
border-bottom-color: var(--border, $fallback--border);
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.follow-box {
|
||||||
|
text-align: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
line-height: 1.5em;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-left: auto;
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.usercard {
|
.usercard {
|
||||||
width: fill-available;
|
width: fill-available;
|
||||||
margin: 0.2em 0 0 0.7em;
|
|
||||||
border-radius: $fallback--panelRadius;
|
border-radius: $fallback--panelRadius;
|
||||||
border-radius: var(--panelRadius, $fallback--panelRadius);
|
border-radius: var(--panelRadius, $fallback--panelRadius);
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
|
@ -129,9 +145,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.approval {
|
.approval {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
button {
|
button {
|
||||||
width: 100%;
|
margin-top: 0.5em;
|
||||||
margin-bottom: 0.5em;
|
margin-right: 0.5em;
|
||||||
|
flex: 1 1;
|
||||||
|
max-width: 12em;
|
||||||
|
min-width: 8em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<router-link :to="{ name: 'user-settings' }" v-if="!isOtherUser">
|
<router-link :to="{ name: 'user-settings' }" v-if="!isOtherUser">
|
||||||
<i class="button-icon icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i>
|
<i class="button-icon icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i>
|
||||||
</router-link>
|
</router-link>
|
||||||
<a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser">
|
<a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser && !user.is_local">
|
||||||
<i class="icon-link-ext usersettings"></i>
|
<i class="icon-link-ext usersettings"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -386,6 +386,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.floater {
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
<Timeline
|
<Timeline
|
||||||
:label="$t('user_card.statuses')"
|
:label="$t('user_card.statuses')"
|
||||||
:disabled="!user.statuses_count"
|
:disabled="!user.statuses_count"
|
||||||
|
:count="user.statuses_count"
|
||||||
:embedded="true"
|
:embedded="true"
|
||||||
:title="$t('user_profile.timeline_title')"
|
:title="$t('user_profile.timeline_title')"
|
||||||
:timeline="timeline"
|
:timeline="timeline"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { unescape } from 'lodash'
|
import { unescape } from 'lodash'
|
||||||
|
|
||||||
import TabSwitcher from '../tab_switcher/tab_switcher.js'
|
import TabSwitcher from '../tab_switcher/tab_switcher.js'
|
||||||
|
import ImageCropper from '../image_cropper/image_cropper.vue'
|
||||||
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
||||||
import fileSizeFormatService from '../../services/file_size_format/file_size_format.js'
|
import fileSizeFormatService from '../../services/file_size_format/file_size_format.js'
|
||||||
|
|
||||||
|
@ -20,14 +21,12 @@ const UserSettings = {
|
||||||
followImportError: false,
|
followImportError: false,
|
||||||
followsImported: false,
|
followsImported: false,
|
||||||
enableFollowsExport: true,
|
enableFollowsExport: true,
|
||||||
avatarUploading: false,
|
pickAvatarBtnVisible: true,
|
||||||
bannerUploading: false,
|
bannerUploading: false,
|
||||||
backgroundUploading: false,
|
backgroundUploading: false,
|
||||||
followListUploading: false,
|
followListUploading: false,
|
||||||
avatarPreview: null,
|
|
||||||
bannerPreview: null,
|
bannerPreview: null,
|
||||||
backgroundPreview: null,
|
backgroundPreview: null,
|
||||||
avatarUploadError: null,
|
|
||||||
bannerUploadError: null,
|
bannerUploadError: null,
|
||||||
backgroundUploadError: null,
|
backgroundUploadError: null,
|
||||||
deletingAccount: false,
|
deletingAccount: false,
|
||||||
|
@ -41,7 +40,8 @@ const UserSettings = {
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
StyleSwitcher,
|
StyleSwitcher,
|
||||||
TabSwitcher
|
TabSwitcher,
|
||||||
|
ImageCropper
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user () {
|
user () {
|
||||||
|
@ -60,6 +60,9 @@ const UserSettings = {
|
||||||
private: { selected: this.newDefaultScope === 'private' },
|
private: { selected: this.newDefaultScope === 'private' },
|
||||||
direct: { selected: this.newDefaultScope === 'direct' }
|
direct: { selected: this.newDefaultScope === 'direct' }
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
currentSaveStateNotice () {
|
||||||
|
return this.$store.state.interface.settings.currentSaveStateNotice
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -117,35 +120,15 @@ const UserSettings = {
|
||||||
}
|
}
|
||||||
reader.readAsDataURL(file)
|
reader.readAsDataURL(file)
|
||||||
},
|
},
|
||||||
submitAvatar () {
|
submitAvatar (cropper) {
|
||||||
if (!this.avatarPreview) { return }
|
const img = cropper.getCroppedCanvas().toDataURL('image/jpeg')
|
||||||
|
return this.$store.state.api.backendInteractor.updateAvatar({ params: { img } }).then((user) => {
|
||||||
let img = this.avatarPreview
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
let imginfo = new Image()
|
|
||||||
let cropX, cropY, cropW, cropH
|
|
||||||
imginfo.src = img
|
|
||||||
if (imginfo.height > imginfo.width) {
|
|
||||||
cropX = 0
|
|
||||||
cropW = imginfo.width
|
|
||||||
cropY = Math.floor((imginfo.height - imginfo.width) / 2)
|
|
||||||
cropH = imginfo.width
|
|
||||||
} else {
|
|
||||||
cropY = 0
|
|
||||||
cropH = imginfo.height
|
|
||||||
cropX = Math.floor((imginfo.width - imginfo.height) / 2)
|
|
||||||
cropW = imginfo.height
|
|
||||||
}
|
|
||||||
this.avatarUploading = true
|
|
||||||
this.$store.state.api.backendInteractor.updateAvatar({params: {img, cropX, cropY, cropW, cropH}}).then((user) => {
|
|
||||||
if (!user.error) {
|
if (!user.error) {
|
||||||
this.$store.commit('addNewUsers', [user])
|
this.$store.commit('addNewUsers', [user])
|
||||||
this.$store.commit('setCurrentUser', user)
|
this.$store.commit('setCurrentUser', user)
|
||||||
this.avatarPreview = null
|
|
||||||
} else {
|
} else {
|
||||||
this.avatarUploadError = this.$t('upload.error.base') + user.error
|
throw new Error(this.$t('upload.error.base') + user.error)
|
||||||
}
|
}
|
||||||
this.avatarUploading = false
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
clearUploadError (slot) {
|
clearUploadError (slot) {
|
||||||
|
|
|
@ -1,7 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="settings panel panel-default">
|
<div class="settings panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
{{$t('settings.user_settings')}}
|
<div class="title">
|
||||||
|
{{$t('settings.user_settings')}}
|
||||||
|
</div>
|
||||||
|
<transition name="fade">
|
||||||
|
<template v-if="currentSaveStateNotice">
|
||||||
|
<div @click.prevent class="alert error" v-if="currentSaveStateNotice.error">
|
||||||
|
{{ $t('settings.saving_err') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div @click.prevent class="alert transparent" v-if="!currentSaveStateNotice.error">
|
||||||
|
{{ $t('settings.saving_ok') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body profile-edit">
|
<div class="panel-body profile-edit">
|
||||||
<tab-switcher>
|
<tab-switcher>
|
||||||
|
@ -48,19 +61,10 @@
|
||||||
<h2>{{$t('settings.avatar')}}</h2>
|
<h2>{{$t('settings.avatar')}}</h2>
|
||||||
<p class="visibility-notice">{{$t('settings.avatar_size_instruction')}}</p>
|
<p class="visibility-notice">{{$t('settings.avatar_size_instruction')}}</p>
|
||||||
<p>{{$t('settings.current_avatar')}}</p>
|
<p>{{$t('settings.current_avatar')}}</p>
|
||||||
<img :src="user.profile_image_url_original" class="old-avatar"></img>
|
<img :src="user.profile_image_url_original" class="current-avatar"></img>
|
||||||
<p>{{$t('settings.set_new_avatar')}}</p>
|
<p>{{$t('settings.set_new_avatar')}}</p>
|
||||||
<img class="new-avatar" v-bind:src="avatarPreview" v-if="avatarPreview">
|
<button class="btn" type="button" id="pick-avatar" v-show="pickAvatarBtnVisible">{{$t('settings.upload_a_photo')}}</button>
|
||||||
</img>
|
<image-cropper trigger="#pick-avatar" :submitHandler="submitAvatar" @open="pickAvatarBtnVisible=false" @close="pickAvatarBtnVisible=true" />
|
||||||
<div>
|
|
||||||
<input type="file" @change="uploadFile('avatar', $event)" ></input>
|
|
||||||
</div>
|
|
||||||
<i class="icon-spin4 animate-spin" v-if="avatarUploading"></i>
|
|
||||||
<button class="btn btn-default" v-else-if="avatarPreview" @click="submitAvatar">{{$t('general.submit')}}</button>
|
|
||||||
<div class='alert error' v-if="avatarUploadError">
|
|
||||||
Error: {{ avatarUploadError }}
|
|
||||||
<i class="button-icon icon-cancel" @click="clearUploadError('avatar')"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<h2>{{$t('settings.profile_banner')}}</h2>
|
<h2>{{$t('settings.profile_banner')}}</h2>
|
||||||
|
@ -167,6 +171,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.profile-edit {
|
.profile-edit {
|
||||||
.bio {
|
.bio {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -193,5 +199,13 @@
|
||||||
.bg {
|
.bg {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.current-avatar {
|
||||||
|
display: block;
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
border-radius: $fallback--avatarRadius;
|
||||||
|
border-radius: var(--avatarRadius, $fallback--avatarRadius);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -21,6 +21,11 @@
|
||||||
"more": "More",
|
"more": "More",
|
||||||
"generic_error": "An error occured"
|
"generic_error": "An error occured"
|
||||||
},
|
},
|
||||||
|
"image_cropper": {
|
||||||
|
"crop_picture": "Crop picture",
|
||||||
|
"save": "Save",
|
||||||
|
"cancel": "Cancel"
|
||||||
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"login": "Log in",
|
"login": "Log in",
|
||||||
"description": "Log in with OAuth",
|
"description": "Log in with OAuth",
|
||||||
|
@ -31,6 +36,10 @@
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"hint": "Log in to join the discussion"
|
"hint": "Log in to join the discussion"
|
||||||
},
|
},
|
||||||
|
"media_modal": {
|
||||||
|
"previous": "Previous",
|
||||||
|
"next": "Next"
|
||||||
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
|
@ -206,6 +215,7 @@
|
||||||
"theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.",
|
"theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.",
|
||||||
"theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.",
|
"theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.",
|
||||||
"tooltipRadius": "Tooltips/alerts",
|
"tooltipRadius": "Tooltips/alerts",
|
||||||
|
"upload_a_photo": "Upload a photo",
|
||||||
"user_settings": "User Settings",
|
"user_settings": "User Settings",
|
||||||
"values": {
|
"values": {
|
||||||
"false": "no",
|
"false": "no",
|
||||||
|
@ -332,7 +342,8 @@
|
||||||
"repeated": "repeated",
|
"repeated": "repeated",
|
||||||
"show_new": "Show new",
|
"show_new": "Show new",
|
||||||
"up_to_date": "Up-to-date",
|
"up_to_date": "Up-to-date",
|
||||||
"no_more_statuses": "No more statuses"
|
"no_more_statuses": "No more statuses",
|
||||||
|
"no_statuses": "No statuses"
|
||||||
},
|
},
|
||||||
"user_card": {
|
"user_card": {
|
||||||
"approve": "Approve",
|
"approve": "Approve",
|
||||||
|
@ -344,7 +355,7 @@
|
||||||
"follow_sent": "Request sent!",
|
"follow_sent": "Request sent!",
|
||||||
"follow_progress": "Requesting…",
|
"follow_progress": "Requesting…",
|
||||||
"follow_again": "Send request again?",
|
"follow_again": "Send request again?",
|
||||||
"follow_unfollow": "Stop following",
|
"follow_unfollow": "Unfollow",
|
||||||
"followees": "Following",
|
"followees": "Following",
|
||||||
"followers": "Followers",
|
"followers": "Followers",
|
||||||
"following": "Following!",
|
"following": "Following!",
|
||||||
|
|
|
@ -84,12 +84,12 @@ export default function createPersistedState ({
|
||||||
setState(key, reducer(state, paths), storage)
|
setState(key, reducer(state, paths), storage)
|
||||||
.then(success => {
|
.then(success => {
|
||||||
if (typeof success !== 'undefined') {
|
if (typeof success !== 'undefined') {
|
||||||
if (mutation.type === 'setOption') {
|
if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {
|
||||||
store.dispatch('settingsSaved', { success })
|
store.dispatch('settingsSaved', { success })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, error => {
|
}, error => {
|
||||||
if (mutation.type === 'setOption') {
|
if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {
|
||||||
store.dispatch('settingsSaved', { error })
|
store.dispatch('settingsSaved', { error })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -231,8 +231,14 @@ const users = {
|
||||||
store.commit('setToken', result.access_token)
|
store.commit('setToken', result.access_token)
|
||||||
store.dispatch('loginUser', result.access_token)
|
store.dispatch('loginUser', result.access_token)
|
||||||
} else {
|
} else {
|
||||||
let data = await response.json()
|
const data = await response.json()
|
||||||
let errors = humanizeErrors(JSON.parse(data.error))
|
let errors = JSON.parse(data.error)
|
||||||
|
// replace ap_id with username
|
||||||
|
if (errors.ap_id) {
|
||||||
|
errors.username = errors.ap_id
|
||||||
|
delete errors.ap_id
|
||||||
|
}
|
||||||
|
errors = humanizeErrors(errors)
|
||||||
store.commit('signUpFailure', errors)
|
store.commit('signUpFailure', errors)
|
||||||
throw Error(errors)
|
throw Error(errors)
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,9 @@ export const parseUser = (data) => {
|
||||||
output.statuses_count = data.statuses_count
|
output.statuses_count = data.statuses_count
|
||||||
output.friends = []
|
output.friends = []
|
||||||
output.followers = []
|
output.followers = []
|
||||||
|
if (data.pleroma) {
|
||||||
|
output.follow_request_count = data.pleroma.follow_request_count
|
||||||
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
BIN
static/bg2.jpg
BIN
static/bg2.jpg
Binary file not shown.
Before Width: | Height: | Size: 224 KiB |
BIN
static/bgalt.jpg
BIN
static/bgalt.jpg
Binary file not shown.
Before Width: | Height: | Size: 323 KiB |
Loading…
Add table
Reference in a new issue