Merge branch 'develop' into 'patch-1'

# Conflicts:
#   src/i18n/messages.js
This commit is contained in:
lambda 2018-06-08 13:30:49 +00:00
commit af47d51cd1
42 changed files with 787 additions and 140 deletions

View file

@ -2,6 +2,7 @@ import UserPanel from './components/user_panel/user_panel.vue'
import NavPanel from './components/nav_panel/nav_panel.vue'
import Notifications from './components/notifications/notifications.vue'
import UserFinder from './components/user_finder/user_finder.vue'
import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue'
import ChatPanel from './components/chat_panel/chat_panel.vue'
@ -12,8 +13,9 @@ export default {
NavPanel,
Notifications,
UserFinder,
ChatPanel,
InstanceSpecificPanel
WhoToFollowPanel,
InstanceSpecificPanel,
ChatPanel
},
data: () => ({
mobileActivePanel: 'timeline'
@ -27,6 +29,7 @@ export default {
style () { return { 'background-image': `url(${this.background})` } },
sitename () { return this.$store.state.config.name },
chat () { return this.$store.state.chat.channel.state === 'joined' },
showWhoToFollowPanel () { return this.$store.state.config.showWhoToFollowPanel },
showInstanceSpecificPanel () { return this.$store.state.config.showInstanceSpecificPanel }
},
methods: {

View file

@ -88,13 +88,13 @@ label.select {
input, textarea, .select {
border: none;
border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
border-radius: $fallback--inputRadius;
border-radius: var(--inputRadius, $fallback--inputRadius);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
border-top: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0px 0px 2px black inset;
background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg);
background-color: $fallback--input;
background-color: var(--input, $fallback--input);
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
font-family: sans-serif;
@ -154,8 +154,8 @@ input, textarea, .select {
border-top: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0px 0px 2px black inset;
margin-right: .5em;
background-color: $fallback--btn;
background-color: var(--btn, $fallback--btn);
background-color: $fallback--input;
background-color: var(--input, $fallback--input);
vertical-align: top;
text-align: center;
line-height: 1.1em;

View file

@ -24,6 +24,7 @@
<user-panel></user-panel>
<nav-panel></nav-panel>
<instance-specific-panel v-if="showInstanceSpecificPanel"></instance-specific-panel>
<who-to-follow-panel v-if="currentUser && showWhoToFollowPanel"></who-to-follow-panel>
<notifications v-if="currentUser"></notifications>
</div>
</div>

View file

@ -4,7 +4,8 @@ $darkened-background: whitesmoke;
$fallback--bg: #121a24;
$fallback--btn: #182230;
$fallback--faint: #999;
$fallback--input: #182230;
$fallback--faint: rgba(185, 185, 186, .5);
$fallback--fg: #b9b9ba;
$fallback--link: #d8a070;
$fallback--icon: #666;
@ -21,6 +22,7 @@ $fallback--cAlertRed: rgba(211,16,20,.5);
$fallback--panelRadius: 10px;
$fallback--checkBoxRadius: 2px;
$fallback--btnRadius: 4px;
$fallback--inputRadius: 4px;
$fallback--tooltipRadius: 5px;
$fallback--avatarRadius: 4px;
$fallback--avatarAltRadius: 10px;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

View file

@ -96,6 +96,9 @@
background: rgba(230,230,230,0.6);
font-weight: bold;
z-index: 4;
line-height: 1;
border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
}
.small {

View file

@ -3,7 +3,7 @@
<div class="panel-heading conversation-heading">
{{ $t('timeline.conversation') }}
<span v-if="collapsable" style="float:right;">
<small><a href="#" @click.prevent="$emit('toggleExpanded')">Collapse</a></small>
<small><a href="#" @click.prevent="$emit('toggleExpanded')">{{ $t('timeline.collapse') }}</a></small>
</span>
</div>
<div class="panel-body">

View file

@ -45,8 +45,6 @@
border-bottom: 1px solid;
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
padding: 0;
&:first-child a {

View file

@ -98,7 +98,7 @@
.status {
padding: 0.25em 0;
color: $fallback--faint;
color: var($fallback--faint, --faint);
color: var(--faint, $fallback--faint);
}
padding: 0;
.media-body {

View file

@ -48,12 +48,21 @@ const PostStatusForm = {
highlighted: 0,
newStatus: {
status: statusText,
files: []
files: [],
visibility: 'public'
},
caret: 0
}
},
computed: {
vis () {
return {
public: { selected: this.newStatus.visibility === 'public' },
unlisted: { selected: this.newStatus.visibility === 'unlisted' },
private: { selected: this.newStatus.visibility === 'private' },
direct: { selected: this.newStatus.visibility === 'direct' }
}
},
candidates () {
const firstchar = this.textAtCaret.charAt(0)
if (firstchar === '@') {
@ -118,6 +127,9 @@ const PostStatusForm = {
},
isOverLengthLimit () {
return this.hasStatusLengthLimit && (this.statusLength > this.statusLengthLimit)
},
scopeOptionsEnabled () {
return this.$store.state.config.scopeOptionsEnabled
}
},
methods: {
@ -185,6 +197,8 @@ const PostStatusForm = {
this.posting = true
statusPoster.postStatus({
status: newStatus.status,
spoilerText: newStatus.spoilerText || null,
visibility: newStatus.visibility,
media: newStatus.files,
store: this.$store,
inReplyToStatusId: this.replyTo
@ -192,7 +206,8 @@ const PostStatusForm = {
if (!data.error) {
this.newStatus = {
status: '',
files: []
files: [],
visibility: newStatus.visibility
}
this.$emit('posted')
let el = this.$el.querySelector('textarea')
@ -239,18 +254,20 @@ const PostStatusForm = {
e.dataTransfer.dropEffect = 'copy'
},
resize (e) {
const target = e.target || e
target.style.height = 'auto'
const heightPx = target.scrollHeight - 10
if (heightPx > 54) {
target.style.height = `${target.scrollHeight - 10}px`
}
if (target.value === '') {
target.style.height = '16px'
if (!e.target) { return }
const vertPadding = Number(window.getComputedStyle(e.target)['padding-top'].substr(0, 1)) +
Number(window.getComputedStyle(e.target)['padding-bottom'].substr(0, 1))
e.target.style.height = 'auto'
e.target.style.height = `${e.target.scrollHeight - vertPadding}px`
if (e.target.value === '') {
e.target.style.height = '16px'
}
},
clearError () {
this.error = null
},
changeVis (visibility) {
this.newStatus.visibility = visibility
}
}
}

View file

@ -2,6 +2,12 @@
<div class="post-status-form">
<form @submit.prevent="postStatus(newStatus)">
<div class="form-group" >
<input
v-if="scopeOptionsEnabled"
type="text"
:placeholder="$t('post_status.content_warning')"
v-model="newStatus.spoilerText"
class="form-cw">
<textarea
ref="textarea"
@click="setCaret"
@ -18,16 +24,17 @@
@input="resize"
@paste="paste">
</textarea>
<div v-if="scopeOptionsEnabled" class="visibility-tray">
<i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct"></i>
<i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private"></i>
<i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted"></i>
<i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public"></i>
</div>
</div>
<div style="position:relative;" v-if="candidates">
<div class="autocomplete-panel">
<div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))">
<div v-if="candidate.highlighted" class="autocomplete">
<span v-if="candidate.img"><img :src="candidate.img"></span>
<span v-else>{{candidate.utf}}</span>
<span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
</div>
<div v-else class="autocomplete">
<div class="autocomplete" :class="{ highlighted: candidate.highlighted }">
<span v-if="candidate.img"><img :src="candidate.img"></img></span>
<span v-else>{{candidate.utf}}</span>
<span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
@ -84,6 +91,17 @@
}
}
.post-status-form .visibility-tray {
font-size: 1.2em;
padding: 3px;
cursor: pointer;
.selected {
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
}
}
.post-status-form, .login {
.form-bottom {
display: flex;
@ -135,10 +153,6 @@
cursor: not-allowed;
}
.icon-cancel {
cursor: pointer;
}
form {
display: flex;
flex-direction: column;
@ -152,7 +166,15 @@
line-height:24px;
}
form textarea {
form textarea.form-cw {
line-height:16px;
resize: none;
overflow: hidden;
transition: min-height 200ms 100ms;
min-height: 1px;
}
form textarea.form-control {
line-height:16px;
resize: none;
overflow: hidden;
@ -161,7 +183,7 @@
box-sizing: content-box;
}
form textarea:focus {
form textarea.form-control:focus {
min-height: 48px;
}
@ -186,8 +208,8 @@
z-index: 1;
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
min-width: 75%;
background: $fallback--btn;
background: var(--btn, $fallback--btn);
background: $fallback--bg;
background: var(--bg, $fallback--bg);
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
}
@ -216,6 +238,11 @@
color: $fallback--faint;
color: var(--faint, $fallback--faint);
}
&.highlighted {
background-color: $fallback--btn;
background-color: var(--btn, $fallback--btn);
}
}
}
</style>

View file

@ -104,6 +104,18 @@ const Status = {
StillImage
},
methods: {
visibilityIcon (visibility) {
switch (visibility) {
case 'private':
return 'icon-lock'
case 'unlisted':
return 'icon-lock-open-alt'
case 'direct':
return 'icon-mail-alt'
default:
return 'icon-globe'
}
},
linkClicked ({target}) {
if (target.tagName === 'SPAN') {
target = target.parentNode

View file

@ -55,6 +55,7 @@
<router-link class="timeago" :to="{ name: 'conversation', params: { id: status.id } }">
<timeago :since="status.created_at" :auto-update="60"></timeago>
</router-link>
<span v-if="status.visibility"><i :class="visibilityIcon(status.visibility)"></i> </span>
<a :href="status.external_url" target="_blank" v-if="!status.is_local" class="source_url"><i class="icon-link-ext"></i></a>
<template v-if="expandable">
<a href="#" @click.prevent="toggleExpanded"><i class="icon-plus-squared"></i></a>
@ -165,8 +166,6 @@
border-left-width: 0px;
line-height: 18px;
min-width: 0;
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
@ -189,6 +188,10 @@
margin: 0 0 0.25em 0.8em;
}
.usercard {
margin-bottom: .7em
}
.media-heading {
flex-wrap: nowrap;
}

View file

@ -14,6 +14,7 @@ export default {
greenColorLocal: '',
orangeColorLocal: '',
btnRadiusLocal: '',
inputRadiusLocal: '',
panelRadiusLocal: '',
avatarRadiusLocal: '',
avatarAltRadiusLocal: '',
@ -42,6 +43,7 @@ export default {
this.orangeColorLocal = rgbstr2hex(this.$store.state.config.colors.cOrange)
this.btnRadiusLocal = this.$store.state.config.radii.btnRadius || 4
this.inputRadiusLocal = this.$store.state.config.radii.inputRadius || 4
this.panelRadiusLocal = this.$store.state.config.radii.panelRadius || 10
this.avatarRadiusLocal = this.$store.state.config.radii.avatarRadius || 5
this.avatarAltRadiusLocal = this.$store.state.config.radii.avatarAltRadius || 50
@ -85,6 +87,7 @@ export default {
cGreen: greenRgb,
cOrange: orangeRgb,
btnRadius: this.btnRadiusLocal,
inputRadius: this.inputRadiusLocal,
panelRadius: this.panelRadiusLocal,
avatarRadius: this.avatarRadiusLocal,
avatarAltRadius: this.avatarAltRadiusLocal,

View file

@ -58,6 +58,11 @@
<input id="btnradius" class="theme-radius-rn" type="range" v-model="btnRadiusLocal" max="16">
<input id="btnradius-t" class="theme-radius-in" type="text" v-model="btnRadiusLocal">
</div>
<div class="radius-item">
<label for="inputradius" class="theme-radius-lb">{{$t('settings.inputRadius')}}</label>
<input id="inputradius" class="theme-radius-rn" type="range" v-model="inputRadiusLocal" max="16">
<input id="inputradius-t" class="theme-radius-in" type="text" v-model="inputRadiusLocal">
</div>
<div class="radius-item">
<label for="panelradius" class="theme-radius-lb">{{$t('settings.panelRadius')}}</label>
<input id="panelradius" class="theme-radius-rn" type="range" v-model="panelRadiusLocal" max="50">
@ -86,6 +91,7 @@
</div>
<div :style="{
'--btnRadius': btnRadiusLocal + 'px',
'--inputRadius': inputRadiusLocal + 'px',
'--panelRadius': panelRadiusLocal + 'px',
'--avatarRadius': avatarRadiusLocal + 'px',
'--avatarAltRadius': avatarAltRadiusLocal + 'px',

View file

@ -2,16 +2,24 @@ import StillImage from '../still-image/still-image.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js'
export default {
props: [ 'user', 'switcher', 'hideBio' ],
props: [ 'user', 'switcher', 'selected', 'hideBio' ],
computed: {
headingStyle () {
const color = this.$store.state.config.colors.bg
if (color) {
const rgb = hex2rgb(color)
const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)`
console.log(rgb)
console.log([
`url(${this.user.cover_photo})`,
`linear-gradient(to bottom, ${tintColor}, ${tintColor})`
].join(', '))
return {
backgroundColor: `rgb(${Math.floor(rgb[0] * 0.53)}, ${Math.floor(rgb[1] * 0.56)}, ${Math.floor(rgb[2] * 0.59)})`,
backgroundImage: `url(${this.user.cover_photo})`
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(', ')
}
}
},
@ -61,8 +69,10 @@ export default {
store.state.api.backendInteractor.setUserMute(this.user)
},
setProfileView (v) {
const store = this.$store
store.commit('setProfileView', { v })
if (this.switcher) {
const store = this.$store
store.commit('setProfileView', { v })
}
}
}
}

View file

@ -14,8 +14,9 @@
</router-link>
<div class="name-and-screen-name">
<div :title="user.name" class='user-name'>{{user.name}}</div>
<router-link :to="{ name: 'user-profile', params: { id: user.id } }">
<div class='user-screen-name'>@{{user.screen_name}}</div>
<router-link class='user-screen-name':to="{ name: 'user-profile', params: { id: user.id } }">
<span>@{{user.screen_name}}</span>
<span class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span>
</router-link>
</div>
</div>
@ -73,20 +74,17 @@
</div>
</div>
<div class="panel-body profile-panel-body">
<div class="user-counts">
<div class="user-count">
<a href="#" v-on:click.prevent="setProfileView('statuses')" v-if="switcher"><h5>{{ $t('user_card.statuses') }}</h5></a>
<h5 v-else>{{ $t('user_card.statuses') }}</h5>
<span>{{user.statuses_count}} <br><span class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span></span>
<div class="user-counts" :class="{clickable: switcher}">
<div class="user-count" v-on:click.prevent="setProfileView('statuses')" :class="{selected: selected === 'statuses'}">
<h5>{{ $t('user_card.statuses') }}</h5>
<span>{{user.statuses_count}} <br></span>
</div>
<div class="user-count">
<a href="#" v-on:click.prevent="setProfileView('friends')" v-if="switcher"><h5>{{ $t('user_card.followees') }}</h5></a>
<h5 v-else>{{ $t('user_card.followees') }}</h5>
<div class="user-count" v-on:click.prevent="setProfileView('friends')" :class="{selected: selected === 'friends'}">
<h5>{{ $t('user_card.followees') }}</h5>
<span>{{user.friends_count}}</span>
</div>
<div class="user-count">
<a href="#" v-on:click.prevent="setProfileView('followers')" v-if="switcher"><h5>{{ $t('user_card.followers') }}</h5></a>
<h5 v-else>{{ $t('user_card.followers') }}</h5>
<div class="user-count" v-on:click.prevent="setProfileView('followers')" :class="{selected: selected === 'followers'}">
<h5>{{ $t('user_card.followers') }}</h5>
<span>{{user.followers_count}}</span>
</div>
</div>
@ -112,20 +110,18 @@
}
.profile-panel-body {
top: -0em;
padding-top: 4em;
word-wrap: break-word;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%);
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%)
}
.user-info {
color: white;
padding: 0 16px 16px 16px;
margin-bottom: -4em;
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
padding: 0 16px;
.container {
padding: 16px 10px 4px 10px;
padding: 16px 10px 6px 10px;
display: flex;
max-height: 56px;
overflow: hidden;
@ -154,10 +150,9 @@
}
}
text-shadow: 0px 1px 1.5px rgba(0, 0, 0, 1.0);
.usersettings {
color: #fff;
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
opacity: .8;
}
@ -171,14 +166,15 @@
}
.user-name{
color: white;
text-overflow: ellipsis;
overflow: hidden;
}
.user-screen-name {
color: white;
font-weight: lighter;
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
display: inline-block;
font-weight: light;
font-size: 15px;
padding-right: 0.1em;
}
@ -191,14 +187,11 @@
div {
flex: 1;
}
margin-top: 0.7em;
margin-bottom: -1.0em;
.following {
color: white;
font-size: 14px;
flex: 0 0 100%;
margin: -0.7em 0.0em 0.3em 0.0em;
margin: 0 0 .4em 0;
padding-left: 16px;
text-align: left;
}
@ -238,12 +231,37 @@
.user-counts {
display: flex;
line-height:16px;
padding: 1em 1.5em 0em 1em;
padding: .5em 1.5em 0em 1.5em;
text-align: center;
justify-content: space-between;
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
&.clickable {
.user-count {
cursor: pointer;
&:hover:not(.selected) {
transition: border-bottom 100ms;
border-bottom: 3px solid $fallback--link;
border-bottom: 3px solid var(--link, $fallback--link);
}
}
}
}
.user-count {
flex: 1;
padding: .5em 0 .5em 0;
margin: 0 .5em;
&.selected {
transition: none;
border-bottom: 5px solid $fallback--link;
border-bottom: 5px solid var(--link, $fallback--link);
border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
}
h5 {
font-size:1em;
@ -256,7 +274,8 @@
}
.dailyAvg {
font-size: 0.8em;
opacity: 0.5;
margin-left: 1em;
font-size: 0.7em;
color: #CCC;
}
</style>

View file

@ -1,7 +1,7 @@
<template>
<div>
<div v-if="user" class="user-profile panel panel-default">
<user-card-content :user="user" :switcher="true"></user-card-content>
<user-card-content :user="user" :switcher="true" :selected="timeline.viewing"></user-card-content>
</div>
<Timeline :title="$t('user_profile.timeline_title')" :timeline="timeline" :timeline-name="'user'" :user-id="userId"/>
</div>

View file

@ -8,8 +8,15 @@ const UserSettings = {
followList: null,
followImportError: false,
followsImported: false,
enableFollowsExport: true,
uploading: [ false, false, false, false ],
previews: [ null, null, null ]
previews: [ null, null, null ],
deletingAccount: false,
deleteAccountConfirmPasswordInput: '',
deleteAccountError: false,
changePasswordInputs: [ '', '', '' ],
changedPassword: false,
changePasswordError: false
}
},
components: {
@ -137,6 +144,37 @@ const UserSettings = {
this.uploading[3] = false
})
},
/* This function takes an Array of Users
* and outputs a file with all the addresses for the user to download
*/
exportPeople (users, filename) {
// Get all the friends addresses
var UserAddresses = users.map(function (user) {
// check is it's a local user
if (user && user.is_local) {
// append the instance address
// eslint-disable-next-line no-undef
user.screen_name += '@' + location.hostname
}
return user.screen_name
}).join('\n')
// Make the user download the file
var fileToDownload = document.createElement('a')
fileToDownload.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(UserAddresses))
fileToDownload.setAttribute('download', filename)
fileToDownload.style.display = 'none'
document.body.appendChild(fileToDownload)
fileToDownload.click()
document.body.removeChild(fileToDownload)
},
exportFollows () {
this.enableFollowsExport = false
this.$store.state.api.backendInteractor
.fetchFriends({id: this.$store.state.users.currentUser.id})
.then((friendList) => {
this.exportPeople(friendList, 'friends.csv')
})
},
followListChange () {
// eslint-disable-next-line no-undef
let formData = new FormData()
@ -146,6 +184,37 @@ const UserSettings = {
dismissImported () {
this.followsImported = false
this.followImportError = false
},
confirmDelete () {
this.deletingAccount = true
},
deleteAccount () {
this.$store.state.api.backendInteractor.deleteAccount({password: this.deleteAccountConfirmPasswordInput})
.then((res) => {
if (res.status === 'success') {
this.$store.dispatch('logout')
this.$router.push('/main/all')
} else {
this.deleteAccountError = res.error
}
})
},
changePassword () {
const params = {
password: this.changePasswordInputs[0],
newPassword: this.changePasswordInputs[1],
newPasswordConfirmation: this.changePasswordInputs[2]
}
this.$store.state.api.backendInteractor.changePassword(params)
.then((res) => {
if (res.status === 'success') {
this.changedPassword = true
this.changePasswordError = false
} else {
this.changedPassword = false
this.changePasswordError = res.error
}
})
}
}
}

View file

@ -49,6 +49,25 @@
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[2]"></i>
<button class="btn btn-default" v-else-if="previews[2]" @click="submitBg">{{$t('general.submit')}}</button>
</div>
<div class="setting-item">
<h3>{{$t('settings.change_password')}}</h3>
<div>
<p>{{$t('settings.current_password')}}</p>
<input type="password" v-model="changePasswordInputs[0]">
</div>
<div>
<p>{{$t('settings.new_password')}}</p>
<input type="password" v-model="changePasswordInputs[1]">
</div>
<div>
<p>{{$t('settings.confirm_new_password')}}</p>
<input type="password" v-model="changePasswordInputs[2]">
</div>
<button class="btn btn-default" @click="changePassword">{{$t('general.submit')}}</button>
<p v-if="changedPassword">{{$t('settings.changed_password')}}</p>
<p v-else-if="changePasswordError !== false">{{$t('settings.change_password_error')}}</p>
<p v-if="changePasswordError">{{changePasswordError}}</p>
</div>
<div class="setting-item" v-if="pleromaBackend">
<h3>{{$t('settings.follow_import')}}</h3>
<p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
@ -62,10 +81,31 @@
<p>{{$t('settings.follows_imported')}}</p>
</div>
<div v-else-if="followImportError">
<i class="icon-cross" @click="dismissImported"</i>
<i class="icon-cross" @click="dismissImported"></i>
<p>{{$t('settings.follow_import_error')}}</p>
</div>
</div>
<div class="setting-item" v-if="enableFollowsExport">
<h3>{{$t('settings.follow_export')}}</h3>
<button class="btn btn-default" @click="exportFollows">{{$t('settings.follow_export_button')}}</button>
</div>
<div class="setting-item" v-else>
<h3>{{$t('settings.follow_export_processing')}}</h3>
</div>
<hr>
<div class="setting-item">
<h3>{{$t('settings.delete_account')}}</h3>
<p v-if="!deletingAccount">{{$t('settings.delete_account_description')}}</p>
<div v-if="deletingAccount">
<p>{{$t('settings.delete_account_instructions')}}</p>
<p>{{$t('login.password')}}</p>
<input type="password" v-model="deleteAccountConfirmPasswordInput">
<button class="btn btn-default" @click="deleteAccount">{{$t('settings.delete_account')}}</button>
</div>
<p v-if="deleteAccountError !== false">{{$t('settings.delete_account_error')}}</p>
<p v-if="deleteAccountError">{{deleteAccountError}}</p>
<button class="btn btn-default" v-if="!deletingAccount" @click="confirmDelete">{{$t('general.submit')}}</button>
</div>
</div>
</div>
</template>

View file

@ -0,0 +1,123 @@
function showWhoToFollow (panel, reply, aHost, aUser) {
var users = reply.ids
var cn
var index = 0
var random = Math.floor(Math.random() * 10)
for (cn = random; cn < users.length; cn = cn + 10) {
var user
user = users[cn]
var img
if (user.icon) {
img = user.icon
} else {
img = '/images/avi.png'
}
var name = user.to_id
if (index === 0) {
panel.img1 = img
panel.name1 = name
panel.$store.state.api.backendInteractor.externalProfile(name)
.then((externalUser) => {
if (!externalUser.error) {
panel.$store.commit('addNewUsers', [externalUser])
panel.id1 = externalUser.id
}
})
} else if (index === 1) {
panel.img2 = img
panel.name2 = name
panel.$store.state.api.backendInteractor.externalProfile(name)
.then((externalUser) => {
if (!externalUser.error) {
panel.$store.commit('addNewUsers', [externalUser])
panel.id2 = externalUser.id
}
})
} else if (index === 2) {
panel.img3 = img
panel.name3 = name
panel.$store.state.api.backendInteractor.externalProfile(name)
.then((externalUser) => {
if (!externalUser.error) {
panel.$store.commit('addNewUsers', [externalUser])
panel.id3 = externalUser.id
}
})
}
index = index + 1
if (index > 2) {
break
}
}
}
function getWhoToFollow (panel) {
var user = panel.$store.state.users.currentUser.screen_name
if (user) {
panel.name1 = 'Loading...'
panel.name2 = 'Loading...'
panel.name3 = 'Loading...'
var host = window.location.hostname
var whoToFollowProvider = panel.$store.state.config.whoToFollowProvider
var url
url = whoToFollowProvider.replace(/{{host}}/g, encodeURIComponent(host))
url = url.replace(/{{user}}/g, encodeURIComponent(user))
window.fetch(url, {mode: 'cors'}).then(function (response) {
if (response.ok) {
return response.json()
} else {
panel.name1 = ''
panel.name2 = ''
panel.name3 = ''
}
}).then(function (reply) {
showWhoToFollow(panel, reply, host, user)
})
}
}
const WhoToFollowPanel = {
data: () => ({
img1: '/images/avi.png',
name1: '',
id1: 0,
img2: '/images/avi.png',
name2: '',
id2: 0,
img3: '/images/avi.png',
name3: '',
id3: 0
}),
computed: {
user: function () {
return this.$store.state.users.currentUser.screen_name
},
moreUrl: function () {
var host = window.location.hostname
var user = this.user
var whoToFollowLink = this.$store.state.config.whoToFollowLink
var url
url = whoToFollowLink.replace(/{{host}}/g, encodeURIComponent(host))
url = url.replace(/{{user}}/g, encodeURIComponent(user))
return url
},
showWhoToFollowPanel () {
return this.$store.state.config.showWhoToFollowPanel
}
},
watch: {
user: function (user, oldUser) {
if (this.showWhoToFollowPanel) {
getWhoToFollow(this)
}
}
},
mounted:
function () {
if (this.showWhoToFollowPanel) {
getWhoToFollow(this)
}
}
}
export default WhoToFollowPanel

View file

@ -0,0 +1,37 @@
<template>
<div class="who-to-follow-panel">
<div class="panel panel-default base01-background">
<div class="panel-heading timeline-heading base02-background base04">
<div class="title">
Who to follow
</div>
</div>
<div class="panel-body who-to-follow">
<p>
<img v-bind:src="img1"/> <router-link :to="{ name: 'user-profile', params: { id: id1 } }">{{ name1 }}</router-link><br>
<img v-bind:src="img2"/> <router-link :to="{ name: 'user-profile', params: { id: id2 } }">{{ name2 }}</router-link><br>
<img v-bind:src="img3"/> <router-link :to="{ name: 'user-profile', params: { id: id3 } }">{{ name3 }}</router-link><br>
<img v-bind:src="$store.state.config.logo"> <a v-bind:href="moreUrl" target="_blank">More</a>
</p>
</div>
</div>
</div>
</template>
<script src="./who_to_follow_panel.js" ></script>
<style lang="scss">
.who-to-follow * {
vertical-align: middle;
}
.who-to-follow img {
width: 32px;
height: 32px;
}
.who-to-follow p {
line-height: 40px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View file

@ -270,6 +270,7 @@ const en = {
cOrange: 'Orange (Favorite)',
cGreen: 'Green (Retweet)',
btnRadius: 'Buttons',
inputRadius: 'Input fields',
panelRadius: 'Panels',
avatarRadius: 'Avatars',
avatarAltRadius: 'Avatars (Notifications)',
@ -288,7 +289,20 @@ const en = {
follow_import: 'Follow import',
import_followers_from_a_csv_file: 'Import follows from a csv file',
follows_imported: 'Follows imported! Processing them will take a while.',
follow_import_error: 'Error importing followers'
follow_import_error: 'Error importing followers',
delete_account: 'Delete Account',
delete_account_description: 'Permanently delete your account and all your messages.',
delete_account_instructions: 'Type your password in the input below to confirm account deletion.',
delete_account_error: 'There was an issue deleting your account. If this persists please contact your instance administrator.',
follow_export: 'Follow export',
follow_export_processing: 'Processing, you\'ll soon be asked to download your file',
follow_export_button: 'Export your follows to a csv file',
change_password: 'Change Password',
current_password: 'Current password',
new_password: 'New password',
confirm_new_password: 'Confirm new password',
changed_password: 'Password changed successfully!',
change_password_error: 'There was an issue changing your password.'
},
notifications: {
notifications: 'Notifications',
@ -313,6 +327,7 @@ const en = {
},
post_status: {
posting: 'Posting',
content_warning: 'Content warning (optional)',
default: 'Just landed in L.A.'
},
finder: {
@ -830,8 +845,8 @@ const fr = {
blocked: 'Bloqué',
block: 'Bloquer',
statuses: 'Statuts',
mute: 'Mettre en muet',
muted: 'Mis en muet',
mute: 'Masquer',
muted: 'Masqué',
followers: 'Vous suivent',
followees: 'Suivis',
per_day: 'par jour',
@ -839,7 +854,7 @@ const fr = {
},
timeline: {
show_new: 'Afficher plus',
error_fetching: 'Erreur en cherchant des mises à jours',
error_fetching: 'Erreur en cherchant les mises à jour',
up_to_date: 'À jour',
load_older: 'Afficher plus',
conversation: 'Conversation',
@ -850,32 +865,32 @@ const fr = {
user_settings: 'Paramètres utilisateur',
name_bio: 'Nom & Bio',
name: 'Nom',
bio: 'Bioraphie',
bio: 'Biographie',
avatar: 'Avatar',
current_avatar: 'Votre avatar',
current_avatar: 'Avatar actuel',
set_new_avatar: 'Changer d\'avatar',
profile_banner: 'Bannière du profil',
current_profile_banner: 'Bannière du profil',
profile_banner: 'Bannière de profil',
current_profile_banner: 'Bannière de profil actuelle',
set_new_profile_banner: 'Changer de bannière',
profile_background: 'Image de fond',
set_new_profile_background: 'Changer d\'image de fond',
settings: 'Paramètres',
theme: 'Thème',
filtering: 'Filtre',
filtering_explanation: 'Tout les statuts contenant ces mots vont être cachés, un mot par ligne.',
filtering_explanation: 'Tout les statuts contenant ces mots seront masqués. Un mot par ligne.',
attachments: 'Pièces jointes',
hide_attachments_in_tl: 'Cacher les pièces jointes dans le journal',
hide_attachments_in_convo: 'Cacher les pièces jointes dans les conversations',
nsfw_clickthrough: 'Activer le clic pour afficher les images marquées comme contenu adulte ou sensible',
autoload: 'Activer le chargement automatique une fois le bas de la page atteint',
reply_link_preview: 'Activer un aperçu d\'une réponse sur passage de la souris',
hide_attachments_in_tl: 'Masquer les pièces jointes dans le journal',
hide_attachments_in_convo: 'Masquer les pièces jointes dans les conversations',
nsfw_clickthrough: 'Masquer les images marquées comme contenu adulte ou sensible',
autoload: 'Charger la suite automatiquement une fois le bas de la page atteint',
reply_link_preview: 'Afficher un aperçu lors du survol de liens vers une réponse',
presets: 'Thèmes prédéfinis',
theme_help: 'Utilisez les codes de couleur hexadécimaux (#aabbcc) pour customiser les couleurs de votre thème.',
theme_help: 'Spécifiez des codes couleur hexadécimaux (#aabbcc) pour personnaliser les couleurs du thème',
background: 'Arrière plan',
foreground: 'Premier plan',
text: 'Texte',
links: 'Liens',
streaming: 'Active le défilement automatique de nouveaux statuts lorsqu\'on est au haut de la page',
streaming: 'Charger automatiquement les nouveaux statuts lorsque vous êtes au haut de la page',
follow_import: 'Importer ses abonnements',
import_followers_from_a_csv_file: 'Importer ses abonnements depuis un fichier csv',
follows_imported: 'Abonnements importés ! Le traitement peut prendre un moment.',
@ -886,33 +901,34 @@ const fr = {
cGreen: 'Vert (Partager)',
btnRadius: 'Boutons',
panelRadius: 'Fenêtres',
inputRadius: 'Champs de texte',
avatarRadius: 'Avatars',
avatarAltRadius: 'Avatars (Notifications)',
tooltipRadius: 'Info-bulles/alertes ',
attachmentRadius: 'Pièces jointes',
radii_help: 'Mettre en place l\'arondissement des coins de l\'interface (en pixels)',
stop_gifs: 'Passer la souris sur un GIF pour l\'animer'
radii_help: 'Vous pouvez ici choisir le niveau d\'arrondi des angles de l\'interface (en pixels)',
stop_gifs: 'N\'animer les GIFS que lors du survol du curseur de la souris'
},
notifications: {
notifications: 'Notifications',
read: 'Lu !',
followed_you: 'vous a suivi',
followed_you: 'a commencé à vous suivre',
favorited_you: 'a aimé votre statut',
repeated_you: 'a partagé votre statut'
},
login: {
login: 'Connexion',
username: 'Nom d\'utilisateur',
username: 'Identifiant',
password: 'Mot de passe',
register: 'S\'inscrire',
logout: 'Déconnexion'
},
registration: {
registration: 'Inscription',
fullname: 'Nom affiché',
fullname: 'Pseudonyme',
email: 'Adresse email',
bio: 'Biographie',
password_confirm: 'Confirmez le mot de passe'
password_confirm: 'Confirmation du mot de passe'
},
post_status: {
posting: 'Envoi en cours',
@ -920,7 +936,7 @@ const fr = {
},
finder: {
find_user: 'Chercher un utilisateur',
error_fetching_user: 'Une erreur est survenue lors de la recherche de l\'utilisateur'
error_fetching_user: 'Erreur lors de la recherche de l\'utilisateur'
},
general: {
submit: 'Envoyer',
@ -1017,7 +1033,7 @@ const oc = {
timeline: {
show_new: 'Ne veire mai',
error_fetching: 'Error en cercant de mesas a jorn',
up_to_date: 'Actualizat',
up_to_date: 'A jorn',
load_older: 'Ne veire mai',
conversation: 'Conversacion',
collapse: 'Tampar',
@ -1049,6 +1065,7 @@ const oc = {
cRed: 'Roge (Anullar)',
cOrange: 'Irange (Metre en favorit)',
cGreen: 'Verd (Repartajar)',
inputRadius: 'Camps tèxte',
btnRadius: 'Botons',
panelRadius: 'Panèls',
avatarRadius: 'Avatars',
@ -1104,7 +1121,7 @@ const oc = {
apply: 'Aplicar'
},
user_profile: {
timeline_title: 'Flux a lutilizaire'
timeline_title: 'Flux utilizaire'
}
}
@ -1169,13 +1186,14 @@ const pl = {
cOrange: 'Pomarańczowy (ulubione)',
cGreen: 'Zielony (powtórzenia)',
btnRadius: 'Przyciski',
inputRadius: 'Pola tekstowe',
panelRadius: 'Panele',
avatarRadius: 'Awatary',
avatarAltRadius: 'Awatary (powiadomienia)',
tooltipRadius: 'Etykiety/alerty',
attachmentRadius: 'Załączniki',
filtering: 'Filtrowanie',
filtering_explanation: 'Wszystkie statusy zawierające te słowa będą wyciszone. Jedno słowo na linijkę',
filtering_explanation: 'Wszystkie statusy zawierające te słowa będą wyciszone. Jedno słowo na linijkę.',
attachments: 'Załączniki',
hide_attachments_in_tl: 'Ukryj załączniki w osi czasu',
hide_attachments_in_convo: 'Ukryj załączniki w rozmowach',
@ -1187,7 +1205,20 @@ const pl = {
follow_import: 'Import obserwowanych',
import_followers_from_a_csv_file: 'Importuj obserwowanych z pliku CSV',
follows_imported: 'Obserwowani zaimportowani! Przetwarzanie może trochę potrwać.',
follow_import_error: 'Błąd przy importowaniu obserwowanych'
follow_import_error: 'Błąd przy importowaniu obserwowanych',
delete_account: 'Usuń konto',
delete_account_description: 'Trwale usuń konto i wszystkie posty.',
delete_account_instructions: 'Wprowadź swoje hasło w poniższe pole aby potwierdzić usunięcie konta.',
delete_account_error: 'Wystąpił problem z usuwaniem twojego konta. Jeżeli problem powtarza się, poinformuj administratora swojej instancji.',
follow_export: 'Eksport obserwowanych',
follow_export_processing: 'Przetwarzanie, wkrótce twój plik zacznie się ściągać.',
follow_export_button: 'Eksportuj swoją listę obserwowanych do pliku CSV',
change_password: 'Zmień hasło',
current_password: 'Obecne hasło',
new_password: 'Nowe hasło',
confirm_new_password: 'Potwierdź nowe hasło',
changed_password: 'Hasło zmienione poprawnie!',
change_password_error: 'Podczas zmiany hasła wystąpił problem.'
},
notifications: {
notifications: 'Powiadomienia',
@ -1505,6 +1536,7 @@ const ru = {
cOrange: 'Нравится',
cGreen: 'Повторить',
btnRadius: 'Кнопки',
inputRadius: 'Поля ввода',
panelRadius: 'Панели',
avatarRadius: 'Аватары',
avatarAltRadius: 'Аватары в уведомлениях',
@ -1681,6 +1713,139 @@ const nb = {
}
}
const he = {
chat: {
title: 'צ\'אט'
},
nav: {
chat: 'צ\'אט מקומי',
timeline: 'ציר הזמן',
mentions: 'אזכורים',
public_tl: 'ציר הזמן הציבורי',
twkn: 'כל הרשת הידועה'
},
user_card: {
follows_you: 'עוקב אחריך!',
following: 'עוקב!',
follow: 'עקוב',
blocked: 'חסום!',
block: 'חסימה',
statuses: 'סטטוסים',
mute: 'השתק',
muted: 'מושתק',
followers: 'עוקבים',
followees: 'נעקבים',
per_day: 'ליום',
remote_follow: 'עקיבה מרחוק'
},
timeline: {
show_new: 'הראה חדש',
error_fetching: 'שגיאה בהבאת הודעות',
up_to_date: 'עדכני',
load_older: 'טען סטטוסים חדשים',
conversation: 'שיחה',
collapse: 'מוטט',
repeated: 'חזר'
},
settings: {
user_settings: 'הגדרות משתמש',
name_bio: 'שם ואודות',
name: 'שם',
bio: 'אודות',
avatar: 'תמונת פרופיל',
current_avatar: 'תמונת הפרופיל הנוכחית שלך',
set_new_avatar: 'קבע תמונת פרופיל חדשה',
profile_banner: 'כרזת הפרופיל',
current_profile_banner: 'כרזת הפרופיל הנוכחית שלך',
set_new_profile_banner: 'קבע כרזת פרופיל חדשה',
profile_background: 'רקע הפרופיל',
set_new_profile_background: 'קבע רקע פרופיל חדש',
settings: 'הגדרות',
theme: 'תמה',
presets: 'ערכים קבועים מראש',
theme_help: 'השתמש בקודי צבע הקס (#אדום-אדום-ירוק-ירוק-כחול-כחול) על מנת להתאים אישית את תמת הצבע שלך.',
radii_help: 'קבע מראש עיגול פינות לממשק (בפיקסלים)',
background: 'רקע',
foreground: 'חזית',
text: 'טקסט',
links: 'לינקים',
cBlue: 'כחול (תגובה, עקיבה)',
cRed: 'אדום (ביטול)',
cOrange: 'כתום (לייק)',
cGreen: 'ירוק (חזרה)',
btnRadius: 'כפתורים',
inputRadius: 'שדות קלט',
panelRadius: 'פאנלים',
avatarRadius: 'תמונות פרופיל',
avatarAltRadius: 'תמונות פרופיל (התראות)',
tooltipRadius: 'טולטיפ \\ התראות',
attachmentRadius: 'צירופים',
filtering: 'סינון',
filtering_explanation: 'כל הסטטוסים הכוללים את המילים הללו יושתקו, אחד לשורה',
attachments: 'צירופים',
hide_attachments_in_tl: 'החבא צירופים בציר הזמן',
hide_attachments_in_convo: 'החבא צירופים בשיחות',
nsfw_clickthrough: 'החל החבאת צירופים לא בטוחים לצפיה בעת עבודה בעזרת לחיצת עכבר',
stop_gifs: 'נגן-בעת-ריחוף GIFs',
autoload: 'החל טעינה אוטומטית בגלילה לתחתית הדף',
streaming: 'החל זרימת הודעות אוטומטית בעת גלילה למעלה הדף',
reply_link_preview: 'החל תצוגה מקדימה של לינק-תגובה בעת ריחוף עם העכבר',
follow_import: 'יבוא עקיבות',
import_followers_from_a_csv_file: 'ייבא את הנעקבים שלך מקובץ csv',
follows_imported: 'נעקבים יובאו! ייקח זמן מה לעבד אותם.',
follow_import_error: 'שגיאה בייבוא נעקבים.',
delete_account: 'מחק משתמש',
delete_account_description: 'מחק לצמיתות את המשתמש שלך ואת כל הודעותיך.',
delete_account_instructions: 'הכנס את סיסמתך בקלט למטה על מנת לאשר מחיקת משתמש.',
delete_account_error: 'הייתה בעיה במחיקת המשתמש. אם זה ממשיך, אנא עדכן את מנהל השרת שלך.',
follow_export: 'יצוא עקיבות',
follow_export_processing: 'טוען. בקרוב תתבקש להוריד את הקובץ את הקובץ שלך',
follow_export_button: 'ייצא את הנעקבים שלך לקובץ csv',
change_password: 'שנה סיסמה',
current_password: 'סיסמה נוכחית',
new_password: 'סיסמה חדשה',
confirm_new_password: 'אשר סיסמה',
changed_password: 'סיסמה שונתה בהצלחה!',
change_password_error: 'הייתה בעיה בשינוי סיסמתך.'
},
notifications: {
notifications: 'התראות',
read: 'קרא!',
followed_you: 'עקב אחריך!',
favorited_you: 'אהב את הסטטוס שלך',
repeated_you: 'חזר על הסטטוס שלך'
},
login: {
login: 'התחבר',
username: 'שם המשתמש',
password: 'סיסמה',
register: 'הירשם',
logout: 'התנתק'
},
registration: {
registration: 'הרשמה',
fullname: 'שם תצוגה',
email: 'אימייל',
bio: 'אודות',
password_confirm: 'אישור סיסמה'
},
post_status: {
posting: 'מפרסם',
default: 'הרגע נחת ב-ל.א.'
},
finder: {
find_user: 'מציאת משתמש',
error_fetching_user: 'שגיאה במציאת משתמש'
},
general: {
submit: 'שלח',
apply: 'החל'
},
user_profile: {
timeline_title: 'ציר זמן המשתמש'
}
}
const messages = {
de,
fi,
@ -1697,7 +1862,8 @@ const messages = {
es,
pt,
ru,
nb
nb,
he
}
export default messages

View file

@ -88,11 +88,15 @@ window.fetch('/api/statusnet/config.json')
window.fetch('/static/config.json')
.then((res) => res.json())
.then((data) => {
const {theme, background, logo, showInstanceSpecificPanel} = data
const {theme, background, logo, showWhoToFollowPanel, whoToFollowProvider, whoToFollowLink, showInstanceSpecificPanel, scopeOptionsEnabled} = data
store.dispatch('setOption', { name: 'theme', value: theme })
store.dispatch('setOption', { name: 'background', value: background })
store.dispatch('setOption', { name: 'logo', value: logo })
store.dispatch('setOption', { name: 'showWhoToFollowPanel', value: showWhoToFollowPanel })
store.dispatch('setOption', { name: 'whoToFollowProvider', value: whoToFollowProvider })
store.dispatch('setOption', { name: 'whoToFollowLink', value: whoToFollowLink })
store.dispatch('setOption', { name: 'showInstanceSpecificPanel', value: showInstanceSpecificPanel })
store.dispatch('setOption', { name: 'scopeOptionsEnabled', value: scopeOptionsEnabled })
if (data['chatDisabled']) {
store.dispatch('disableChat')
}

View file

@ -30,6 +30,8 @@ const BLOCKING_URL = '/api/blocks/create.json'
const UNBLOCKING_URL = '/api/blocks/destroy.json'
const USER_URL = '/api/users/show.json'
const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
import { each, map } from 'lodash'
import 'whatwg-fetch'
@ -329,12 +331,14 @@ const retweet = ({ id, credentials }) => {
})
}
const postStatus = ({credentials, status, mediaIds, inReplyToStatusId}) => {
const postStatus = ({credentials, status, spoilerText, visibility, mediaIds, inReplyToStatusId}) => {
const idsText = mediaIds.join(',')
const form = new FormData()
form.append('status', status)
form.append('source', 'Pleroma FE')
if (spoilerText) form.append('spoiler_text', spoilerText)
if (visibility) form.append('visibility', visibility)
form.append('media_ids', idsText)
if (inReplyToStatusId) {
form.append('in_reply_to_status_id', inReplyToStatusId)
@ -373,6 +377,34 @@ const followImport = ({params, credentials}) => {
.then((response) => response.ok)
}
const deleteAccount = ({credentials, password}) => {
const form = new FormData()
form.append('password', password)
return fetch(DELETE_ACCOUNT_URL, {
body: form,
method: 'POST',
headers: authHeaders(credentials)
})
.then((response) => response.json())
}
const changePassword = ({credentials, password, newPassword, newPasswordConfirmation}) => {
const form = new FormData()
form.append('password', password)
form.append('new_password', newPassword)
form.append('new_password_confirmation', newPasswordConfirmation)
return fetch(CHANGE_PASSWORD_URL, {
body: form,
method: 'POST',
headers: authHeaders(credentials)
})
.then((response) => response.json())
}
const fetchMutes = ({credentials}) => {
const url = '/api/qvitter/mutes.json'
@ -408,7 +440,9 @@ const apiService = {
updateProfile,
updateBanner,
externalProfile,
followImport
followImport,
deleteAccount,
changePassword
}
export default apiService

View file

@ -61,6 +61,9 @@ const backendInteractorService = (credentials) => {
const externalProfile = (profileUrl) => apiService.externalProfile({profileUrl, credentials})
const followImport = ({params}) => apiService.followImport({params, credentials})
const deleteAccount = ({password}) => apiService.deleteAccount({credentials, password})
const changePassword = ({password, newPassword, newPasswordConfirmation}) => apiService.changePassword({credentials, password, newPassword, newPasswordConfirmation})
const backendInteractorServiceInstance = {
fetchStatus,
fetchConversation,
@ -82,7 +85,9 @@ const backendInteractorService = (credentials) => {
updateBanner,
updateProfile,
externalProfile,
followImport
followImport,
deleteAccount,
changePassword
}
return backendInteractorServiceInstance

View file

@ -1,10 +1,10 @@
import { map } from 'lodash'
import apiService from '../api/api.service.js'
const postStatus = ({ store, status, media = [], inReplyToStatusId = undefined }) => {
const postStatus = ({ store, status, spoilerText, visibility, media = [], inReplyToStatusId = undefined }) => {
const mediaIds = map(media, 'id')
return apiService.postStatus({credentials: store.state.users.currentUser.credentials, status, mediaIds, inReplyToStatusId})
return apiService.postStatus({credentials: store.state.users.currentUser.credentials, status, spoilerText, visibility, mediaIds, inReplyToStatusId})
.then((data) => data.json())
.then((data) => {
if (!data.error) {

View file

@ -71,13 +71,11 @@ const setColors = (col, commit) => {
colors.bg = rgb2hex(col.bg.r, col.bg.g, col.bg.b) // background
colors.lightBg = rgb2hex((col.bg.r + col.fg.r) / 2, (col.bg.g + col.fg.g) / 2, (col.bg.b + col.fg.b) / 2) // hilighted bg
colors.btn = rgb2hex(col.fg.r, col.fg.g, col.fg.b) // panels & buttons
colors.input = `rgba(${col.fg.r}, ${col.fg.g}, ${col.fg.b}, .5)`
colors.border = rgb2hex(col.fg.r - mod, col.fg.g - mod, col.fg.b - mod) // borders
colors.faint = rgb2hex(
col.text.r * 0.45 + col.fg.r * 0.55,
col.text.g * 0.45 + col.fg.g * 0.55,
col.text.b * 0.45 + col.fg.b * 0.55) // faint text
colors.faint = `rgba(${col.text.r}, ${col.text.g}, ${col.text.b}, .5)`
colors.fg = rgb2hex(col.text.r, col.text.g, col.text.b) // text
colors.lightFg = rgb2hex(col.text.r - mod, col.text.g - mod, col.text.b - mod) // strong text
colors.lightFg = rgb2hex(col.text.r - mod * 5, col.text.g - mod * 5, col.text.b - mod * 5) // strong text
colors['base07'] = rgb2hex(col.text.r - mod * 2, col.text.g - mod * 2, col.text.b - mod * 2)
@ -92,6 +90,7 @@ const setColors = (col, commit) => {
colors.cAlertRed = col.cRed && `rgba(${col.cRed.r}, ${col.cRed.g}, ${col.cRed.b}, .5)`
radii.btnRadius = col.btnRadius
radii.inputRadius = col.inputRadius
radii.panelRadius = col.panelRadius
radii.avatarRadius = col.avatarRadius
radii.avatarAltRadius = col.avatarAltRadius