Merge branch 'polish' into shigusegubu

* polish: (21 commits)
  fixed #87
  fixup! panel styling cleanup
  fixup! fixup! Added logic to process reply to favorite request and update likes counter accordingly. Should fix some of cases of doubled likes and likes counter not decrementing.
  fixup! Added logic to process reply to favorite request and update likes counter accordingly. Should fix some of cases of doubled likes and likes counter not decrementing.
  fixup! Added logic to process reply to favorite request and update likes counter accordingly. Should fix some of cases of doubled likes and likes counter not decrementing.
  fixes broken nsfw hider in notifications
  restored "progress" cursor indicator for loading nsfw images
  fixed #72
  Added ability to hide certain types of notifications
  fixup! Separated tab-switcher into a reusable component. This depends on JSX addition
  minor style tweaks
  panel styling cleanup
  moved replies filtering to "filter" category in settings, made it more consistent
  settings page update
  tabs for settings
  allow multiple file upload
  Show lock icon instead of hiding repeat button, tusky-style. Added hint explaining what's going on. Fixes favorite button jumping left and right depending on post visibility
  Added logic to process reply to favorite request and update likes counter accordingly. Should fix some of cases of doubled likes and likes counter not decrementing.
  fixed still-image not preserving original aspect ratio and resolution.
  Separated tab-switcher into a reusable component. This depends on JSX addition
  ...
This commit is contained in:
Henry Jameson 2018-08-30 18:37:36 +03:00
commit aaba8432c2
28 changed files with 765 additions and 424 deletions

View file

@ -1,5 +1,5 @@
{
"presets": ["es2015", "stage-2"],
"plugins": ["transform-runtime", "lodash"],
"presets": ["es2015", "stage-2", "env"],
"plugins": ["transform-runtime", "lodash", "transform-vue-jsx"],
"comments": false
}

View file

@ -54,7 +54,7 @@ module.exports = {
loader: 'vue'
},
{
test: /\.js$/,
test: /\.jsx?$/,
loader: 'babel',
include: projectRoot,
exclude: /node_modules\/(?!tributejs)/

View file

@ -37,8 +37,12 @@
"autoprefixer": "^6.4.0",
"babel-core": "^6.0.0",
"babel-eslint": "^7.0.0",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^6.0.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.0.0",
"babel-plugin-transform-vue-jsx": "3",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.0.0",
"babel-preset-stage-2": "^6.0.0",
"babel-register": "^6.0.0",

View file

@ -48,7 +48,7 @@ a {
color: var(--link, $fallback--link);
}
button{
button {
user-select: none;
color: $fallback--fg;
color: var(--fg, $fallback--fg);
@ -64,10 +64,19 @@ button{
font-size: 14px;
font-family: sans-serif;
&::-moz-focus-inner {
border: none;
}
&:hover {
box-shadow: 0px 0px 4px rgba(255, 255, 255, 0.3);
}
&:active {
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
border-top: 1px solid rgba(0, 0, 0, 0.2);
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
@ -105,6 +114,7 @@ input, textarea, .select {
position: relative;
height: 29px;
line-height: 16px;
hyphens: none;
.icon-down-open {
position: absolute;
@ -282,15 +292,25 @@ main-router {
}
.panel-heading {
display: flex;
border-radius: $fallback--panelRadius $fallback--panelRadius 0 0;
border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0;
background-size: cover;
padding: 0.6em 1.0em;
padding: .6em .6em;
text-align: left;
font-size: 1.3em;
line-height: 24px;
background-color: $fallback--btn;
background-color: var(--btn, $fallback--btn);
align-items: baseline;
.title {
flex: 1 0 auto;
}
button {
height: 100%;
}
}
.panel-heading.stub {
@ -460,4 +480,3 @@ nav {
border-radius: $fallback--inputRadius;
border-radius: var(--inputRadius, $fallback--inputRadius);
}

View file

@ -16,7 +16,7 @@ const Attachment = {
loopVideo: this.$store.state.config.loopVideo,
showHidden: false,
loading: false,
img: this.type === 'image' && document.createElement('img')
img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img')
}
},
components: {

View file

@ -51,6 +51,10 @@
.nsfw-placeholder {
cursor: pointer;
&.loading {
cursor: progress;
}
}
.small-attachment {
@ -61,6 +65,7 @@
}
.attachment {
position: relative;
flex: 1 0 30%;
margin: 0.5em 0.7em 0.6em 0.0em;
align-self: flex-start;
@ -88,10 +93,6 @@
display: flex;
}
&.loading {
cursor: progress;
}
.hider {
position: absolute;
margin: 10px;

View file

@ -6,8 +6,10 @@ const mediaUpload = {
const input = this.$el.querySelector('input')
input.addEventListener('change', ({target}) => {
const file = target.files[0]
this.uploadFile(file)
for (var i = 0; i < target.files.length; i++) {
let file = target.files[i]
this.uploadFile(file)
}
})
},
data () {

View file

@ -3,7 +3,7 @@
<label class="btn btn-default">
<i class="icon-spin4 animate-spin" v-if="uploading"></i>
<i class="icon-upload" v-if="!uploading"></i>
<input type=file style="position: fixed; top: -100em"></input>
<input type="file" style="position: fixed; top: -100em" multiple="true"></input>
</label>
</div>
</template>

View file

@ -11,6 +11,14 @@ const Notifications = {
notificationsFetcher.startFetching({ store, credentials })
},
computed: {
visibleTypes () {
return [
this.$store.state.config.notificationVisibility.likes && 'like',
this.$store.state.config.notificationVisibility.mentions && 'mention',
this.$store.state.config.notificationVisibility.repeats && 'repeat',
this.$store.state.config.notificationVisibility.follows && 'follow'
].filter(_ => _)
},
notifications () {
return this.$store.state.statuses.notifications.data
},
@ -18,13 +26,13 @@ const Notifications = {
return this.$store.state.statuses.notifications.error
},
unseenNotifications () {
return filter(this.notifications, ({seen}) => !seen)
return filter(this.visibleNotifications, ({seen}) => !seen)
},
visibleNotifications () {
// Don't know why, but sortBy([seen, -action.id]) doesn't work.
let sortedNotifications = sortBy(this.notifications, ({action}) => -action.id)
sortedNotifications = sortBy(sortedNotifications, 'seen')
return sortedNotifications
return sortedNotifications.filter((notification) => this.visibleTypes.includes(notification.type))
},
unseenCount () {
return this.unseenNotifications.length

View file

@ -4,58 +4,27 @@
// a bit of a hack to allow scrolling below notifications
padding-bottom: 15em;
.title {
display: inline-block;
}
.panel {
background: $fallback--bg;
background: var(--bg, $fallback--bg)
}
.panel-body {
border-color: $fallback--border;
border-color: var(--border, $fallback--border)
}
.panel-heading {
// force the text to stay centered, while keeping
// the button in the right side of the panel heading
position: relative;
background: $fallback--btn;
background: var(--btn, $fallback--btn);
color: $fallback--fg;
color: var(--fg, $fallback--fg);
display: flex;
align-items: baseline;
.read-button {
position: absolute;
right: 0.7em;
height: 1.8em;
line-height: 100%;
}
}
.unseen-count {
display: inline-block;
background-color: $fallback--cRed;
background-color: var(--cRed, $fallback--cRed);
text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5);
min-width: 1.3em;
border-radius: 1.3em;
margin: 0 0.2em 0 -0.4em;
border-radius: 99px;
min-width: 22px;
max-width: 22px;
min-height: 22px;
max-height: 22px;
color: white;
font-size: 0.9em;
font-size: 15px;
line-height: 22px;
text-align: center;
line-height: 1.3em;
vertical-align: middle
}
.loadmore-error {
position: absolute;
right: 0.6em;
font-size: 14px;
min-width: 6em;
font-family: sans-serif;
text-align: center;
padding: 0 0.25em 0 0.25em;
margin: 0;
@ -73,7 +42,8 @@
box-sizing: border-box;
display: flex;
border-bottom: 1px solid;
border-bottom-color: inherit;
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
.broken-favorite {
border-radius: $fallback--tooltipRadius;

View file

@ -2,8 +2,10 @@
<div class="notifications">
<div class="panel panel-default">
<div class="panel-heading">
<span class="unseen-count" v-if="unseenCount">{{unseenCount}}</span>
<div class="title"> {{$t('notifications.notifications')}}</div>
<div class="title">
{{$t('notifications.notifications')}}
<span class="unseen-count" v-if="unseenCount">{{unseenCount}}</span>
</div>
<div @click.prevent class="loadmore-error alert error" v-if="error">
{{$t('timeline.error_fetching')}}
</div>

View file

@ -1,7 +1,12 @@
<template>
<div v-if="loggedIn && visibility !== 'private' && visibility !== 'direct'">
<i :class='classes' class='icon-retweet rt-active' v-on:click.prevent='retweet()'></i>
<span v-if='status.repeat_num > 0'>{{status.repeat_num}}</span>
<div v-if="loggedIn">
<template v-if="visibility !== 'private' && visibility !== 'direct'">
<i :class='classes' class='icon-retweet rt-active' v-on:click.prevent='retweet()'></i>
<span v-if='status.repeat_num > 0'>{{status.repeat_num}}</span>
</template>
<template v-else>
<i :class='classes' class='icon-lock' :title="$t('timeline.no_retweet_hint')"></i>
</template>
</div>
<div v-else-if="!loggedIn">
<i :class='classes' class='icon-retweet'></i>

View file

@ -1,4 +1,5 @@
/* eslint-env browser */
import TabSwitcher from '../tab_switcher/tab_switcher.jsx'
import StyleSwitcher from '../style_switcher/style_switcher.vue'
import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'
import { filter, trim } from 'lodash'
@ -9,6 +10,7 @@ const settings = {
hideAttachmentsLocal: this.$store.state.config.hideAttachments,
hideAttachmentsInConvLocal: this.$store.state.config.hideAttachmentsInConv,
hideNsfwLocal: this.$store.state.config.hideNsfw,
notificationVisibilityLocal: this.$store.state.config.notificationVisibility,
replyVisibilityLocal: this.$store.state.config.replyVisibility,
loopVideoLocal: this.$store.state.config.loopVideo,
loopVideoSilentOnlyLocal: this.$store.state.config.loopVideoSilentOnly,
@ -29,6 +31,7 @@ const settings = {
}
},
components: {
TabSwitcher,
StyleSwitcher,
InterfaceLanguageSwitcher
},
@ -47,6 +50,18 @@ const settings = {
hideNsfwLocal (value) {
this.$store.dispatch('setOption', { name: 'hideNsfw', value })
},
'notificationVisibilityLocal.likes' (value) {
this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })
},
'notificationVisibilityLocal.follows' (value) {
this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })
},
'notificationVisibilityLocal.repeats' (value) {
this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })
},
'notificationVisibilityLocal.mentions' (value) {
this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })
},
replyVisibilityLocal (value) {
this.$store.dispatch('setOption', { name: 'replyVisibility', value })
},

View file

@ -4,90 +4,132 @@
{{$t('settings.settings')}}
</div>
<div class="panel-body">
<div class="setting-item">
<h2>{{$t('settings.theme')}}</h2>
<style-switcher></style-switcher>
</div>
<div class="setting-item">
<h2>{{$t('settings.filtering')}}</h2>
<p>{{$t('settings.filtering_explanation')}}</p>
<textarea id="muteWords" v-model="muteWordsString"></textarea>
</div>
<div class="setting-item">
<h2>{{$t('nav.timeline')}}</h2>
<ul class="setting-list">
<li>
<input type="checkbox" id="collapseMessageWithSubject" v-model="collapseMessageWithSubjectLocal">
<label for="collapseMessageWithSubject">{{$t('settings.collapse_subject')}}</label>
</li>
<li>
<input type="checkbox" id="streaming" v-model="streamingLocal">
<label for="streaming">{{$t('settings.streaming')}}</label>
<ul class="setting-list suboptions" :class="[{disabled: !streamingLocal}]">
<tab-switcher>
<div :label="$t('settings.general')" >
<div class="setting-item">
<h2>{{ $t('settings.interfaceLanguage') }}</h2>
<interface-language-switcher />
</div>
<div class="setting-item">
<h2>{{$t('nav.timeline')}}</h2>
<ul class="setting-list">
<li>
<input :disabled="!streamingLocal" type="checkbox" id="pauseOnUnfocused" v-model="pauseOnUnfocusedLocal">
<label for="pauseOnUnfocused">{{$t('settings.pause_on_unfocused')}}</label>
<input type="checkbox" id="collapseMessageWithSubject" v-model="collapseMessageWithSubjectLocal">
<label for="collapseMessageWithSubject">{{$t('settings.collapse_subject')}}</label>
</li>
<li>
<input type="checkbox" id="streaming" v-model="streamingLocal">
<label for="streaming">{{$t('settings.streaming')}}</label>
<ul class="setting-list suboptions" :class="[{disabled: !streamingLocal}]">
<li>
<input :disabled="!streamingLocal" type="checkbox" id="pauseOnUnfocused" v-model="pauseOnUnfocusedLocal">
<label for="pauseOnUnfocused">{{$t('settings.pause_on_unfocused')}}</label>
</li>
</ul>
</li>
<li>
<input type="checkbox" id="autoload" v-model="autoLoadLocal">
<label for="autoload">{{$t('settings.autoload')}}</label>
</li>
<li>
<input type="checkbox" id="hoverPreview" v-model="hoverPreviewLocal">
<label for="hoverPreview">{{$t('settings.reply_link_preview')}}</label>
</li>
</ul>
</li>
<li>
<input type="checkbox" id="autoload" v-model="autoLoadLocal">
<label for="autoload">{{$t('settings.autoload')}}</label>
</li>
<li>
<input type="checkbox" id="hoverPreview" v-model="hoverPreviewLocal">
<label for="hoverPreview">{{$t('settings.reply_link_preview')}}</label>
</li>
<li>
<label for="replyVisibility" class="select">
<select id="replyVisibility" v-model="replyVisibilityLocal">
<option value="all" selected>{{$t('settings.reply_visibility_all')}}</option>
<option value="following">{{$t('settings.reply_visibility_following')}}</option>
<option value="self">{{$t('settings.reply_visibility_self')}}</option>
</select>
<i class="icon-down-open"/>
</label>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{$t('settings.attachments')}}</h2>
<ul class="setting-list">
<li>
<input type="checkbox" id="hideAttachments" v-model="hideAttachmentsLocal">
<label for="hideAttachments">{{$t('settings.hide_attachments_in_tl')}}</label>
</li>
<li>
<input type="checkbox" id="hideAttachmentsInConv" v-model="hideAttachmentsInConvLocal">
<label for="hideAttachmentsInConv">{{$t('settings.hide_attachments_in_convo')}}</label>
</li>
<li>
<input type="checkbox" id="hideNsfw" v-model="hideNsfwLocal">
<label for="hideNsfw">{{$t('settings.nsfw_clickthrough')}}</label>
</li>
<li>
<input type="checkbox" id="stopGifs" v-model="stopGifs">
<label for="stopGifs">{{$t('settings.stop_gifs')}}</label>
</li>
<li>
<input type="checkbox" id="loopVideo" v-model="loopVideoLocal">
<label for="loopVideo">{{$t('settings.loop_video')}}</label>
<ul class="setting-list suboptions" :class="[{disabled: !streamingLocal}]">
</div>
<div class="setting-item">
<h2>{{$t('settings.attachments')}}</h2>
<ul class="setting-list">
<li>
<input :disabled="!loopVideoLocal || !loopSilentAvailable" type="checkbox" id="loopVideoSilentOnly" v-model="loopVideoSilentOnlyLocal">
<label for="loopVideoSilentOnly">{{$t('settings.loop_video_silent_only')}}</label>
<div v-if="!loopSilentAvailable" class="unavailable">
<i class="icon-globe"/>! {{$t('settings.limited_availability')}}
</div>
<input type="checkbox" id="hideAttachments" v-model="hideAttachmentsLocal">
<label for="hideAttachments">{{$t('settings.hide_attachments_in_tl')}}</label>
</li>
<li>
<input type="checkbox" id="hideAttachmentsInConv" v-model="hideAttachmentsInConvLocal">
<label for="hideAttachmentsInConv">{{$t('settings.hide_attachments_in_convo')}}</label>
</li>
<li>
<input type="checkbox" id="hideNsfw" v-model="hideNsfwLocal">
<label for="hideNsfw">{{$t('settings.nsfw_clickthrough')}}</label>
</li>
<li>
<input type="checkbox" id="stopGifs" v-model="stopGifs">
<label for="stopGifs">{{$t('settings.stop_gifs')}}</label>
</li>
<li>
<input type="checkbox" id="loopVideo" v-model="loopVideoLocal">
<label for="loopVideo">{{$t('settings.loop_video')}}</label>
<ul class="setting-list suboptions" :class="[{disabled: !streamingLocal}]">
<li>
<input :disabled="!loopVideoLocal || !loopSilentAvailable" type="checkbox" id="loopVideoSilentOnly" v-model="loopVideoSilentOnlyLocal">
<label for="loopVideoSilentOnly">{{$t('settings.loop_video_silent_only')}}</label>
<div v-if="!loopSilentAvailable" class="unavailable">
<i class="icon-globe"/>! {{$t('settings.limited_availability')}}
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('settings.interfaceLanguage') }}</h2>
<interface-language-switcher />
</div>
</div>
</div>
<div :label="$t('settings.theme')" >
<div class="setting-item">
<style-switcher></style-switcher>
</div>
</div>
<div :label="$t('settings.filtering')" >
<div class="setting-item">
<div class="select-multiple">
<span class="label">{{$t('settings.notification_visibility')}}</span>
<ul class="option-list">
<li>
<input type="checkbox" id="notification-visibility-likes" v-model="notificationVisibilityLocal.likes">
<label for="notification-visibility-likes">
{{$t('settings.notification_visibility_likes')}}
</label>
</li>
<li>
<input type="checkbox" id="notification-visibility-repeats" v-model="notificationVisibilityLocal.repeats">
<label for="notification-visibility-repeats">
{{$t('settings.notification_visibility_repeats')}}
</label>
</li>
<li>
<input type="checkbox" id="notification-visibility-follows" v-model="notificationVisibilityLocal.follows">
<label for="notification-visibility-follows">
{{$t('settings.notification_visibility_follows')}}
</label>
</li>
<li>
<input type="checkbox" id="notification-visibility-mentions" v-model="notificationVisibilityLocal.mentions">
<label for="notification-visibility-mentions">
{{$t('settings.notification_visibility_mentions')}}
</label>
</li>
</ul>
</label>
</div>
<div>
{{$t('settings.replies_in_timeline')}}
<label for="replyVisibility" class="select">
<select id="replyVisibility" v-model="replyVisibilityLocal">
<option value="all" selected>{{$t('settings.reply_visibility_all')}}</option>
<option value="following">{{$t('settings.reply_visibility_following')}}</option>
<option value="self">{{$t('settings.reply_visibility_self')}}</option>
</select>
<i class="icon-down-open"/>
</label>
</div>
</div>
<div class="setting-item">
<p>{{$t('settings.filtering_explanation')}}</p>
<textarea id="muteWords" v-model="muteWordsString"></textarea>
</div>
</div>
</tab-switcher>
</div>
</div>
</template>
@ -103,6 +145,23 @@
margin: 1em 1em 1.4em;
padding-bottom: 1.4em;
div {
margin-bottom: .5em;
&:last-child {
margin-bottom: 0;
}
}
&:last-child {
border-bottom: none;
padding-bottom: 0;
margin-bottom: 1em;
}
select {
min-width: 10em;
}
textarea {
width: 100%;
@ -130,12 +189,24 @@
}
.btn {
margin-top: 1em;
min-height: 28px;
}
.submit {
margin-top: 1em;
min-height: 30px;
width: 10em;
}
}
.setting-list {
.select-multiple {
display: flex;
.option-list {
margin: 0;
padding-left: .5em;
}
}
.setting-list,
.option-list{
list-style-type: none;
padding-left: 2em;
li {

View file

@ -18,7 +18,11 @@ const StillImage = {
onLoad () {
const canvas = this.$refs.canvas
if (!canvas) return
canvas.getContext('2d').drawImage(this.$refs.src, 1, 1, canvas.width, canvas.height)
const width = this.$refs.src.naturalWidth
const height = this.$refs.src.naturalHeight
canvas.width = width
canvas.height = height
canvas.getContext('2d').drawImage(this.$refs.src, 0, 0, width, height)
}
}
}

View file

@ -60,6 +60,7 @@
right: 0;
width: 100%;
height: 100%;
object-fit: contain;
}
}
</style>

View file

@ -1,102 +1,30 @@
<template>
<div>
<div>{{$t('settings.presets')}}
<div>
<div class="presets-container">
<div>
{{$t('settings.presets')}}
<label for="style-switcher" class='select'>
<select id="style-switcher" v-model="selected" class="style-switcher">
<option v-for="style in availableStyles" :value="style" :style="{
backgroundColor: style[1],
color: style[3]
}">{{style[0]}}</option>
<option v-for="style in availableStyles"
:value="style"
:style="{
backgroundColor: style[1],
color: style[3]
}">
{{style[0]}}
</option>
</select>
<i class="icon-down-open"/>
</label>
</div>
<div>
<div class="import-export">
<button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button>
<button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button>
<p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p>
</div>
<div class="color-container">
<p>{{$t('settings.theme_help')}}</p>
<div class="color-item">
<label for="bgcolor" class="theme-color-lb">{{$t('settings.background')}}</label>
<input id="bgcolor" class="theme-color-cl" type="color" v-model="bgColorLocal">
<input id="bgcolor-t" class="theme-color-in" type="text" v-model="bgColorLocal">
</div>
<div class="color-item">
<label for="fgcolor" class="theme-color-lb">{{$t('settings.foreground')}}</label>
<input id="fgcolor" class="theme-color-cl" type="color" v-model="btnColorLocal">
<input id="fgcolor-t" class="theme-color-in" type="text" v-model="btnColorLocal">
</div>
<div class="color-item">
<label for="textcolor" class="theme-color-lb">{{$t('settings.text')}}</label>
<input id="textcolor" class="theme-color-cl" type="color" v-model="textColorLocal">
<input id="textcolor-t" class="theme-color-in" type="text" v-model="textColorLocal">
</div>
<div class="color-item">
<label for="linkcolor" class="theme-color-lb">{{$t('settings.links')}}</label>
<input id="linkcolor" class="theme-color-cl" type="color" v-model="linkColorLocal">
<input id="linkcolor-t" class="theme-color-in" type="text" v-model="linkColorLocal">
</div>
<div class="color-item">
<label for="redcolor" class="theme-color-lb">{{$t('settings.cRed')}}</label>
<input id="redcolor" class="theme-color-cl" type="color" v-model="redColorLocal">
<input id="redcolor-t" class="theme-color-in" type="text" v-model="redColorLocal">
</div>
<div class="color-item">
<label for="bluecolor" class="theme-color-lb">{{$t('settings.cBlue')}}</label>
<input id="bluecolor" class="theme-color-cl" type="color" v-model="blueColorLocal">
<input id="bluecolor-t" class="theme-color-in" type="text" v-model="blueColorLocal">
</div>
<div class="color-item">
<label for="greencolor" class="theme-color-lb">{{$t('settings.cGreen')}}</label>
<input id="greencolor" class="theme-color-cl" type="color" v-model="greenColorLocal">
<input id="greencolor-t" class="theme-color-in" type="green" v-model="greenColorLocal">
</div>
<div class="color-item">
<label for="orangecolor" class="theme-color-lb">{{$t('settings.cOrange')}}</label>
<input id="orangecolor" class="theme-color-cl" type="color" v-model="orangeColorLocal">
<input id="orangecolor-t" class="theme-color-in" type="text" v-model="orangeColorLocal">
</div>
</div>
<div class="radius-container">
<p>{{$t('settings.radii_help')}}</p>
<div class="radius-item">
<label for="btnradius" class="theme-radius-lb">{{$t('settings.btnRadius')}}</label>
<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">
<input id="panelradius-t" class="theme-radius-in" type="text" v-model="panelRadiusLocal">
</div>
<div class="radius-item">
<label for="avatarradius" class="theme-radius-lb">{{$t('settings.avatarRadius')}}</label>
<input id="avatarradius" class="theme-radius-rn" type="range" v-model="avatarRadiusLocal" max="28">
<input id="avatarradius-t" class="theme-radius-in" type="green" v-model="avatarRadiusLocal">
</div>
<div class="radius-item">
<label for="avataraltradius" class="theme-radius-lb">{{$t('settings.avatarAltRadius')}}</label>
<input id="avataraltradius" class="theme-radius-rn" type="range" v-model="avatarAltRadiusLocal" max="28">
<input id="avataraltradius-t" class="theme-radius-in" type="text" v-model="avatarAltRadiusLocal">
</div>
<div class="radius-item">
<label for="attachmentradius" class="theme-radius-lb">{{$t('settings.attachmentRadius')}}</label>
<input id="attachmentrradius" class="theme-radius-rn" type="range" v-model="attachmentRadiusLocal" max="50">
<input id="attachmentradius-t" class="theme-radius-in" type="text" v-model="attachmentRadiusLocal">
</div>
<div class="radius-item">
<label for="tooltipradius" class="theme-radius-lb">{{$t('settings.tooltipRadius')}}</label>
<input id="tooltipradius" class="theme-radius-rn" type="range" v-model="tooltipRadiusLocal" max="20">
<input id="tooltipradius-t" class="theme-radius-in" type="text" v-model="tooltipRadiusLocal">
</div>
</div>
</div>
<div class="preview-container">
<div :style="{
'--btnRadius': btnRadiusLocal + 'px',
'--inputRadius': inputRadiusLocal + 'px',
@ -127,8 +55,95 @@
</div>
</div>
</div>
<button class="btn" @click="setCustomTheme">{{$t('general.apply')}}</button>
</div>
<div class="color-container">
<p>{{$t('settings.theme_help')}}</p>
<div class="color-item">
<label for="bgcolor" class="theme-color-lb">{{$t('settings.background')}}</label>
<input id="bgcolor" class="theme-color-cl" type="color" v-model="bgColorLocal">
<input id="bgcolor-t" class="theme-color-in" type="text" v-model="bgColorLocal">
</div>
<div class="color-item">
<label for="fgcolor" class="theme-color-lb">{{$t('settings.foreground')}}</label>
<input id="fgcolor" class="theme-color-cl" type="color" v-model="btnColorLocal">
<input id="fgcolor-t" class="theme-color-in" type="text" v-model="btnColorLocal">
</div>
<div class="color-item">
<label for="textcolor" class="theme-color-lb">{{$t('settings.text')}}</label>
<input id="textcolor" class="theme-color-cl" type="color" v-model="textColorLocal">
<input id="textcolor-t" class="theme-color-in" type="text" v-model="textColorLocal">
</div>
<div class="color-item">
<label for="linkcolor" class="theme-color-lb">{{$t('settings.links')}}</label>
<input id="linkcolor" class="theme-color-cl" type="color" v-model="linkColorLocal">
<input id="linkcolor-t" class="theme-color-in" type="text" v-model="linkColorLocal">
</div>
<div class="color-item">
<label for="redcolor" class="theme-color-lb">{{$t('settings.cRed')}}</label>
<input id="redcolor" class="theme-color-cl" type="color" v-model="redColorLocal">
<input id="redcolor-t" class="theme-color-in" type="text" v-model="redColorLocal">
</div>
<div class="color-item">
<label for="bluecolor" class="theme-color-lb">{{$t('settings.cBlue')}}</label>
<input id="bluecolor" class="theme-color-cl" type="color" v-model="blueColorLocal">
<input id="bluecolor-t" class="theme-color-in" type="text" v-model="blueColorLocal">
</div>
<div class="color-item">
<label for="greencolor" class="theme-color-lb">{{$t('settings.cGreen')}}</label>
<input id="greencolor" class="theme-color-cl" type="color" v-model="greenColorLocal">
<input id="greencolor-t" class="theme-color-in" type="green" v-model="greenColorLocal">
</div>
<div class="color-item">
<label for="orangecolor" class="theme-color-lb">{{$t('settings.cOrange')}}</label>
<input id="orangecolor" class="theme-color-cl" type="color" v-model="orangeColorLocal">
<input id="orangecolor-t" class="theme-color-in" type="text" v-model="orangeColorLocal">
</div>
</div>
<div class="radius-container">
<p>{{$t('settings.radii_help')}}</p>
<div class="radius-item">
<label for="btnradius" class="theme-radius-lb">{{$t('settings.btnRadius')}}</label>
<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">
<input id="panelradius-t" class="theme-radius-in" type="text" v-model="panelRadiusLocal">
</div>
<div class="radius-item">
<label for="avatarradius" class="theme-radius-lb">{{$t('settings.avatarRadius')}}</label>
<input id="avatarradius" class="theme-radius-rn" type="range" v-model="avatarRadiusLocal" max="28">
<input id="avatarradius-t" class="theme-radius-in" type="green" v-model="avatarRadiusLocal">
</div>
<div class="radius-item">
<label for="avataraltradius" class="theme-radius-lb">{{$t('settings.avatarAltRadius')}}</label>
<input id="avataraltradius" class="theme-radius-rn" type="range" v-model="avatarAltRadiusLocal" max="28">
<input id="avataraltradius-t" class="theme-radius-in" type="text" v-model="avatarAltRadiusLocal">
</div>
<div class="radius-item">
<label for="attachmentradius" class="theme-radius-lb">{{$t('settings.attachmentRadius')}}</label>
<input id="attachmentrradius" class="theme-radius-rn" type="range" v-model="attachmentRadiusLocal" max="50">
<input id="attachmentradius-t" class="theme-radius-in" type="text" v-model="attachmentRadiusLocal">
</div>
<div class="radius-item">
<label for="tooltipradius" class="theme-radius-lb">{{$t('settings.tooltipRadius')}}</label>
<input id="tooltipradius" class="theme-radius-rn" type="range" v-model="tooltipRadiusLocal" max="20">
<input id="tooltipradius-t" class="theme-radius-in" type="text" v-model="tooltipRadiusLocal">
</div>
</div>
<div class="apply-container">
<button class="btn submit" @click="setCustomTheme">{{$t('general.apply')}}</button>
</div>
</div>
</template>
<script src="./style_switcher.js"></script>
@ -144,15 +159,19 @@
color: var(--cRed, $fallback--cRed);
}
.apply-container,
.radius-container,
.color-container {
.color-container,
.presets-container {
display: flex;
p {
flex: 2 0 100%;
margin-top: 2em;
margin-bottom: .5em;
}
}
.radius-container {
flex-direction: column;
}
@ -162,6 +181,36 @@
justify-content: space-between;
}
.presets-container {
justify-content: center;
.import-export {
display: flex;
.btn {
margin-left: .5em;
}
}
}
.preview-container {
border-top: 1px dashed;
border-bottom: 1px dashed;
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
margin: 1em -1em 0;
padding: 1em;
.btn {
margin-top: 1em;
min-height: 30px;
width: 10em;
}
}
.apply-container {
justify-content: center;
}
.radius-item,
.color-item {
min-width: 20em;
@ -229,6 +278,7 @@
flex: 0;
min-width: 2em;
cursor: pointer;
max-height: 29px;
}
.theme-preview-content {

View file

@ -0,0 +1,44 @@
import Vue from 'vue'
import './tab_switcher.scss'
export default Vue.component('tab-switcher', {
name: 'TabSwitcher',
data () {
return {
active: 0
}
},
methods: {
activateTab(index) {
return () => this.active = index;
}
},
render(h) {
const tabs = this.$slots.default
.filter(slot => slot.data)
.map((slot, index) => {
const classes = ['tab']
if (index === this.active) {
classes.push('active')
}
return (<button onClick={this.activateTab(index)} class={ classes.join(' ') }>{slot.data.attrs.label}</button>)
});
const contents = (
<div>
{this.$slots.default.filter(slot => slot.data)[this.active]}
</div>
);
return (
<div class="tab-switcher">
<div class="tabs">
{tabs}
</div>
<div class="contents">
{contents}
</div>
</div>
)
}
})

View file

@ -0,0 +1,43 @@
@import '../../_variables.scss';
.tab-switcher {
.tabs {
display: flex;
position: relative;
justify-content: center;
width: 100%;
overflow: hidden;
padding-top: 5px;
&::after, &::before {
display: block;
content: '';
flex: 1 1 auto;
}
.tab, &::after, &::before {
border-bottom: 1px solid;
border-bottom-color: $fallback--btn;
border-bottom-color: var(--btn, $fallback--btn);
}
.tab {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
padding: .3em 1em;
&:not(.active) {
border-bottom: 1px solid;
border-bottom-color: $fallback--btn;
border-bottom-color: var(--btn, $fallback--btn);
z-index: 4;
}
&.active {
background: transparent;
border-bottom: none;
z-index: 5;
}
}
}
}

View file

@ -57,36 +57,8 @@
@import '../../_variables.scss';
.timeline {
.timeline-heading {
position: relative;
display: flex;
}
.title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 70%;
}
.loadmore-button {
position: absolute;
right: 0.6em;
font-size: 14px;
min-width: 6em;
height: 1.8em;
line-height: 100%;
}
.loadmore-text {
position: absolute;
right: 0.6em;
font-size: 14px;
min-width: 6em;
font-family: sans-serif;
text-align: center;
padding: 0 0.5em 0 0.5em;
opacity: 0.8;
background-color: transparent;
color: $fallback--faint;
@ -98,7 +70,6 @@
right: 0.6em;
font-size: 14px;
min-width: 6em;
font-family: sans-serif;
text-align: center;
padding: 0 0.25em 0 0.25em;
margin: 0;

View file

@ -1,3 +1,4 @@
import TabSwitcher from '../tab_switcher/tab_switcher.jsx'
import StyleSwitcher from '../style_switcher/style_switcher.vue'
const UserSettings = {
@ -23,7 +24,8 @@ const UserSettings = {
}
},
components: {
StyleSwitcher
StyleSwitcher,
TabSwitcher
},
computed: {
user () {

View file

@ -4,126 +4,131 @@
{{$t('settings.user_settings')}}
</div>
<div class="panel-body profile-edit">
<div class="tab-switcher">
<button class="btn btn-default" @click="activateTab('profile')">{{$t('settings.profile_tab')}}</button>
<button class="btn btn-default" @click="activateTab('security')">{{$t('settings.security_tab')}}</button>
<button class="btn btn-default" @click="activateTab('data_import_export')" v-if="pleromaBackend">{{$t('settings.data_import_export_tab')}}</button>
</div>
<div class="setting-item" v-if="activeTab == 'profile'">
<h2>{{$t('settings.name_bio')}}</h2>
<p>{{$t('settings.name')}}</p>
<input class='name-changer' id='username' v-model="newname"></input>
<p>{{$t('settings.bio')}}</p>
<textarea class="bio" v-model="newbio"></textarea>
<p>
<input type="checkbox" v-model="newlocked" id="account-locked">
<label for="account-locked">{{$t('settings.lock_account_description')}}</label>
</p>
<div v-if="scopeOptionsEnabled">
<label for="default-vis">{{$t('settings.default_vis')}}</label>
<div id="default-vis" 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>
<tab-switcher>
<div :label="$t('settings.profile_tab')">
<div class="setting-item" >
<h2>{{$t('settings.name_bio')}}</h2>
<p>{{$t('settings.name')}}</p>
<input class='name-changer' id='username' v-model="newname"></input>
<p>{{$t('settings.bio')}}</p>
<textarea class="bio" v-model="newbio"></textarea>
<p>
<input type="checkbox" v-model="newlocked" id="account-locked">
<label for="account-locked">{{$t('settings.lock_account_description')}}</label>
</p>
<div v-if="scopeOptionsEnabled">
<label for="default-vis">{{$t('settings.default_vis')}}</label>
<div id="default-vis" 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>
<button :disabled='newname.length <= 0' class="btn btn-default" @click="updateProfile">{{$t('general.submit')}}</button>
</div>
<div class="setting-item">
<h2>{{$t('settings.avatar')}}</h2>
<p>{{$t('settings.current_avatar')}}</p>
<img :src="user.profile_image_url_original" class="old-avatar"></img>
<p>{{$t('settings.set_new_avatar')}}</p>
<img class="new-avatar" v-bind:src="previews[0]" v-if="previews[0]">
</img>
<div>
<input type="file" @change="uploadFile(0, $event)" ></input>
</div>
<i class="icon-spin4 animate-spin" v-if="uploading[0]"></i>
<button class="btn btn-default" v-else-if="previews[0]" @click="submitAvatar">{{$t('general.submit')}}</button>
</div>
<div class="setting-item">
<h2>{{$t('settings.profile_banner')}}</h2>
<p>{{$t('settings.current_profile_banner')}}</p>
<img :src="user.cover_photo" class="banner"></img>
<p>{{$t('settings.set_new_profile_banner')}}</p>
<img class="banner" v-bind:src="previews[1]" v-if="previews[1]">
</img>
<div>
<input type="file" @change="uploadFile(1, $event)" ></input>
</div>
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[1]"></i>
<button class="btn btn-default" v-else-if="previews[1]" @click="submitBanner">{{$t('general.submit')}}</button>
</div>
<div class="setting-item">
<h2>{{$t('settings.profile_background')}}</h2>
<p>{{$t('settings.set_new_profile_background')}}</p>
<img class="bg" v-bind:src="previews[2]" v-if="previews[2]">
</img>
<div>
<input type="file" @change="uploadFile(2, $event)" ></input>
</div>
<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>
<button :disabled='newname.length <= 0' class="btn btn-default" @click="updateProfile">{{$t('general.submit')}}</button>
</div>
<div class="setting-item" v-if="activeTab == 'profile'">
<h2>{{$t('settings.avatar')}}</h2>
<p>{{$t('settings.current_avatar')}}</p>
<img :src="user.profile_image_url_original" class="old-avatar"></img>
<p>{{$t('settings.set_new_avatar')}}</p>
<img class="new-avatar" v-bind:src="previews[0]" v-if="previews[0]">
</img>
<div>
<input type="file" @change="uploadFile(0, $event)" ></input>
<div :label="$t('settings.security_tab')">
<div class="setting-item">
<h2>{{$t('settings.change_password')}}</h2>
<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">
<h2>{{$t('settings.delete_account')}}</h2>
<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>
<i class="icon-spin4 animate-spin" v-if="uploading[0]"></i>
<button class="btn btn-default" v-else-if="previews[0]" @click="submitAvatar">{{$t('general.submit')}}</button>
</div>
<div class="setting-item" v-if="activeTab == 'profile'">
<h2>{{$t('settings.profile_banner')}}</h2>
<p>{{$t('settings.current_profile_banner')}}</p>
<img :src="user.cover_photo" class="banner"></img>
<p>{{$t('settings.set_new_profile_banner')}}</p>
<img class="banner" v-bind:src="previews[1]" v-if="previews[1]">
</img>
<div>
<input type="file" @change="uploadFile(1, $event)" ></input>
<div :label="$t('settings.data_import_export_tab')" v-if="pleromaBackend">
<div class="setting-item">
<h2>{{$t('settings.follow_import')}}</h2>
<p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
<form v-model="followImportForm">
<input type="file" ref="followlist" v-on:change="followListChange"></input>
</form>
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[3]"></i>
<button class="btn btn-default" v-else @click="importFollows">{{$t('general.submit')}}</button>
<div v-if="followsImported">
<i class="icon-cross" @click="dismissImported"></i>
<p>{{$t('settings.follows_imported')}}</p>
</div>
<div v-else-if="followImportError">
<i class="icon-cross" @click="dismissImported"></i>
<p>{{$t('settings.follow_import_error')}}</p>
</div>
</div>
<div class="setting-item" v-if="enableFollowsExport">
<h2>{{$t('settings.follow_export')}}</h2>
<button class="btn btn-default" @click="exportFollows">{{$t('settings.follow_export_button')}}</button>
</div>
<div class="setting-item" v-else>
<h2>{{$t('settings.follow_export_processing')}}</h2>
</div>
</div>
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[1]"></i>
<button class="btn btn-default" v-else-if="previews[1]" @click="submitBanner">{{$t('general.submit')}}</button>
</div>
<div class="setting-item" v-if="activeTab == 'profile'">
<h2>{{$t('settings.profile_background')}}</h2>
<p>{{$t('settings.set_new_profile_background')}}</p>
<img class="bg" v-bind:src="previews[2]" v-if="previews[2]">
</img>
<div>
<input type="file" @change="uploadFile(2, $event)" ></input>
</div>
<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" v-if="activeTab == 'security'">
<h2>{{$t('settings.change_password')}}</h2>
<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 && activeTab == 'data_import_export'">
<h2>{{$t('settings.follow_import')}}</h2>
<p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
<form v-model="followImportForm">
<input type="file" ref="followlist" v-on:change="followListChange"></input>
</form>
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[3]"></i>
<button class="btn btn-default" v-else @click="importFollows">{{$t('general.submit')}}</button>
<div v-if="followsImported">
<i class="icon-cross" @click="dismissImported"></i>
<p>{{$t('settings.follows_imported')}}</p>
</div>
<div v-else-if="followImportError">
<i class="icon-cross" @click="dismissImported"></i>
<p>{{$t('settings.follow_import_error')}}</p>
</div>
</div>
<div class="setting-item" v-if="enableFollowsExport && activeTab == 'data_import_export'">
<h2>{{$t('settings.follow_export')}}</h2>
<button class="btn btn-default" @click="exportFollows">{{$t('settings.follow_export_button')}}</button>
</div>
<div class="setting-item" v-else-if="activeTab == 'data_import_export'">
<h2>{{$t('settings.follow_export_processing')}}</h2>
</div>
<hr>
<div class="setting-item" v-if="activeTab == 'security'">
<h2>{{$t('settings.delete_account')}}</h2>
<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>
</tab-switcher>
</div>
</div>
</template>
@ -151,13 +156,4 @@
margin: 0.25em;
}
}
.tab-switcher {
margin: 7px 7px;
display: inline-block;
button {
height: 30px;
}
}
</style>

View file

@ -48,8 +48,8 @@ const de = {
settings: 'Einstellungen',
theme: 'Farbschema',
presets: 'Voreinstellungen',
export_theme: 'Aktuelles Theme exportieren',
import_theme: 'Gespeichertes Theme laden',
export_theme: 'Farbschema speichern',
import_theme: 'Farbschema laden',
invalid_theme_imported: 'Die ausgewählte Datei ist kein unterstütztes Pleroma-Theme. Keine Änderungen wurden vorgenommen.',
theme_help: 'Benutze HTML Farbcodes (#rrggbb) um dein Farbschema anzupassen',
radii_help: 'Kantenrundung (in Pixel) der Oberfläche anpassen',
@ -273,9 +273,11 @@ const en = {
load_older: 'Load older statuses',
conversation: 'Conversation',
collapse: 'Collapse',
repeated: 'repeated'
repeated: 'repeated',
no_retweet_hint: 'Post is marked as followers-only or direct and cannot be repeated'
},
settings: {
general: 'General',
user_settings: 'User Settings',
name_bio: 'Name & Bio',
name: 'Name',
@ -291,8 +293,8 @@ const en = {
settings: 'Settings',
theme: 'Theme',
presets: 'Presets',
export_theme: 'Export current theme',
import_theme: 'Load saved theme',
export_theme: 'Save preset',
import_theme: 'Load preset',
theme_help: 'Use hex color codes (#rrggbb) to customize your color theme.',
invalid_theme_imported: 'The selected file is not a supported Pleroma theme. No changes to your theme were made.',
radii_help: 'Set up interface edge rounding (in pixels)',
@ -325,9 +327,15 @@ const en = {
loop_video: 'Loop videos',
loop_video_silent_only: 'Loop only videos without sound (i.e. Mastodon\'s "gifs")',
reply_link_preview: 'Enable reply-link preview on mouse hover',
replies_in_timeline: 'Replies in timeline',
reply_visibility_all: 'Show all replies',
reply_visibility_following: 'Only show replies directed at me or users I\'m following',
reply_visibility_self: 'Only show replies directed at me',
notification_visibility: 'Types of notifications to show',
notification_visibility_likes: 'Likes',
notification_visibility_mentions: 'Mentions',
notification_visibility_repeats: 'Repeats',
notification_visibility_follows: 'Follows',
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.',
@ -1620,9 +1628,11 @@ const ru = {
load_older: 'Загрузить старые статусы',
conversation: 'Разговор',
collapse: 'Свернуть',
repeated: 'повторил(а)'
repeated: 'повторил(а)',
no_retweet_hint: 'Пост помечен как "только для подписчиков" или "личное" и поэтому не может быть повторён'
},
settings: {
general: 'Общие',
user_settings: 'Настройки пользователя',
name_bio: 'Имя и описание',
name: 'Имя',
@ -1637,8 +1647,8 @@ const ru = {
set_new_profile_background: 'Загрузить новый фон профиля',
settings: 'Настройки',
theme: 'Тема',
export_theme: 'Экспортировать текущую тему',
import_theme: 'Загрузить сохранённую тему',
export_theme: 'Сохранить Тему',
import_theme: 'Загрузить Тему',
presets: 'Пресеты',
theme_help: 'Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.',
radii_help: 'Округление краёв элементов интерфейса (в пикселях)',
@ -1670,6 +1680,15 @@ const ru = {
loop_video: 'Зациливать видео',
loop_video_silent_only: 'Зацикливать только беззвучные видео (т.е. "гифки" с Mastodon)',
reply_link_preview: 'Включить предварительный просмотр ответа при наведении мыши',
replies_in_timeline: 'Ответы в ленте',
reply_visibility_all: 'Показывать все ответы',
reply_visibility_following: 'Показывать только ответы мне и тех на кого я подписан',
reply_visibility_self: 'Показывать только ответы мне',
notification_visibility: 'Показывать уведомления',
notification_visibility_likes: 'Лайки',
notification_visibility_mentions: 'Упоминания',
notification_visibility_repeats: 'Повторы',
notification_visibility_follows: 'Подписки',
follow_import: 'Импортировать читаемых',
import_followers_from_a_csv_file: 'Импортировать читаемых из файла .csv',
follows_imported: 'Список читаемых импортирован. Обработка займёт некоторое время..',

View file

@ -50,6 +50,7 @@ const persistedStateOptions = {
'config.hideAttachmentsInConv',
'config.hideNsfw',
'config.replyVisibility',
'config.notificationVisibility',
'config.autoLoad',
'config.hoverPreview',
'config.streaming',

View file

@ -18,6 +18,12 @@ const defaultState = {
pauseOnUnfocused: true,
stopGifs: false,
replyVisibility: 'all',
notificationVisibility: {
follows: true,
mentions: true,
likes: true,
repeats: true
},
muteWords: [],
highlight: {},
interfaceLanguage: browserLocale

View file

@ -68,6 +68,15 @@ export const prepareStatus = (status) => {
return status
}
const visibleNotificationTypes = (rootState) => {
return [
rootState.config.notificationVisibility.likes && 'like',
rootState.config.notificationVisibility.mentions && 'mention',
rootState.config.notificationVisibility.repeats && 'repeat',
rootState.config.notificationVisibility.follows && 'follow'
].filter(_ => _)
}
export const statusType = (status) => {
if (status.is_post_verb) {
return 'status'
@ -86,8 +95,7 @@ export const statusType = (status) => {
return 'deletion'
}
// TODO change to status.activity_type === 'follow' when gs supports it
if (status.text.match(/started following/)) {
if (status.text.match(/started following/) || status.activity_type === 'follow') {
return 'follow'
}
@ -187,11 +195,11 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
const favoriteStatus = (favorite, counter) => {
const status = find(allStatuses, { id: toInteger(favorite.in_reply_to_status_id) })
if (status) {
status.fave_num += 1
// This is our favorite, so the relevant bit.
if (favorite.user.id === user.id) {
status.favorited = true
} else {
status.fave_num += 1
}
}
return status
@ -225,6 +233,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
},
'favorite': (favorite) => {
// Only update if this is a new favorite.
// Ignore our own favorites because we get info about likes as response to like request
if (!state.favorites.has(favorite.id)) {
state.favorites.add(favorite.id)
favoriteStatus(favorite)
@ -268,7 +277,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
}
}
const addNewNotifications = (state, { dispatch, notifications, older }) => {
const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes }) => {
const allStatuses = state.allStatuses
const allStatusesObject = state.allStatusesObject
each(notifications, (notification) => {
@ -317,7 +326,7 @@ const addNewNotifications = (state, { dispatch, notifications, older }) => {
result.image = action.attachments[0].url
}
if (fresh && !state.notifications.desktopNotificationSilence) {
if (fresh && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.ntype)) {
let notification = new window.Notification(title, result)
// Chrome is known for not closing notifications automatically
// according to MDN, anyway.
@ -347,6 +356,11 @@ export const mutations = {
const newStatus = state.allStatusesObject[status.id]
newStatus.favorited = value
},
setFavoritedConfirm (state, { status }) {
const newStatus = state.allStatusesObject[status.id]
newStatus.favorited = status.favorited
newStatus.fave_num = status.fave_num
},
setRetweeted (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id]
newStatus.repeated = value
@ -399,7 +413,7 @@ const statuses = {
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser })
},
addNewNotifications ({ rootState, commit, dispatch }, { notifications, older }) {
commit('addNewNotifications', { dispatch, notifications, older })
commit('addNewNotifications', { visibleNotificationTypes: visibleNotificationTypes(rootState), dispatch, notifications, older })
},
setError ({ rootState, commit }, { value }) {
commit('setError', { value })
@ -424,11 +438,31 @@ const statuses = {
// Optimistic favoriting...
commit('setFavorited', { status, value: true })
apiService.favorite({ id: status.id, credentials: rootState.users.currentUser.credentials })
.then(response => {
if (response.ok) {
return response.json()
} else {
return {}
}
})
.then(status => {
commit('setFavoritedConfirm', { status })
})
},
unfavorite ({ rootState, commit }, status) {
// Optimistic favoriting...
commit('setFavorited', { status, value: false })
apiService.unfavorite({ id: status.id, credentials: rootState.users.currentUser.credentials })
.then(response => {
if (response.ok) {
return response.json()
} else {
return {}
}
})
.then(status => {
commit('setFavoritedConfirm', { status })
})
},
retweet ({ rootState, commit }, status) {
// Optimistic retweeting...

View file

@ -247,7 +247,7 @@ describe('The Statuses module', () => {
in_reply_to_status_id: '1', // The API uses strings here...
uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00',
text: 'a favorited something by b',
user: {}
user: { id: 99 }
}
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
@ -264,7 +264,7 @@ describe('The Statuses module', () => {
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.maxId).to.eq(favorite.id)
// If something is favorited by the current user, it also sets the 'favorited' property
// If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request.
const user = {
id: 1
}
@ -281,7 +281,7 @@ describe('The Statuses module', () => {
mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user })
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(2)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true)
})

111
yarn.lock
View file

@ -434,6 +434,10 @@ babel-helper-replace-supers@^6.24.1:
babel-traverse "^6.24.1"
babel-types "^6.24.1"
babel-helper-vue-jsx-merge-props@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6"
babel-helpers@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
@ -500,6 +504,10 @@ babel-plugin-syntax-exponentiation-operator@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
babel-plugin-syntax-jsx@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
babel-plugin-syntax-object-rest-spread@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
@ -516,7 +524,7 @@ babel-plugin-transform-async-generator-functions@^6.24.1:
babel-plugin-syntax-async-generators "^6.5.0"
babel-runtime "^6.22.0"
babel-plugin-transform-async-to-generator@^6.24.1:
babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761"
dependencies:
@ -555,7 +563,7 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.22.0:
dependencies:
babel-runtime "^6.22.0"
babel-plugin-transform-es2015-block-scoping@^6.24.1:
babel-plugin-transform-es2015-block-scoping@^6.23.0, babel-plugin-transform-es2015-block-scoping@^6.24.1:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f"
dependencies:
@ -565,7 +573,7 @@ babel-plugin-transform-es2015-block-scoping@^6.24.1:
babel-types "^6.26.0"
lodash "^4.17.4"
babel-plugin-transform-es2015-classes@^6.24.1:
babel-plugin-transform-es2015-classes@^6.23.0, babel-plugin-transform-es2015-classes@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
dependencies:
@ -579,33 +587,33 @@ babel-plugin-transform-es2015-classes@^6.24.1:
babel-traverse "^6.24.1"
babel-types "^6.24.1"
babel-plugin-transform-es2015-computed-properties@^6.24.1:
babel-plugin-transform-es2015-computed-properties@^6.22.0, babel-plugin-transform-es2015-computed-properties@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
dependencies:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
babel-plugin-transform-es2015-destructuring@^6.22.0:
babel-plugin-transform-es2015-destructuring@^6.22.0, babel-plugin-transform-es2015-destructuring@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
dependencies:
babel-runtime "^6.22.0"
babel-plugin-transform-es2015-duplicate-keys@^6.24.1:
babel-plugin-transform-es2015-duplicate-keys@^6.22.0, babel-plugin-transform-es2015-duplicate-keys@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e"
dependencies:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
babel-plugin-transform-es2015-for-of@^6.22.0:
babel-plugin-transform-es2015-for-of@^6.22.0, babel-plugin-transform-es2015-for-of@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
dependencies:
babel-runtime "^6.22.0"
babel-plugin-transform-es2015-function-name@^6.24.1:
babel-plugin-transform-es2015-function-name@^6.22.0, babel-plugin-transform-es2015-function-name@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
dependencies:
@ -619,7 +627,7 @@ babel-plugin-transform-es2015-literals@^6.22.0:
dependencies:
babel-runtime "^6.22.0"
babel-plugin-transform-es2015-modules-amd@^6.24.1:
babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
dependencies:
@ -627,6 +635,15 @@ babel-plugin-transform-es2015-modules-amd@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
babel-plugin-transform-es2015-modules-commonjs@^6.23.0:
version "6.26.2"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3"
dependencies:
babel-plugin-transform-strict-mode "^6.24.1"
babel-runtime "^6.26.0"
babel-template "^6.26.0"
babel-types "^6.26.0"
babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a"
@ -636,7 +653,7 @@ babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
babel-template "^6.26.0"
babel-types "^6.26.0"
babel-plugin-transform-es2015-modules-systemjs@^6.24.1:
babel-plugin-transform-es2015-modules-systemjs@^6.23.0, babel-plugin-transform-es2015-modules-systemjs@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23"
dependencies:
@ -644,7 +661,7 @@ babel-plugin-transform-es2015-modules-systemjs@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
babel-plugin-transform-es2015-modules-umd@^6.24.1:
babel-plugin-transform-es2015-modules-umd@^6.23.0, babel-plugin-transform-es2015-modules-umd@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468"
dependencies:
@ -652,14 +669,14 @@ babel-plugin-transform-es2015-modules-umd@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
babel-plugin-transform-es2015-object-super@^6.24.1:
babel-plugin-transform-es2015-object-super@^6.22.0, babel-plugin-transform-es2015-object-super@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d"
dependencies:
babel-helper-replace-supers "^6.24.1"
babel-runtime "^6.22.0"
babel-plugin-transform-es2015-parameters@^6.24.1:
babel-plugin-transform-es2015-parameters@^6.23.0, babel-plugin-transform-es2015-parameters@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
dependencies:
@ -670,7 +687,7 @@ babel-plugin-transform-es2015-parameters@^6.24.1:
babel-traverse "^6.24.1"
babel-types "^6.24.1"
babel-plugin-transform-es2015-shorthand-properties@^6.24.1:
babel-plugin-transform-es2015-shorthand-properties@^6.22.0, babel-plugin-transform-es2015-shorthand-properties@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
dependencies:
@ -683,7 +700,7 @@ babel-plugin-transform-es2015-spread@^6.22.0:
dependencies:
babel-runtime "^6.22.0"
babel-plugin-transform-es2015-sticky-regex@^6.24.1:
babel-plugin-transform-es2015-sticky-regex@^6.22.0, babel-plugin-transform-es2015-sticky-regex@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
dependencies:
@ -697,13 +714,13 @@ babel-plugin-transform-es2015-template-literals@^6.22.0:
dependencies:
babel-runtime "^6.22.0"
babel-plugin-transform-es2015-typeof-symbol@^6.22.0:
babel-plugin-transform-es2015-typeof-symbol@^6.22.0, babel-plugin-transform-es2015-typeof-symbol@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
dependencies:
babel-runtime "^6.22.0"
babel-plugin-transform-es2015-unicode-regex@^6.24.1:
babel-plugin-transform-es2015-unicode-regex@^6.22.0, babel-plugin-transform-es2015-unicode-regex@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9"
dependencies:
@ -711,7 +728,7 @@ babel-plugin-transform-es2015-unicode-regex@^6.24.1:
babel-runtime "^6.22.0"
regexpu-core "^2.0.0"
babel-plugin-transform-exponentiation-operator@^6.24.1:
babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e"
dependencies:
@ -726,7 +743,7 @@ babel-plugin-transform-object-rest-spread@^6.22.0:
babel-plugin-syntax-object-rest-spread "^6.8.0"
babel-runtime "^6.26.0"
babel-plugin-transform-regenerator@^6.24.1:
babel-plugin-transform-regenerator@^6.22.0, babel-plugin-transform-regenerator@^6.24.1:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f"
dependencies:
@ -745,6 +762,47 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
babel-plugin-transform-vue-jsx@3:
version "3.7.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-3.7.0.tgz#d40492e6692a36b594f7e9a1928f43e969740960"
dependencies:
esutils "^2.0.2"
babel-preset-env@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a"
dependencies:
babel-plugin-check-es2015-constants "^6.22.0"
babel-plugin-syntax-trailing-function-commas "^6.22.0"
babel-plugin-transform-async-to-generator "^6.22.0"
babel-plugin-transform-es2015-arrow-functions "^6.22.0"
babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
babel-plugin-transform-es2015-block-scoping "^6.23.0"
babel-plugin-transform-es2015-classes "^6.23.0"
babel-plugin-transform-es2015-computed-properties "^6.22.0"
babel-plugin-transform-es2015-destructuring "^6.23.0"
babel-plugin-transform-es2015-duplicate-keys "^6.22.0"
babel-plugin-transform-es2015-for-of "^6.23.0"
babel-plugin-transform-es2015-function-name "^6.22.0"
babel-plugin-transform-es2015-literals "^6.22.0"
babel-plugin-transform-es2015-modules-amd "^6.22.0"
babel-plugin-transform-es2015-modules-commonjs "^6.23.0"
babel-plugin-transform-es2015-modules-systemjs "^6.23.0"
babel-plugin-transform-es2015-modules-umd "^6.23.0"
babel-plugin-transform-es2015-object-super "^6.22.0"
babel-plugin-transform-es2015-parameters "^6.23.0"
babel-plugin-transform-es2015-shorthand-properties "^6.22.0"
babel-plugin-transform-es2015-spread "^6.22.0"
babel-plugin-transform-es2015-sticky-regex "^6.22.0"
babel-plugin-transform-es2015-template-literals "^6.22.0"
babel-plugin-transform-es2015-typeof-symbol "^6.23.0"
babel-plugin-transform-es2015-unicode-regex "^6.22.0"
babel-plugin-transform-exponentiation-operator "^6.22.0"
babel-plugin-transform-regenerator "^6.22.0"
browserslist "^3.2.6"
invariant "^2.2.2"
semver "^5.3.0"
babel-preset-es2015@^6.0.0:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939"
@ -996,6 +1054,13 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
caniuse-db "^1.0.30000639"
electron-to-chromium "^1.2.7"
browserslist@^3.2.6:
version "3.2.8"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6"
dependencies:
caniuse-lite "^1.0.30000844"
electron-to-chromium "^1.3.47"
buffer@^4.9.0:
version "4.9.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
@ -1069,6 +1134,10 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
version "1.0.30000801"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000801.tgz#a1d49def94c4e5aca5ccf1d58812e4668fac19d4"
caniuse-lite@^1.0.30000844:
version "1.0.30000878"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000878.tgz#c644c39588dd42d3498e952234c372e5a40a4123"
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@ -1789,6 +1858,10 @@ electron-to-chromium@^1.2.7:
version "1.3.32"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.32.tgz#11d0684c0840e003c4be8928f8ac5f35dbc2b4e6"
electron-to-chromium@^1.3.47:
version "1.3.61"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.61.tgz#a8ac295b28d0f03d85e37326fd16b6b6b17a1795"
emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"