logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe
commit: e34e1ccdae06302ee734414267a357ff5b7c888c
parent: 3768a4623fc227614b0b3920c2cb92ca16404a69
Author: Shpuld Shpludson <shp@cock.li>
Date:   Fri, 22 Feb 2019 14:54:12 +0000

Merge branch '227-manage-blocks-mutes' into 'develop'

Add Blocks / Mutes management tabs under user settings page

See merge request pleroma/pleroma-fe!578

Diffstat:

Mpackage.json1+
Asrc/components/basic_user_card/basic_user_card.js28++++++++++++++++++++++++++++
Asrc/components/basic_user_card/basic_user_card.vue92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/block_card/block_card.js37+++++++++++++++++++++++++++++++++++++
Asrc/components/block_card/block_card.vue25+++++++++++++++++++++++++
Asrc/components/mute_card/mute_card.js37+++++++++++++++++++++++++++++++++++++
Asrc/components/mute_card/mute_card.vue25+++++++++++++++++++++++++
Msrc/components/user_settings/user_settings.js30++++++++++++++++++++++++++++--
Msrc/components/user_settings/user_settings.vue6++++++
Asrc/hocs/with_list/with_list.js40++++++++++++++++++++++++++++++++++++++++
Asrc/hocs/with_list/with_list.scss7+++++++
Asrc/hocs/with_load_more/with_load_more.js91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/hocs/with_load_more/with_load_more.scss11+++++++++++
Asrc/hocs/with_subscription/with_subscription.js84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/hocs/with_subscription/with_subscription.scss11+++++++++++
Msrc/i18n/en.json12+++++++++++-
Msrc/modules/users.js47++++++++++++++++++++++++++++++++++++++++++-----
Msrc/services/api/api.service.js13+++++++++++++
Msrc/services/backend_interactor_service/backend_interactor_service.js2++
Myarn.lock96++++++++++++++++++++++---------------------------------------------------------
20 files changed, 617 insertions(+), 78 deletions(-)

diff --git a/package.json b/package.json @@ -28,6 +28,7 @@ "sass-loader": "^4.0.2", "vue": "^2.5.13", "vue-chat-scroll": "^1.2.1", + "vue-compose": "^0.7.1", "vue-i18n": "^7.3.2", "vue-router": "^3.0.1", "vue-template-compiler": "^2.3.4", diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js @@ -0,0 +1,28 @@ +import UserCardContent from '../user_card_content/user_card_content.vue' +import UserAvatar from '../user_avatar/user_avatar.vue' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' + +const BasicUserCard = { + props: [ + 'user' + ], + data () { + return { + userExpanded: false + } + }, + components: { + UserCardContent, + UserAvatar + }, + methods: { + toggleUserExpanded () { + this.userExpanded = !this.userExpanded + }, + userProfileLink (user) { + return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) + } + } +} + +export default BasicUserCard diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue @@ -0,0 +1,92 @@ +<template> + <div class="user-card"> + <router-link :to="userProfileLink(user)"> + <UserAvatar class="avatar" :compact="true" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/> + </router-link> + <div class="user-card-expanded-content" v-if="userExpanded"> + <user-card-content :user="user" :switcher="false"></user-card-content> + </div> + <div class="user-card-collapsed-content" v-else> + <div class="user-card-primary-area"> + <div :title="user.name" class="user-name"> + <span v-if="user.name_html" v-html="user.name_html"></span> + <span v-else>{{ user.name }}</span> + </div> + <div> + <router-link class='user-screen-name' :to="userProfileLink(user)"> + @{{user.screen_name}} + </router-link> + </div> + </div> + <div class="user-card-secondary-area"> + <slot name="secondary-area"></slot> + </div> + </div> + </div> +</template> + +<script src="./basic_user_card.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.user-card { + display: flex; + flex: 1 0; + padding-top: 0.6em; + padding-right: 1em; + padding-bottom: 0.6em; + padding-left: 1em; + border-bottom: 1px solid; + margin: 0; + border-bottom-color: $fallback--border; + border-bottom-color: var(--border, $fallback--border); + + &-collapsed-content { + margin-left: 0.7em; + text-align: left; + flex: 1; + display: flex; + align-items: flex-start; + justify-content: space-between; + } + + &-primary-area { + flex: 1; + .user-name { + img { + object-fit: contain; + height: 16px; + width: 16px; + vertical-align: middle; + } + } + } + + &-secondary-area { + flex: none; + } + + &-expanded-content { + flex: 1; + margin: 0.2em 0 0 0.7em; + border-radius: $fallback--panelRadius; + border-radius: var(--panelRadius, $fallback--panelRadius); + border-style: solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + border-width: 1px; + overflow: hidden; + + .panel-heading { + background: transparent; + flex-direction: column; + align-items: stretch; + } + + p { + margin-bottom: 0; + } + } +} +</style> diff --git a/src/components/block_card/block_card.js b/src/components/block_card/block_card.js @@ -0,0 +1,37 @@ +import BasicUserCard from '../basic_user_card/basic_user_card.vue' + +const BlockCard = { + props: ['userId'], + data () { + return { + progress: false + } + }, + computed: { + user () { + return this.$store.getters.userById(this.userId) + }, + blocked () { + return this.user.statusnet_blocking + } + }, + components: { + BasicUserCard + }, + methods: { + unblockUser () { + this.progress = true + this.$store.dispatch('unblockUser', this.user.id).then(() => { + this.progress = false + }) + }, + blockUser () { + this.progress = true + this.$store.dispatch('blockUser', this.user.id).then(() => { + this.progress = false + }) + } + } +} + +export default BlockCard diff --git a/src/components/block_card/block_card.vue b/src/components/block_card/block_card.vue @@ -0,0 +1,24 @@ +<template> + <basic-user-card :user="user"> + <template slot="secondary-area"> + <button class="btn btn-default" @click="unblockUser" :disabled="progress" v-if="blocked"> + <template v-if="progress"> + {{ $t('user_card.unblock_progress') }} + </template> + <template v-else> + {{ $t('user_card.unblock') }} + </template> + </button> + <button class="btn btn-default" @click="blockUser" :disabled="progress" v-else> + <template v-if="progress"> + {{ $t('user_card.block_progress') }} + </template> + <template v-else> + {{ $t('user_card.block') }} + </template> + </button> + </template> + </basic-user-card> +</template> + +<script src="./block_card.js"></script>+ \ No newline at end of file diff --git a/src/components/mute_card/mute_card.js b/src/components/mute_card/mute_card.js @@ -0,0 +1,37 @@ +import BasicUserCard from '../basic_user_card/basic_user_card.vue' + +const MuteCard = { + props: ['userId'], + data () { + return { + progress: false + } + }, + computed: { + user () { + return this.$store.getters.userById(this.userId) + }, + muted () { + return this.user.muted + } + }, + components: { + BasicUserCard + }, + methods: { + unmuteUser () { + this.progress = true + this.$store.dispatch('unmuteUser', this.user.id).then(() => { + this.progress = false + }) + }, + muteUser () { + this.progress = true + this.$store.dispatch('muteUser', this.user.id).then(() => { + this.progress = false + }) + } + } +} + +export default MuteCard diff --git a/src/components/mute_card/mute_card.vue b/src/components/mute_card/mute_card.vue @@ -0,0 +1,24 @@ +<template> + <basic-user-card :user="user"> + <template slot="secondary-area"> + <button class="btn btn-default" @click="unmuteUser" :disabled="progress" v-if="muted"> + <template v-if="progress"> + {{ $t('user_card.unmute_progress') }} + </template> + <template v-else> + {{ $t('user_card.unmute') }} + </template> + </button> + <button class="btn btn-default" @click="muteUser" :disabled="progress" v-else> + <template v-if="progress"> + {{ $t('user_card.mute_progress') }} + </template> + <template v-else> + {{ $t('user_card.mute') }} + </template> + </button> + </template> + </basic-user-card> +</template> + +<script src="./mute_card.js"></script>+ \ No newline at end of file diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js @@ -1,9 +1,33 @@ -import { unescape } from 'lodash' +import { compose } from 'vue-compose' +import unescape from 'lodash/unescape' +import get from 'lodash/get' import TabSwitcher from '../tab_switcher/tab_switcher.js' import ImageCropper from '../image_cropper/image_cropper.vue' import StyleSwitcher from '../style_switcher/style_switcher.vue' import fileSizeFormatService from '../../services/file_size_format/file_size_format.js' +import BlockCard from '../block_card/block_card.vue' +import MuteCard from '../mute_card/mute_card.vue' +import withSubscription from '../../hocs/with_subscription/with_subscription' +import withList from '../../hocs/with_list/with_list' + +const BlockList = compose( + withSubscription({ + fetch: (props, $store) => $store.dispatch('fetchBlocks'), + select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []), + childPropName: 'entries' + }), + withList({ getEntryProps: userId => ({ userId }) }) +)(BlockCard) + +const MuteList = compose( + withSubscription({ + fetch: (props, $store) => $store.dispatch('fetchMutes'), + select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []), + childPropName: 'entries' + }), + withList({ getEntryProps: userId => ({ userId }) }) +)(MuteCard) const UserSettings = { data () { @@ -41,7 +65,9 @@ const UserSettings = { components: { StyleSwitcher, TabSwitcher, - ImageCropper + ImageCropper, + BlockList, + MuteList }, computed: { user () { diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue @@ -162,6 +162,12 @@ <h2>{{$t('settings.follow_export_processing')}}</h2> </div> </div> + + <div :label="$t('settings.blocks_tab')"> + <block-list :refresh="true"> + <template slot="empty">{{$t('settings.no_blocks')}}</template> + </block-list> + </div> </tab-switcher> </div> </div> diff --git a/src/hocs/with_list/with_list.js b/src/hocs/with_list/with_list.js @@ -0,0 +1,40 @@ +import Vue from 'vue' +import map from 'lodash/map' +import isEmpty from 'lodash/isEmpty' +import './with_list.scss' + +const defaultEntryPropsGetter = entry => ({ entry }) +const defaultKeyGetter = entry => entry.id + +const withList = ({ + getEntryProps = defaultEntryPropsGetter, // function to accept entry and index values and return props to be passed into the item component + getKey = defaultKeyGetter // funciton to accept entry and index values and return key prop value +}) => (ItemComponent) => ( + Vue.component('withList', { + props: [ + 'entries', // array of entry + 'entryProps', // additional props to be passed into each entry + 'entryListeners' // additional event listeners to be passed into each entry + ], + render (createElement) { + return ( + <div class="with-list"> + {map(this.entries, (entry, index) => { + const props = { + key: getKey(entry, index), + props: { + ...this.$props.entryProps, + ...getEntryProps(entry, index) + }, + on: this.$props.entryListeners + } + return <ItemComponent {...props} /> + })} + {isEmpty(this.entries) && this.$slots.empty && <div class="with-list-empty-content faint">{this.$slots.empty}</div>} + </div> + ) + } + }) +) + +export default withList diff --git a/src/hocs/with_list/with_list.scss b/src/hocs/with_list/with_list.scss @@ -0,0 +1,6 @@ +.with-list { + &-empty-content { + text-align: center; + padding: 10px; + } +}+ \ No newline at end of file diff --git a/src/hocs/with_load_more/with_load_more.js b/src/hocs/with_load_more/with_load_more.js @@ -0,0 +1,91 @@ +import Vue from 'vue' +import filter from 'lodash/filter' +import isEmpty from 'lodash/isEmpty' +import './with_load_more.scss' + +const withLoadMore = ({ + fetch, // function to fetch entries and return a promise + select, // function to select data from store + childPropName = 'entries' // name of the prop to be passed into the wrapped component +}) => (WrappedComponent) => { + const originalProps = WrappedComponent.props || [] + const props = filter(originalProps, v => v !== 'entries') + + return Vue.component('withLoadMore', { + render (createElement) { + const props = { + props: { + ...this.$props, + [childPropName]: this.entries + }, + on: this.$listeners, + scopedSlots: this.$scopedSlots + } + const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value)) + return ( + <div class="with-load-more"> + <WrappedComponent {...props}> + {children} + </WrappedComponent> + <div class="with-load-more-footer"> + {this.error && <a onClick={this.fetchEntries} class="alert error">{this.$t('general.generic_error')}</a>} + {!this.error && this.loading && <i class="icon-spin3 animate-spin"/>} + {!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>} + </div> + </div> + ) + }, + props, + data () { + return { + loading: false, + bottomedOut: false, + error: false + } + }, + computed: { + entries () { + return select(this.$props, this.$store) || [] + } + }, + created () { + window.addEventListener('scroll', this.scrollLoad) + if (this.entries.length === 0) { + this.fetchEntries() + } + }, + destroyed () { + window.removeEventListener('scroll', this.scrollLoad) + }, + methods: { + fetchEntries () { + if (!this.loading) { + this.loading = true + this.error = false + fetch(this.$props, this.$store) + .then((newEntries) => { + this.loading = false + this.bottomedOut = isEmpty(newEntries) + }) + .catch(() => { + this.loading = false + this.error = true + }) + } + }, + scrollLoad (e) { + const bodyBRect = document.body.getBoundingClientRect() + const height = Math.max(bodyBRect.height, -(bodyBRect.y)) + if (this.loading === false && + this.bottomedOut === false && + this.$el.offsetHeight > 0 && + (window.innerHeight + window.pageYOffset) >= (height - 750) + ) { + this.fetchEntries() + } + } + } + }) +} + +export default withLoadMore diff --git a/src/hocs/with_load_more/with_load_more.scss b/src/hocs/with_load_more/with_load_more.scss @@ -0,0 +1,10 @@ +.with-load-more { + &-footer { + padding: 10px; + text-align: center; + + .error { + font-size: 14px; + } + } +}+ \ No newline at end of file diff --git a/src/hocs/with_subscription/with_subscription.js b/src/hocs/with_subscription/with_subscription.js @@ -0,0 +1,84 @@ +import Vue from 'vue' +import reject from 'lodash/reject' +import isEmpty from 'lodash/isEmpty' +import omit from 'lodash/omit' +import './with_subscription.scss' + +const withSubscription = ({ + fetch, // function to fetch entries and return a promise + select, // function to select data from store + childPropName = 'content' // name of the prop to be passed into the wrapped component +}) => (WrappedComponent) => { + const originalProps = WrappedComponent.props || [] + const props = reject(originalProps, v => v === 'content') + + return Vue.component('withSubscription', { + props: [ + ...props, + 'refresh' // boolean saying to force-fetch data whenever created + ], + render (createElement) { + if (!this.error && !this.loading) { + const props = { + props: { + ...omit(this.$props, 'refresh'), + [childPropName]: this.fetchedData + }, + on: this.$listeners, + scopedSlots: this.$scopedSlots + } + const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value)) + return ( + <div class="with-subscription"> + <WrappedComponent {...props}> + {children} + </WrappedComponent> + </div> + ) + } else { + return ( + <div class="with-subscription-loading"> + {this.error + ? <a onClick={this.fetchData} class="alert error">{this.$t('general.generic_error')}</a> + : <i class="icon-spin3 animate-spin"/> + } + </div> + ) + } + }, + data () { + return { + loading: false, + error: false + } + }, + computed: { + fetchedData () { + return select(this.$props, this.$store) + } + }, + created () { + if (this.refresh || isEmpty(this.fetchedData)) { + this.fetchData() + } + }, + methods: { + fetchData () { + if (!this.loading) { + this.loading = true + this.error = false + fetch(this.$props, this.$store) + .then(() => { + this.loading = false + }) + .catch(() => { + this.error = true + this.loading = false + }) + } + } + } + }) +} + +export default withSubscription diff --git a/src/hocs/with_subscription/with_subscription.scss b/src/hocs/with_subscription/with_subscription.scss @@ -0,0 +1,10 @@ +.with-subscription { + &-loading { + padding: 10px; + text-align: center; + + .error { + font-size: 14px; + } + } +}+ \ No newline at end of file diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -110,6 +110,7 @@ "avatarRadius": "Avatars", "background": "Background", "bio": "Bio", + "blocks_tab": "Blocks", "btnRadius": "Buttons", "cBlue": "Blue (Reply, follow)", "cGreen": "Green (Retweet)", @@ -164,6 +165,7 @@ "lock_account_description": "Restrict your account to approved followers only", "loop_video": "Loop videos", "loop_video_silent_only": "Loop only videos without sound (i.e. Mastodon's \"gifs\")", + "mutes_tab": "Mutes", "play_videos_in_modal": "Play videos directly in the media viewer", "use_contain_fit": "Don't crop the attachment in thumbnails", "name": "Name", @@ -175,6 +177,8 @@ "notification_visibility_mentions": "Mentions", "notification_visibility_repeats": "Repeats", "no_rich_text_description": "Strip rich text formatting from all posts", + "no_blocks": "No blocks", + "no_mutes": "No mutes", "hide_follows_description": "Don't show who I'm following", "hide_followers_description": "Don't show who's following me", "show_admin_badge": "Show Admin badge in my profile", @@ -366,7 +370,13 @@ "muted": "Muted", "per_day": "per day", "remote_follow": "Remote follow", - "statuses": "Statuses" + "statuses": "Statuses", + "unblock": "Unblock", + "unblock_progress": "Unblocking...", + "block_progress": "Blocking...", + "unmute": "Unmute", + "unmute_progress": "Unmuting...", + "mute_progress": "Muting..." }, "user_profile": { "timeline_title": "User Timeline" diff --git a/src/modules/users.js b/src/modules/users.js @@ -85,6 +85,12 @@ export const mutations = { addNewUsers (state, users) { each(users, (user) => mergeOrAdd(state.users, state.usersObject, user)) }, + saveBlocks (state, blockIds) { + state.currentUser.blockIds = blockIds + }, + saveMutes (state, muteIds) { + state.currentUser.muteIds = muteIds + }, setUserForStatus (state, status) { status.user = state.usersObject[status.user.id] }, @@ -137,6 +143,38 @@ const users = { store.rootState.api.backendInteractor.fetchUser({ id }) .then((user) => store.commit('addNewUsers', [user])) }, + fetchBlocks (store) { + return store.rootState.api.backendInteractor.fetchBlocks() + .then((blocks) => { + store.commit('saveBlocks', map(blocks, 'id')) + store.commit('addNewUsers', blocks) + return blocks + }) + }, + blockUser (store, id) { + return store.rootState.api.backendInteractor.blockUser(id) + .then((user) => store.commit('addNewUsers', [user])) + }, + unblockUser (store, id) { + return store.rootState.api.backendInteractor.unblockUser(id) + .then((user) => store.commit('addNewUsers', [user])) + }, + fetchMutes (store) { + return store.rootState.api.backendInteractor.fetchMutes() + .then((mutedUsers) => { + each(mutedUsers, (user) => { user.muted = true }) + store.commit('addNewUsers', mutedUsers) + store.commit('saveMutes', map(mutedUsers, 'id')) + }) + }, + muteUser (store, id) { + return store.state.api.backendInteractor.setUserMute({ id, muted: true }) + .then((user) => store.commit('addNewUsers', [user])) + }, + unmuteUser (store, id) { + return store.state.api.backendInteractor.setUserMute({ id, muted: false }) + .then((user) => store.commit('addNewUsers', [user])) + }, addFriends ({ rootState, commit }, fetchBy) { return new Promise((resolve, reject) => { const user = rootState.users.usersObject[fetchBy] @@ -263,6 +301,8 @@ const users = { const user = data // user.credentials = userCredentials user.credentials = accessToken + user.blockIds = [] + user.muteIds = [] commit('setCurrentUser', user) commit('addNewUsers', [user]) @@ -279,11 +319,8 @@ const users = { // Start getting fresh posts. store.dispatch('startFetching', { timeline: 'friends' }) - // Get user mutes and follower info - store.rootState.api.backendInteractor.fetchMutes().then((mutedUsers) => { - each(mutedUsers, (user) => { user.muted = true }) - store.commit('addNewUsers', mutedUsers) - }) + // Get user mutes + store.dispatch('fetchMutes') // Fetch our friends store.rootState.api.backendInteractor.fetchFriends({ id: user.id }) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js @@ -18,6 +18,7 @@ const MENTIONS_URL = '/api/statuses/mentions.json' const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json' const FOLLOWERS_URL = '/api/statuses/followers.json' const FRIENDS_URL = '/api/statuses/friends.json' +const BLOCKS_URL = '/api/statuses/blocks.json' const FOLLOWING_URL = '/api/friendships/create.json' const UNFOLLOWING_URL = '/api/friendships/destroy.json' const QVITTER_USER_PREF_URL = '/api/qvitter/set_profile_pref.json' @@ -519,6 +520,17 @@ const fetchMutes = ({credentials}) => { }).then((data) => data.json()) } +const fetchBlocks = ({page, credentials}) => { + return fetch(BLOCKS_URL, { + headers: authHeaders(credentials) + }).then((data) => { + if (data.ok) { + return data.json() + } + throw new Error('Error fetching blocks', data) + }) +} + const suggestions = ({credentials}) => { return fetch(SUGGESTIONS_URL, { headers: authHeaders(credentials) @@ -560,6 +572,7 @@ const apiService = { fetchAllFollowing, setUserMute, fetchMutes, + fetchBlocks, register, getCaptcha, updateAvatar, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js @@ -63,6 +63,7 @@ const backendInteractorService = (credentials) => { } const fetchMutes = () => apiService.fetchMutes({credentials}) + const fetchBlocks = (params) => apiService.fetchBlocks({credentials, ...params}) const fetchFollowRequests = () => apiService.fetchFollowRequests({credentials}) const getCaptcha = () => apiService.getCaptcha() @@ -94,6 +95,7 @@ const backendInteractorService = (credentials) => { startFetching, setUserMute, fetchMutes, + fetchBlocks, register, getCaptcha, updateAvatar, diff --git a/yarn.lock b/yarn.lock @@ -38,11 +38,7 @@ dom-event-types "^1.0.0" lodash "^4.17.4" -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -abbrev@1.0.x: +abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" @@ -1385,14 +1381,10 @@ color-convert@^1.3.0, color-convert@^1.9.0: dependencies: color-name "1.1.3" -color-name@1.1.3: +color-name@1.1.3, color-name@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" -color-name@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - color-string@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" @@ -2431,14 +2423,10 @@ extract-zip@^1.6.5, extract-zip@^1.6.7: mkdirp "0.5.1" yauzl "2.4.1" -extsprintf@1.3.0: +extsprintf@1.3.0, extsprintf@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" @@ -3024,18 +3012,12 @@ https-proxy-agent@1: debug "2" extend "3" -iconv-lite@0.4.23: +iconv-lite@0.4.23, iconv-lite@^0.4.4: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.4.4: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - dependencies: - safer-buffer ">= 2.1.2 < 3" - icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -3480,11 +3462,7 @@ js-beautify@^1.6.3: mkdirp "~0.5.0" nopt "~4.0.1" -"js-tokens@^3.0.0 || ^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - -js-tokens@^3.0.2: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -4237,7 +4215,7 @@ minimatch@3.0.3: dependencies: brace-expansion "^1.0.0" -minimist@0.0.8: +minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -4245,10 +4223,6 @@ minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - minipass@^2.2.1, minipass@^2.3.4: version "2.3.5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" @@ -4679,7 +4653,7 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -4787,10 +4761,6 @@ path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -5224,14 +5194,10 @@ punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" -q@1.4.1: +q@1.4.1, q@^1.1.2: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - qjobs@^1.1.4: version "1.2.0" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" @@ -5546,16 +5512,10 @@ resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" -resolve@1.1.x: +resolve@1.1.x, resolve@^1.1.6: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.1.6: - version "1.9.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" - dependencies: - path-parse "^1.0.6" - restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -5607,14 +5567,10 @@ safe-regex@^1.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" -samsam@1.1.2: +samsam@1.1.2, samsam@~1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" -samsam@~1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" - sanitize-html@^1.13.0: version "1.20.0" resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.20.0.tgz#9a602beb1c9faf960fb31f9890f61911cc4d9156" @@ -5988,18 +5944,14 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" +"statuses@>= 1.4.0 < 2", statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" -statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" @@ -6088,7 +6040,7 @@ strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" -supports-color@3.1.2: +supports-color@3.1.2, supports-color@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" dependencies: @@ -6098,7 +6050,7 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.2.3: +supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: @@ -6196,18 +6148,12 @@ timers-browserify@^2.0.2: dependencies: setimmediate "^1.0.4" -tmp@0.0.31: +tmp@0.0.31, tmp@0.0.x: version "0.0.31" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" dependencies: os-tmpdir "~1.0.1" -tmp@0.0.x: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - dependencies: - os-tmpdir "~1.0.2" - to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" @@ -6476,6 +6422,16 @@ vue-chat-scroll@^1.2.1: version "1.3.5" resolved "https://registry.yarnpkg.com/vue-chat-scroll/-/vue-chat-scroll-1.3.5.tgz#a5ee5bae5058f614818a96eac5ee3be4394a2f68" +vue-compose@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/vue-compose/-/vue-compose-0.7.1.tgz#1c11c4cd5e2c8f2743b03fce8ab43d78aabc20b3" + dependencies: + vue-hoc "0.x.x" + +vue-hoc@0.x.x: + version "0.4.7" + resolved "https://registry.yarnpkg.com/vue-hoc/-/vue-hoc-0.4.7.tgz#4d3322ba89b8b0e42b19045ef536c21d948a4fac" + vue-hot-reload-api@^2.0.11: version "2.3.1" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz#b2d3d95402a811602380783ea4f566eb875569a2"