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"], "presets": ["es2015", "stage-2", "env"],
"plugins": ["transform-runtime", "lodash"], "plugins": ["transform-runtime", "lodash", "transform-vue-jsx"],
"comments": false "comments": false
} }

View file

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

View file

@ -37,8 +37,12 @@
"autoprefixer": "^6.4.0", "autoprefixer": "^6.4.0",
"babel-core": "^6.0.0", "babel-core": "^6.0.0",
"babel-eslint": "^7.0.0", "babel-eslint": "^7.0.0",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^6.0.0", "babel-loader": "^6.0.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.0.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-es2015": "^6.0.0",
"babel-preset-stage-2": "^6.0.0", "babel-preset-stage-2": "^6.0.0",
"babel-register": "^6.0.0", "babel-register": "^6.0.0",

View file

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

View file

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

View file

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

View file

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

View file

@ -3,7 +3,7 @@
<label class="btn btn-default"> <label class="btn btn-default">
<i class="icon-spin4 animate-spin" v-if="uploading"></i> <i class="icon-spin4 animate-spin" v-if="uploading"></i>
<i class="icon-upload" 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> </label>
</div> </div>
</template> </template>

View file

@ -11,6 +11,14 @@ const Notifications = {
notificationsFetcher.startFetching({ store, credentials }) notificationsFetcher.startFetching({ store, credentials })
}, },
computed: { 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 () { notifications () {
return this.$store.state.statuses.notifications.data return this.$store.state.statuses.notifications.data
}, },
@ -18,13 +26,13 @@ const Notifications = {
return this.$store.state.statuses.notifications.error return this.$store.state.statuses.notifications.error
}, },
unseenNotifications () { unseenNotifications () {
return filter(this.notifications, ({seen}) => !seen) return filter(this.visibleNotifications, ({seen}) => !seen)
}, },
visibleNotifications () { visibleNotifications () {
// Don't know why, but sortBy([seen, -action.id]) doesn't work. // Don't know why, but sortBy([seen, -action.id]) doesn't work.
let sortedNotifications = sortBy(this.notifications, ({action}) => -action.id) let sortedNotifications = sortBy(this.notifications, ({action}) => -action.id)
sortedNotifications = sortBy(sortedNotifications, 'seen') sortedNotifications = sortBy(sortedNotifications, 'seen')
return sortedNotifications return sortedNotifications.filter((notification) => this.visibleTypes.includes(notification.type))
}, },
unseenCount () { unseenCount () {
return this.unseenNotifications.length return this.unseenNotifications.length

View file

@ -4,58 +4,27 @@
// a bit of a hack to allow scrolling below notifications // a bit of a hack to allow scrolling below notifications
padding-bottom: 15em; 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 { .unseen-count {
display: inline-block; display: inline-block;
background-color: $fallback--cRed; background-color: $fallback--cRed;
background-color: var(--cRed, $fallback--cRed); background-color: var(--cRed, $fallback--cRed);
text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5); text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5);
min-width: 1.3em; border-radius: 99px;
border-radius: 1.3em; min-width: 22px;
margin: 0 0.2em 0 -0.4em; max-width: 22px;
min-height: 22px;
max-height: 22px;
color: white; color: white;
font-size: 0.9em; font-size: 15px;
line-height: 22px;
text-align: center; text-align: center;
line-height: 1.3em; vertical-align: middle
} }
.loadmore-error { .loadmore-error {
position: absolute; position: absolute;
right: 0.6em; right: 0.6em;
font-size: 14px;
min-width: 6em; min-width: 6em;
font-family: sans-serif;
text-align: center; text-align: center;
padding: 0 0.25em 0 0.25em; padding: 0 0.25em 0 0.25em;
margin: 0; margin: 0;
@ -73,7 +42,8 @@
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
border-bottom: 1px solid; border-bottom: 1px solid;
border-bottom-color: inherit; border-color: $fallback--border;
border-color: var(--border, $fallback--border);
.broken-favorite { .broken-favorite {
border-radius: $fallback--tooltipRadius; border-radius: $fallback--tooltipRadius;

View file

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

View file

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

View file

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

View file

@ -4,90 +4,132 @@
{{$t('settings.settings')}} {{$t('settings.settings')}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div class="setting-item"> <tab-switcher>
<h2>{{$t('settings.theme')}}</h2> <div :label="$t('settings.general')" >
<style-switcher></style-switcher> <div class="setting-item">
</div> <h2>{{ $t('settings.interfaceLanguage') }}</h2>
<div class="setting-item"> <interface-language-switcher />
<h2>{{$t('settings.filtering')}}</h2> </div>
<p>{{$t('settings.filtering_explanation')}}</p> <div class="setting-item">
<textarea id="muteWords" v-model="muteWordsString"></textarea> <h2>{{$t('nav.timeline')}}</h2>
</div> <ul class="setting-list">
<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}]">
<li> <li>
<input :disabled="!streamingLocal" type="checkbox" id="pauseOnUnfocused" v-model="pauseOnUnfocusedLocal"> <input type="checkbox" id="collapseMessageWithSubject" v-model="collapseMessageWithSubjectLocal">
<label for="pauseOnUnfocused">{{$t('settings.pause_on_unfocused')}}</label> <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> </li>
</ul> </ul>
</li> </div>
<li> <div class="setting-item">
<input type="checkbox" id="autoload" v-model="autoLoadLocal"> <h2>{{$t('settings.attachments')}}</h2>
<label for="autoload">{{$t('settings.autoload')}}</label> <ul class="setting-list">
</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}]">
<li> <li>
<input :disabled="!loopVideoLocal || !loopSilentAvailable" type="checkbox" id="loopVideoSilentOnly" v-model="loopVideoSilentOnlyLocal"> <input type="checkbox" id="hideAttachments" v-model="hideAttachmentsLocal">
<label for="loopVideoSilentOnly">{{$t('settings.loop_video_silent_only')}}</label> <label for="hideAttachments">{{$t('settings.hide_attachments_in_tl')}}</label>
<div v-if="!loopSilentAvailable" class="unavailable"> </li>
<i class="icon-globe"/>! {{$t('settings.limited_availability')}} <li>
</div> <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> </li>
</ul> </ul>
</li> </div>
</ul> </div>
</div>
<div class="setting-item"> <div :label="$t('settings.theme')" >
<h2>{{ $t('settings.interfaceLanguage') }}</h2> <div class="setting-item">
<interface-language-switcher /> <style-switcher></style-switcher>
</div> </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>
</div> </div>
</template> </template>
@ -103,6 +145,23 @@
margin: 1em 1em 1.4em; margin: 1em 1em 1.4em;
padding-bottom: 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 { textarea {
width: 100%; width: 100%;
@ -130,12 +189,24 @@
} }
.btn { .btn {
margin-top: 1em;
min-height: 28px; min-height: 28px;
}
.submit {
margin-top: 1em;
min-height: 30px;
width: 10em; width: 10em;
} }
} }
.setting-list { .select-multiple {
display: flex;
.option-list {
margin: 0;
padding-left: .5em;
}
}
.setting-list,
.option-list{
list-style-type: none; list-style-type: none;
padding-left: 2em; padding-left: 2em;
li { li {

View file

@ -18,7 +18,11 @@ const StillImage = {
onLoad () { onLoad () {
const canvas = this.$refs.canvas const canvas = this.$refs.canvas
if (!canvas) return 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; right: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: contain;
} }
} }
</style> </style>

View file

@ -1,102 +1,30 @@
<template> <template>
<div> <div>
<div>{{$t('settings.presets')}} <div class="presets-container">
<div>
{{$t('settings.presets')}}
<label for="style-switcher" class='select'> <label for="style-switcher" class='select'>
<select id="style-switcher" v-model="selected" class="style-switcher"> <select id="style-switcher" v-model="selected" class="style-switcher">
<option v-for="style in availableStyles" :value="style" :style="{ <option v-for="style in availableStyles"
backgroundColor: style[1], :value="style"
color: style[3] :style="{
}">{{style[0]}}</option> backgroundColor: style[1],
color: style[3]
}">
{{style[0]}}
</option>
</select> </select>
<i class="icon-down-open"/> <i class="icon-down-open"/>
</label> </label>
</div> </div>
<div> <div class="import-export">
<button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button>
<button class="btn" @click="importTheme">{{ $t('settings.import_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> <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p>
</div> </div>
<div class="color-container"> </div>
<p>{{$t('settings.theme_help')}}</p>
<div class="color-item"> <div class="preview-container">
<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 :style="{ <div :style="{
'--btnRadius': btnRadiusLocal + 'px', '--btnRadius': btnRadiusLocal + 'px',
'--inputRadius': inputRadiusLocal + 'px', '--inputRadius': inputRadiusLocal + 'px',
@ -127,8 +55,95 @@
</div> </div>
</div> </div>
</div> </div>
<button class="btn" @click="setCustomTheme">{{$t('general.apply')}}</button>
</div> </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> </template>
<script src="./style_switcher.js"></script> <script src="./style_switcher.js"></script>
@ -144,15 +159,19 @@
color: var(--cRed, $fallback--cRed); color: var(--cRed, $fallback--cRed);
} }
.apply-container,
.radius-container, .radius-container,
.color-container { .color-container,
.presets-container {
display: flex; display: flex;
p { p {
flex: 2 0 100%;
margin-top: 2em; margin-top: 2em;
margin-bottom: .5em; margin-bottom: .5em;
} }
} }
.radius-container { .radius-container {
flex-direction: column; flex-direction: column;
} }
@ -162,6 +181,36 @@
justify-content: space-between; 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, .radius-item,
.color-item { .color-item {
min-width: 20em; min-width: 20em;
@ -229,6 +278,7 @@
flex: 0; flex: 0;
min-width: 2em; min-width: 2em;
cursor: pointer; cursor: pointer;
max-height: 29px;
} }
.theme-preview-content { .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'; @import '../../_variables.scss';
.timeline { .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 { .loadmore-text {
position: absolute;
right: 0.6em;
font-size: 14px; font-size: 14px;
min-width: 6em;
font-family: sans-serif;
text-align: center;
padding: 0 0.5em 0 0.5em;
opacity: 0.8; opacity: 0.8;
background-color: transparent; background-color: transparent;
color: $fallback--faint; color: $fallback--faint;
@ -98,7 +70,6 @@
right: 0.6em; right: 0.6em;
font-size: 14px; font-size: 14px;
min-width: 6em; min-width: 6em;
font-family: sans-serif;
text-align: center; text-align: center;
padding: 0 0.25em 0 0.25em; padding: 0 0.25em 0 0.25em;
margin: 0; margin: 0;

View file

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

View file

@ -4,126 +4,131 @@
{{$t('settings.user_settings')}} {{$t('settings.user_settings')}}
</div> </div>
<div class="panel-body profile-edit"> <div class="panel-body profile-edit">
<div class="tab-switcher"> <tab-switcher>
<button class="btn btn-default" @click="activateTab('profile')">{{$t('settings.profile_tab')}}</button> <div :label="$t('settings.profile_tab')">
<button class="btn btn-default" @click="activateTab('security')">{{$t('settings.security_tab')}}</button> <div class="setting-item" >
<button class="btn btn-default" @click="activateTab('data_import_export')" v-if="pleromaBackend">{{$t('settings.data_import_export_tab')}}</button> <h2>{{$t('settings.name_bio')}}</h2>
</div> <p>{{$t('settings.name')}}</p>
<div class="setting-item" v-if="activeTab == 'profile'"> <input class='name-changer' id='username' v-model="newname"></input>
<h2>{{$t('settings.name_bio')}}</h2> <p>{{$t('settings.bio')}}</p>
<p>{{$t('settings.name')}}</p> <textarea class="bio" v-model="newbio"></textarea>
<input class='name-changer' id='username' v-model="newname"></input> <p>
<p>{{$t('settings.bio')}}</p> <input type="checkbox" v-model="newlocked" id="account-locked">
<textarea class="bio" v-model="newbio"></textarea> <label for="account-locked">{{$t('settings.lock_account_description')}}</label>
<p> </p>
<input type="checkbox" v-model="newlocked" id="account-locked"> <div v-if="scopeOptionsEnabled">
<label for="account-locked">{{$t('settings.lock_account_description')}}</label> <label for="default-vis">{{$t('settings.default_vis')}}</label>
</p> <div id="default-vis" class="visibility-tray">
<div v-if="scopeOptionsEnabled"> <i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct"></i>
<label for="default-vis">{{$t('settings.default_vis')}}</label> <i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private"></i>
<div id="default-vis" class="visibility-tray"> <i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted"></i>
<i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct"></i> <i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public"></i>
<i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private"></i> </div>
<i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted"></i> </div>
<i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public"></i> <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>
</div> </div>
<button :disabled='newname.length <= 0' class="btn btn-default" @click="updateProfile">{{$t('general.submit')}}</button>
</div> <div :label="$t('settings.security_tab')">
<div class="setting-item" v-if="activeTab == 'profile'"> <div class="setting-item">
<h2>{{$t('settings.avatar')}}</h2> <h2>{{$t('settings.change_password')}}</h2>
<p>{{$t('settings.current_avatar')}}</p> <div>
<img :src="user.profile_image_url_original" class="old-avatar"></img> <p>{{$t('settings.current_password')}}</p>
<p>{{$t('settings.set_new_avatar')}}</p> <input type="password" v-model="changePasswordInputs[0]">
<img class="new-avatar" v-bind:src="previews[0]" v-if="previews[0]"> </div>
</img> <div>
<div> <p>{{$t('settings.new_password')}}</p>
<input type="file" @change="uploadFile(0, $event)" ></input> <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> </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 :label="$t('settings.data_import_export_tab')" v-if="pleromaBackend">
</div> <div class="setting-item">
<div class="setting-item" v-if="activeTab == 'profile'"> <h2>{{$t('settings.follow_import')}}</h2>
<h2>{{$t('settings.profile_banner')}}</h2> <p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
<p>{{$t('settings.current_profile_banner')}}</p> <form v-model="followImportForm">
<img :src="user.cover_photo" class="banner"></img> <input type="file" ref="followlist" v-on:change="followListChange"></input>
<p>{{$t('settings.set_new_profile_banner')}}</p> </form>
<img class="banner" v-bind:src="previews[1]" v-if="previews[1]"> <i class=" icon-spin4 animate-spin uploading" v-if="uploading[3]"></i>
</img> <button class="btn btn-default" v-else @click="importFollows">{{$t('general.submit')}}</button>
<div> <div v-if="followsImported">
<input type="file" @change="uploadFile(1, $event)" ></input> <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> </div>
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[1]"></i> </tab-switcher>
<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>
</div> </div>
</div> </div>
</template> </template>
@ -151,13 +156,4 @@
margin: 0.25em; margin: 0.25em;
} }
} }
.tab-switcher {
margin: 7px 7px;
display: inline-block;
button {
height: 30px;
}
}
</style> </style>

View file

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

View file

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

View file

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

View file

@ -68,6 +68,15 @@ export const prepareStatus = (status) => {
return 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) => { export const statusType = (status) => {
if (status.is_post_verb) { if (status.is_post_verb) {
return 'status' return 'status'
@ -86,8 +95,7 @@ export const statusType = (status) => {
return 'deletion' return 'deletion'
} }
// TODO change to status.activity_type === 'follow' when gs supports it if (status.text.match(/started following/) || status.activity_type === 'follow') {
if (status.text.match(/started following/)) {
return 'follow' return 'follow'
} }
@ -187,11 +195,11 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
const favoriteStatus = (favorite, counter) => { const favoriteStatus = (favorite, counter) => {
const status = find(allStatuses, { id: toInteger(favorite.in_reply_to_status_id) }) const status = find(allStatuses, { id: toInteger(favorite.in_reply_to_status_id) })
if (status) { if (status) {
status.fave_num += 1
// This is our favorite, so the relevant bit. // This is our favorite, so the relevant bit.
if (favorite.user.id === user.id) { if (favorite.user.id === user.id) {
status.favorited = true status.favorited = true
} else {
status.fave_num += 1
} }
} }
return status return status
@ -225,6 +233,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
}, },
'favorite': (favorite) => { 'favorite': (favorite) => {
// Only update if this is a new 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)) { if (!state.favorites.has(favorite.id)) {
state.favorites.add(favorite.id) state.favorites.add(favorite.id)
favoriteStatus(favorite) 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 allStatuses = state.allStatuses
const allStatusesObject = state.allStatusesObject const allStatusesObject = state.allStatusesObject
each(notifications, (notification) => { each(notifications, (notification) => {
@ -317,7 +326,7 @@ const addNewNotifications = (state, { dispatch, notifications, older }) => {
result.image = action.attachments[0].url 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) let notification = new window.Notification(title, result)
// Chrome is known for not closing notifications automatically // Chrome is known for not closing notifications automatically
// according to MDN, anyway. // according to MDN, anyway.
@ -347,6 +356,11 @@ export const mutations = {
const newStatus = state.allStatusesObject[status.id] const newStatus = state.allStatusesObject[status.id]
newStatus.favorited = value 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 }) { setRetweeted (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id] const newStatus = state.allStatusesObject[status.id]
newStatus.repeated = value newStatus.repeated = value
@ -399,7 +413,7 @@ const statuses = {
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser }) commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser })
}, },
addNewNotifications ({ rootState, commit, dispatch }, { notifications, older }) { addNewNotifications ({ rootState, commit, dispatch }, { notifications, older }) {
commit('addNewNotifications', { dispatch, notifications, older }) commit('addNewNotifications', { visibleNotificationTypes: visibleNotificationTypes(rootState), dispatch, notifications, older })
}, },
setError ({ rootState, commit }, { value }) { setError ({ rootState, commit }, { value }) {
commit('setError', { value }) commit('setError', { value })
@ -424,11 +438,31 @@ const statuses = {
// Optimistic favoriting... // Optimistic favoriting...
commit('setFavorited', { status, value: true }) commit('setFavorited', { status, value: true })
apiService.favorite({ id: status.id, credentials: rootState.users.currentUser.credentials }) 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) { unfavorite ({ rootState, commit }, status) {
// Optimistic favoriting... // Optimistic favoriting...
commit('setFavorited', { status, value: false }) commit('setFavorited', { status, value: false })
apiService.unfavorite({ id: status.id, credentials: rootState.users.currentUser.credentials }) 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) { retweet ({ rootState, commit }, status) {
// Optimistic retweeting... // Optimistic retweeting...

View file

@ -247,7 +247,7 @@ describe('The Statuses module', () => {
in_reply_to_status_id: '1', // The API uses strings here... 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', uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00',
text: 'a favorited something by b', text: 'a favorited something by b',
user: {} user: { id: 99 }
} }
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) 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.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.maxId).to.eq(favorite.id) 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 = { const user = {
id: 1 id: 1
} }
@ -281,7 +281,7 @@ describe('The Statuses module', () => {
mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user }) mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user })
expect(state.timelines.public.visibleStatuses.length).to.eql(1) 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) 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-traverse "^6.24.1"
babel-types "^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: babel-helpers@^6.24.1:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" 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" version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" 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: babel-plugin-syntax-object-rest-spread@^6.8.0:
version "6.13.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" 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-plugin-syntax-async-generators "^6.5.0"
babel-runtime "^6.22.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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761"
dependencies: dependencies:
@ -555,7 +563,7 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.22.0:
dependencies: dependencies:
babel-runtime "^6.22.0" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f"
dependencies: dependencies:
@ -565,7 +573,7 @@ babel-plugin-transform-es2015-block-scoping@^6.24.1:
babel-types "^6.26.0" babel-types "^6.26.0"
lodash "^4.17.4" 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" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
dependencies: dependencies:
@ -579,33 +587,33 @@ babel-plugin-transform-es2015-classes@^6.24.1:
babel-traverse "^6.24.1" babel-traverse "^6.24.1"
babel-types "^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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
dependencies: dependencies:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-template "^6.24.1" 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" version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
dependencies: dependencies:
babel-runtime "^6.22.0" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e"
dependencies: dependencies:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-types "^6.24.1" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
dependencies: dependencies:
babel-runtime "^6.22.0" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
dependencies: dependencies:
@ -619,7 +627,7 @@ babel-plugin-transform-es2015-literals@^6.22.0:
dependencies: dependencies:
babel-runtime "^6.22.0" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
dependencies: dependencies:
@ -627,6 +635,15 @@ babel-plugin-transform-es2015-modules-amd@^6.24.1:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-template "^6.24.1" 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: babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
version "6.26.0" 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" 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-template "^6.26.0"
babel-types "^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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23"
dependencies: dependencies:
@ -644,7 +661,7 @@ babel-plugin-transform-es2015-modules-systemjs@^6.24.1:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-template "^6.24.1" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468"
dependencies: dependencies:
@ -652,14 +669,14 @@ babel-plugin-transform-es2015-modules-umd@^6.24.1:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-template "^6.24.1" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d"
dependencies: dependencies:
babel-helper-replace-supers "^6.24.1" babel-helper-replace-supers "^6.24.1"
babel-runtime "^6.22.0" 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" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
dependencies: dependencies:
@ -670,7 +687,7 @@ babel-plugin-transform-es2015-parameters@^6.24.1:
babel-traverse "^6.24.1" babel-traverse "^6.24.1"
babel-types "^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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
dependencies: dependencies:
@ -683,7 +700,7 @@ babel-plugin-transform-es2015-spread@^6.22.0:
dependencies: dependencies:
babel-runtime "^6.22.0" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
dependencies: dependencies:
@ -697,13 +714,13 @@ babel-plugin-transform-es2015-template-literals@^6.22.0:
dependencies: dependencies:
babel-runtime "^6.22.0" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
dependencies: dependencies:
babel-runtime "^6.22.0" 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" 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" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9"
dependencies: dependencies:
@ -711,7 +728,7 @@ babel-plugin-transform-es2015-unicode-regex@^6.24.1:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
regexpu-core "^2.0.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" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e"
dependencies: dependencies:
@ -726,7 +743,7 @@ babel-plugin-transform-object-rest-spread@^6.22.0:
babel-plugin-syntax-object-rest-spread "^6.8.0" babel-plugin-syntax-object-rest-spread "^6.8.0"
babel-runtime "^6.26.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" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f"
dependencies: dependencies:
@ -745,6 +762,47 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-types "^6.24.1" 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: babel-preset-es2015@^6.0.0:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" 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" caniuse-db "^1.0.30000639"
electron-to-chromium "^1.2.7" 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: buffer@^4.9.0:
version "4.9.1" version "4.9.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" 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" version "1.0.30000801"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000801.tgz#a1d49def94c4e5aca5ccf1d58812e4668fac19d4" 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: caseless@~0.12.0:
version "0.12.0" version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" 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" version "1.3.32"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.32.tgz#11d0684c0840e003c4be8928f8ac5f35dbc2b4e6" 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: emojis-list@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"