aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/utils.js9
-rw-r--r--package.json1
-rw-r--r--postcss.config.js5
-rw-r--r--src/App.js6
-rw-r--r--src/App.vue1
-rw-r--r--src/components/basic_user_card/basic_user_card.vue1
-rw-r--r--src/components/extra_buttons/extra_buttons.js13
-rw-r--r--src/components/extra_buttons/extra_buttons.vue15
-rw-r--r--src/components/gallery/gallery.vue8
-rw-r--r--src/components/instance_specific_panel/instance_specific_panel.js3
-rw-r--r--src/components/instance_specific_panel/instance_specific_panel.vue8
-rw-r--r--src/components/interactions/interactions.js4
-rw-r--r--src/components/interactions/interactions.vue9
-rw-r--r--src/components/link-preview/link-preview.js14
-rw-r--r--src/components/link-preview/link-preview.vue2
-rw-r--r--src/components/mobile_nav/mobile_nav.js4
-rw-r--r--src/components/mobile_nav/mobile_nav.vue1
-rw-r--r--src/components/mobile_post_status_modal/mobile_post_status_modal.vue11
-rw-r--r--src/components/search/search.js4
-rw-r--r--src/components/search/search.vue11
-rw-r--r--src/components/status/status.js2
-rw-r--r--src/components/tab_switcher/tab_switcher.js32
-rw-r--r--src/components/user_profile/user_profile.js74
-rw-r--r--src/components/user_profile/user_profile.vue29
-rw-r--r--src/components/who_to_follow/who_to_follow.js3
-rw-r--r--src/i18n/en.json6
-rw-r--r--src/i18n/fi.json9
-rw-r--r--src/i18n/ja.json17
-rw-r--r--src/i18n/ja_pedantic.json17
-rw-r--r--src/i18n/messages.js1
-rw-r--r--src/i18n/te.json352
-rw-r--r--src/modules/statuses.js12
-rw-r--r--src/services/api/api.service.js14
-rw-r--r--src/services/backend_interactor_service/backend_interactor_service.js4
-rw-r--r--src/services/entity_normalizer/entity_normalizer.service.js2
-rw-r--r--src/services/follow_manipulate/follow_manipulate.js30
-rw-r--r--yarn.lock99
37 files changed, 699 insertions, 134 deletions
diff --git a/build/utils.js b/build/utils.js
index b45ffc16..c094c3c8 100644
--- a/build/utils.js
+++ b/build/utils.js
@@ -27,16 +27,17 @@ exports.cssLoaders = function (options) {
return [
{
test: /\.(post)?css$/,
- use: generateLoaders(['css-loader']),
+ use: generateLoaders(['css-loader', 'postcss-loader']),
},
{
test: /\.less$/,
- use: generateLoaders(['css-loader', 'less-loader']),
+ use: generateLoaders(['css-loader', 'postcss-loader', 'less-loader']),
},
{
test: /\.sass$/,
use: generateLoaders([
'css-loader',
+ 'postcss-loader',
{
loader: 'sass-loader',
options: {
@@ -47,11 +48,11 @@ exports.cssLoaders = function (options) {
},
{
test: /\.scss$/,
- use: generateLoaders(['css-loader', 'sass-loader'])
+ use: generateLoaders(['css-loader', 'postcss-loader', 'sass-loader'])
},
{
test: /\.styl(us)?$/,
- use: generateLoaders(['css-loader', 'stylus-loader']),
+ use: generateLoaders(['css-loader', 'postcss-loader', 'stylus-loader']),
},
]
}
diff --git a/package.json b/package.json
index 28f3beba..0f6ae9c8 100644
--- a/package.json
+++ b/package.json
@@ -93,6 +93,7 @@
"nightwatch": "^0.9.8",
"opn": "^4.0.2",
"ora": "^0.3.0",
+ "postcss-loader": "^3.0.0",
"raw-loader": "^0.5.1",
"sass": "^1.17.3",
"sass-loader": "git://github.com/webpack-contrib/sass-loader",
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 00000000..88752c6c
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ plugins: [
+ require('autoprefixer')
+ ]
+}
diff --git a/src/App.js b/src/App.js
index 3624171e..e9cd5917 100644
--- a/src/App.js
+++ b/src/App.js
@@ -89,7 +89,11 @@ export default {
sitename () { return this.$store.state.instance.name },
chat () { return this.$store.state.chat.channel.state === 'joined' },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
- showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel },
+ showInstanceSpecificPanel () {
+ return this.$store.state.instance.showInstanceSpecificPanel &&
+ !this.$store.state.config.hideISP &&
+ this.$store.state.instance.instanceSpecificPanelContent
+ },
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
isMobileLayout () { return this.$store.state.interface.mobileLayout }
},
diff --git a/src/App.vue b/src/App.vue
index bf6e62e2..719e00a4 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -107,6 +107,7 @@
:floating="true"
class="floating-chat mobile-hidden"
/>
+ <MobilePostStatusModal />
<UserReportingModal />
<portal-target name="modal" />
</div>
diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue
index 568e9359..8a02174e 100644
--- a/src/components/basic_user_card/basic_user_card.vue
+++ b/src/components/basic_user_card/basic_user_card.vue
@@ -87,6 +87,7 @@
&-expanded-content {
flex: 1;
margin-left: 0.7em;
+ min-width: 0;
}
}
</style>
diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js
index 2ec72729..2d05ad39 100644
--- a/src/components/extra_buttons/extra_buttons.js
+++ b/src/components/extra_buttons/extra_buttons.js
@@ -16,6 +16,16 @@ const ExtraButtons = {
this.$store.dispatch('unpinStatus', this.status.id)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
+ },
+ muteConversation () {
+ this.$store.dispatch('muteConversation', this.status.id)
+ .then(() => this.$emit('onSuccess'))
+ .catch(err => this.$emit('onError', err.error.error))
+ },
+ unmuteConversation () {
+ this.$store.dispatch('unmuteConversation', this.status.id)
+ .then(() => this.$emit('onSuccess'))
+ .catch(err => this.$emit('onError', err.error.error))
}
},
computed: {
@@ -30,9 +40,6 @@ const ExtraButtons = {
},
canPin () {
return this.ownStatus && (this.status.visibility === 'public' || this.status.visibility === 'unlisted')
- },
- enabled () {
- return this.canPin || this.canDelete
}
}
}
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index cdad1666..564d34df 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -1,6 +1,5 @@
<template>
<v-popover
- v-if="enabled"
trigger="click"
placement="top"
class="extra-button-popover"
@@ -10,6 +9,20 @@
<div slot="popover">
<div class="dropdown-menu">
<button
+ v-if="!status.muted"
+ class="dropdown-item dropdown-item-icon"
+ @click.prevent="muteConversation"
+ >
+ <i class="icon-eye-off" /><span>{{ $t("status.mute_conversation") }}</span>
+ </button>
+ <button
+ v-if="status.muted"
+ class="dropdown-item dropdown-item-icon"
+ @click.prevent="unmuteConversation"
+ >
+ <i class="icon-eye-off" /><span>{{ $t("status.unmute_conversation") }}</span>
+ </button>
+ <button
v-if="!status.pinned && canPin"
v-close-popover
class="dropdown-item dropdown-item-icon"
diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue
index 6adfb76c..6169d294 100644
--- a/src/components/gallery/gallery.vue
+++ b/src/components/gallery/gallery.vue
@@ -61,13 +61,17 @@
}
&.contain-fit {
- img, video {
+ img,
+ video,
+ canvas {
object-fit: contain;
}
}
&.cover-fit {
- img, video {
+ img,
+ video,
+ canvas {
object-fit: cover;
}
}
diff --git a/src/components/instance_specific_panel/instance_specific_panel.js b/src/components/instance_specific_panel/instance_specific_panel.js
index 9bb5e945..09e3d055 100644
--- a/src/components/instance_specific_panel/instance_specific_panel.js
+++ b/src/components/instance_specific_panel/instance_specific_panel.js
@@ -2,9 +2,6 @@ const InstanceSpecificPanel = {
computed: {
instanceSpecificPanelContent () {
return this.$store.state.instance.instanceSpecificPanelContent
- },
- show () {
- return !this.$store.state.config.hideISP
}
}
}
diff --git a/src/components/instance_specific_panel/instance_specific_panel.vue b/src/components/instance_specific_panel/instance_specific_panel.vue
index a7cf6b48..7448ca06 100644
--- a/src/components/instance_specific_panel/instance_specific_panel.vue
+++ b/src/components/instance_specific_panel/instance_specific_panel.vue
@@ -1,8 +1,5 @@
<template>
- <div
- v-if="show"
- class="instance-specific-panel"
- >
+ <div class="instance-specific-panel">
<div class="panel panel-default">
<div class="panel-body">
<!-- eslint-disable vue/no-v-html -->
@@ -14,6 +11,3 @@
</template>
<script src="./instance_specific_panel.js" ></script>
-
-<style lang="scss">
-</style>
diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js
index d4e3cc17..1f8a9de9 100644
--- a/src/components/interactions/interactions.js
+++ b/src/components/interactions/interactions.js
@@ -13,8 +13,8 @@ const Interactions = {
}
},
methods: {
- onModeSwitch (index, dataset) {
- this.filterMode = tabModeDict[dataset.filter]
+ onModeSwitch (key) {
+ this.filterMode = tabModeDict[key]
}
},
components: {
diff --git a/src/components/interactions/interactions.vue b/src/components/interactions/interactions.vue
index d71c99d5..08cee343 100644
--- a/src/components/interactions/interactions.vue
+++ b/src/components/interactions/interactions.vue
@@ -10,18 +10,15 @@
:on-switch="onModeSwitch"
>
<span
- data-tab-dummy
- data-filter="mentions"
+ key="mentions"
:label="$t('nav.mentions')"
/>
<span
- data-tab-dummy
- data-filter="likes+repeats"
+ key="likes+repeats"
:label="$t('interactions.favs_repeats')"
/>
<span
- data-tab-dummy
- data-filter="follows"
+ key="follows"
:label="$t('interactions.follows')"
/>
</tab-switcher>
diff --git a/src/components/link-preview/link-preview.js b/src/components/link-preview/link-preview.js
index 2f6da55e..444aafbe 100644
--- a/src/components/link-preview/link-preview.js
+++ b/src/components/link-preview/link-preview.js
@@ -5,6 +5,11 @@ const LinkPreview = {
'size',
'nsfw'
],
+ data () {
+ return {
+ imageLoaded: false
+ }
+ },
computed: {
useImage () {
// Currently BE shoudn't give cards if tagged NSFW, this is a bit paranoid
@@ -15,6 +20,15 @@ const LinkPreview = {
useDescription () {
return this.card.description && /\S/.test(this.card.description)
}
+ },
+ created () {
+ if (this.useImage) {
+ const newImg = new Image()
+ newImg.onload = () => {
+ this.imageLoaded = true
+ }
+ newImg.src = this.card.image
+ }
}
}
diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue
index 493774c2..69171977 100644
--- a/src/components/link-preview/link-preview.vue
+++ b/src/components/link-preview/link-preview.vue
@@ -7,7 +7,7 @@
rel="noopener"
>
<div
- v-if="useImage"
+ v-if="useImage && imageLoaded"
class="card-image"
:class="{ 'small-image': size === 'small' }"
>
diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js
index 9b341a3b..c2bb76ee 100644
--- a/src/components/mobile_nav/mobile_nav.js
+++ b/src/components/mobile_nav/mobile_nav.js
@@ -1,14 +1,12 @@
import SideDrawer from '../side_drawer/side_drawer.vue'
import Notifications from '../notifications/notifications.vue'
-import MobilePostStatusModal from '../mobile_post_status_modal/mobile_post_status_modal.vue'
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
import GestureService from '../../services/gesture_service/gesture_service'
const MobileNav = {
components: {
SideDrawer,
- Notifications,
- MobilePostStatusModal
+ Notifications
},
data: () => ({
notificationsCloseGesture: undefined,
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index f67b7ff8..d1c24e56 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -70,7 +70,6 @@
ref="sideDrawer"
:logout="logout"
/>
- <MobilePostStatusModal />
</div>
</template>
diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue b/src/components/mobile_post_status_modal/mobile_post_status_modal.vue
index 5db7584b..b6d7d3ba 100644
--- a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue
+++ b/src/components/mobile_post_status_modal/mobile_post_status_modal.vue
@@ -34,14 +34,19 @@
@import '../../_variables.scss';
.post-form-modal-view {
- max-height: 100%;
- display: block;
+ align-items: flex-start;
}
.post-form-modal-panel {
flex-shrink: 0;
- margin: 25% 0 4em 0;
+ margin-top: 25%;
+ margin-bottom: 2em;
width: 100%;
+ max-width: 700px;
+
+ @media (orientation: landscape) {
+ margin-top: 8%;
+ }
}
.new-status-button {
diff --git a/src/components/search/search.js b/src/components/search/search.js
index b434e127..8e903052 100644
--- a/src/components/search/search.js
+++ b/src/components/search/search.js
@@ -75,8 +75,8 @@ const Search = {
const length = this[tabName].length
return length === 0 ? '' : ` (${length})`
},
- onResultTabSwitch (_index, dataset) {
- this.currenResultTab = dataset.filter
+ onResultTabSwitch (key) {
+ this.currenResultTab = key
},
getActiveTab () {
if (this.visibleStatuses.length > 0) {
diff --git a/src/components/search/search.vue b/src/components/search/search.vue
index 4350e672..746bbaa2 100644
--- a/src/components/search/search.vue
+++ b/src/components/search/search.vue
@@ -31,21 +31,18 @@
<tab-switcher
ref="tabSwitcher"
:on-switch="onResultTabSwitch"
- :custom-active="currenResultTab"
+ :active-tab="currenResultTab"
>
<span
- data-tab-dummy
- data-filter="statuses"
+ key="statuses"
:label="$t('user_card.statuses') + resultCount('visibleStatuses')"
/>
<span
- data-tab-dummy
- data-filter="people"
+ key="people"
:label="$t('search.people') + resultCount('users')"
/>
<span
- data-tab-dummy
- data-filter="hashtags"
+ key="hashtags"
:label="$t('search.hashtags') + resultCount('hashtags')"
/>
</tab-switcher>
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 3c172e5b..502d9583 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -335,7 +335,7 @@ const Status = {
return
}
}
- if (target.className.match(/hashtag/)) {
+ if (target.rel.match(/(?:^|\s)tag(?:$|\s)/) || target.className.match(/hashtag/)) {
// Extract tag name from link url
const tag = extractTagFromUrl(target.href)
if (tag) {
diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js
index a5fe019c..08d5d08f 100644
--- a/src/components/tab_switcher/tab_switcher.js
+++ b/src/components/tab_switcher/tab_switcher.js
@@ -4,12 +4,22 @@ import './tab_switcher.scss'
export default Vue.component('tab-switcher', {
name: 'TabSwitcher',
- props: ['renderOnlyFocused', 'onSwitch', 'customActive'],
+ props: ['renderOnlyFocused', 'onSwitch', 'activeTab'],
data () {
return {
active: this.$slots.default.findIndex(_ => _.tag)
}
},
+ computed: {
+ activeIndex () {
+ // In case of controlled component
+ if (this.activeTab) {
+ return this.$slots.default.findIndex(slot => this.activeTab === slot.key)
+ } else {
+ return this.active
+ }
+ }
+ },
beforeUpdate () {
const currentSlot = this.$slots.default[this.active]
if (!currentSlot.tag) {
@@ -17,21 +27,13 @@ export default Vue.component('tab-switcher', {
}
},
methods: {
- activateTab (index, dataset) {
+ activateTab (index) {
return () => {
if (typeof this.onSwitch === 'function') {
- this.onSwitch.call(null, index, this.$slots.default[index].elm.dataset)
+ this.onSwitch.call(null, this.$slots.default[index].key)
}
this.active = index
}
- },
- isActiveTab (index) {
- const customActiveIndex = this.$slots.default.findIndex(slot => {
- const dataFilter = slot.data && slot.data.attrs && slot.data.attrs['data-filter']
- return this.customActive && this.customActive === dataFilter
- })
-
- return customActiveIndex > -1 ? customActiveIndex === index : index === this.active
}
},
render (h) {
@@ -41,13 +43,13 @@ export default Vue.component('tab-switcher', {
const classesTab = ['tab']
const classesWrapper = ['tab-wrapper']
- if (this.isActiveTab(index)) {
+ if (this.activeIndex === index) {
classesTab.push('active')
classesWrapper.push('active')
}
if (slot.data.attrs.image) {
return (
- <div class={ classesWrapper.join(' ')}>
+ <div class={classesWrapper.join(' ')}>
<button
disabled={slot.data.attrs.disabled}
onClick={this.activateTab(index)}
@@ -59,7 +61,7 @@ export default Vue.component('tab-switcher', {
)
}
return (
- <div class={ classesWrapper.join(' ')}>
+ <div class={classesWrapper.join(' ')}>
<button
disabled={slot.data.attrs.disabled}
onClick={this.activateTab(index)}
@@ -71,7 +73,7 @@ export default Vue.component('tab-switcher', {
const contents = this.$slots.default.map((slot, index) => {
if (!slot.tag) return
- const active = index === this.active
+ const active = this.activeIndex === index
if (this.renderOnlyFocused) {
return active
? <div class="active">{slot}</div>
diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js
index 39b99dac..00055707 100644
--- a/src/components/user_profile/user_profile.js
+++ b/src/components/user_profile/user_profile.js
@@ -22,21 +22,23 @@ const FriendList = withLoadMore({
additionalPropNames: ['userId']
})(List)
+const defaultTabKey = 'statuses'
+
const UserProfile = {
data () {
return {
error: false,
- userId: null
+ userId: null,
+ tab: defaultTabKey
}
},
created () {
- // Make sure that timelines used in this page are empty
- this.cleanUp()
const routeParams = this.$route.params
this.load(routeParams.name || routeParams.id)
+ this.tab = get(this.$route, 'query.tab', defaultTabKey)
},
destroyed () {
- this.cleanUp()
+ this.stopFetching()
},
computed: {
timeline () {
@@ -67,17 +69,36 @@ const UserProfile = {
},
methods: {
load (userNameOrId) {
+ const startFetchingTimeline = (timeline, userId) => {
+ // Clear timeline only if load another user's profile
+ if (userId !== this.$store.state.statuses.timelines[timeline].userId) {
+ this.$store.commit('clearTimeline', { timeline })
+ }
+ this.$store.dispatch('startFetchingTimeline', { timeline, userId })
+ }
+
+ const loadById = (userId) => {
+ this.userId = userId
+ startFetchingTimeline('user', userId)
+ startFetchingTimeline('media', userId)
+ if (this.isUs) {
+ startFetchingTimeline('favorites', userId)
+ }
+ // Fetch all pinned statuses immediately
+ this.$store.dispatch('fetchPinnedStatuses', userId)
+ }
+
+ // Reset view
+ this.userId = null
+ this.error = false
+
// Check if user data is already loaded in store
const user = this.$store.getters.findUser(userNameOrId)
if (user) {
- this.userId = user.id
- this.fetchTimelines()
+ loadById(user.id)
} else {
this.$store.dispatch('fetchUser', userNameOrId)
- .then(({ id }) => {
- this.userId = id
- this.fetchTimelines()
- })
+ .then(({ id }) => loadById(id))
.catch((reason) => {
const errorMessage = get(reason, 'error.error')
if (errorMessage === 'No user with such user_id') { // Known error
@@ -90,40 +111,33 @@ const UserProfile = {
})
}
},
- fetchTimelines () {
- const userId = this.userId
- this.$store.dispatch('startFetchingTimeline', { timeline: 'user', userId })
- this.$store.dispatch('startFetchingTimeline', { timeline: 'media', userId })
- if (this.isUs) {
- this.$store.dispatch('startFetchingTimeline', { timeline: 'favorites', userId })
- }
- // Fetch all pinned statuses immediately
- this.$store.dispatch('fetchPinnedStatuses', userId)
- },
- cleanUp () {
+ stopFetching () {
this.$store.dispatch('stopFetching', 'user')
this.$store.dispatch('stopFetching', 'favorites')
this.$store.dispatch('stopFetching', 'media')
- this.$store.commit('clearTimeline', { timeline: 'user' })
- this.$store.commit('clearTimeline', { timeline: 'favorites' })
- this.$store.commit('clearTimeline', { timeline: 'media' })
+ },
+ switchUser (userNameOrId) {
+ this.stopFetching()
+ this.load(userNameOrId)
+ },
+ onTabSwitch (tab) {
+ this.tab = tab
+ this.$router.replace({ query: { tab } })
}
},
watch: {
'$route.params.id': function (newVal) {
if (newVal) {
- this.cleanUp()
- this.load(newVal)
+ this.switchUser(newVal)
}
},
'$route.params.name': function (newVal) {
if (newVal) {
- this.cleanUp()
- this.load(newVal)
+ this.switchUser(newVal)
}
},
- $route () {
- this.$refs.tabSwitcher.activateTab(0)()
+ '$route.query': function (newVal) {
+ this.tab = newVal.tab || defaultTabKey
}
},
components: {
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index cffa28f1..42516916 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -12,22 +12,24 @@
rounded="top"
/>
<tab-switcher
- ref="tabSwitcher"
+ :active-tab="tab"
:render-only-focused="true"
+ :on-switch="onTabSwitch"
>
- <div :label="$t('user_card.statuses')">
- <Timeline
- :count="user.statuses_count"
- :embedded="true"
- :title="$t('user_profile.timeline_title')"
- :timeline="timeline"
- timeline-name="user"
- :user-id="userId"
- :pinned-status-ids="user.pinnedStatusIds"
- />
- </div>
+ <Timeline
+ key="statuses"
+ :label="$t('user_card.statuses')"
+ :count="user.statuses_count"
+ :embedded="true"
+ :title="$t('user_profile.timeline_title')"
+ :timeline="timeline"
+ timeline-name="user"
+ :user-id="userId"
+ :pinned-status-ids="user.pinnedStatusIds"
+ />
<div
v-if="followsTabVisible"
+ key="followees"
:label="$t('user_card.followees')"
:disabled="!user.friends_count"
>
@@ -42,6 +44,7 @@
</div>
<div
v-if="followersTabVisible"
+ key="followers"
:label="$t('user_card.followers')"
:disabled="!user.followers_count"
>
@@ -58,6 +61,7 @@
</FollowerList>
</div>
<Timeline
+ key="media"
:label="$t('user_card.media')"
:disabled="!media.visibleStatuses.length"
:embedded="true"
@@ -68,6 +72,7 @@
/>
<Timeline
v-if="isUs"
+ key="favorites"
:label="$t('user_card.favorites')"
:disabled="!favorites.visibleStatuses.length"
:embedded="true"
diff --git a/src/components/who_to_follow/who_to_follow.js b/src/components/who_to_follow/who_to_follow.js
index f8100257..8fab6c4d 100644
--- a/src/components/who_to_follow/who_to_follow.js
+++ b/src/components/who_to_follow/who_to_follow.js
@@ -21,7 +21,8 @@ const WhoToFollow = {
name: i.display_name,
screen_name: i.acct,
profile_image_url: i.avatar || '/images/avi.png',
- profile_image_url_original: i.avatar || '/images/avi.png'
+ profile_image_url_original: i.avatar || '/images/avi.png',
+ statusnet_profile_url: i.url
}
this.users.push(user)
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 60a3e284..6a9af55c 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -262,7 +262,7 @@
"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",
+ "play_videos_in_modal": "Play videos in a popup frame",
"use_contain_fit": "Don't crop the attachment in thumbnails",
"name": "Name",
"name_bio": "Name & Bio",
@@ -508,7 +508,9 @@
"pinned": "Pinned",
"delete_confirm": "Do you really want to delete this status?",
"reply_to": "Reply to",
- "replies_list": "Replies:"
+ "replies_list": "Replies:",
+ "mute_conversation": "Mute conversation",
+ "unmute_conversation": "Unmute conversation"
},
"user_card": {
"approve": "Approve",
diff --git a/src/i18n/fi.json b/src/i18n/fi.json
index f4179495..e7ed5408 100644
--- a/src/i18n/fi.json
+++ b/src/i18n/fi.json
@@ -278,8 +278,15 @@
"status": {
"favorites": "Tykkäykset",
"repeats": "Toistot",
+ "delete": "Poista",
+ "pin": "Kiinnitä profiiliisi",
+ "unpin": "Poista kiinnitys",
+ "pinned": "Kiinnitetty",
+ "delete_confirm": "Haluatko varmasti postaa viestin?",
"reply_to": "Vastaus",
- "replies_list": "Vastaukset:"
+ "replies_list": "Vastaukset:",
+ "mute_conversation": "Hiljennä keskustelu",
+ "unmute_conversation": "Poista hiljennys"
},
"user_card": {
"approve": "Hyväksy",
diff --git a/src/i18n/ja.json b/src/i18n/ja.json
index c77f28a6..b4c6015d 100644
--- a/src/i18n/ja.json
+++ b/src/i18n/ja.json
@@ -78,6 +78,7 @@
"timeline": "タイムライン",
"twkn": "つながっているすべてのネットワーク",
"user_search": "ユーザーをさがす",
+ "search": "さがす",
"who_to_follow": "おすすめユーザー",
"preferences": "せってい"
},
@@ -105,6 +106,9 @@
"expired": "いれふだは {0} まえに、おわりました",
"not_enough_options": "ユニークなオプションが、たりません"
},
+ "stickers": {
+ "add_sticker": "ステッカーをふやす"
+ },
"interactions": {
"favs_repeats": "リピートとおきにいり",
"follows": "あたらしいフォロー",
@@ -506,7 +510,9 @@
"pinned": "ピンどめ",
"delete_confirm": "ほんとうに、このステータスを、けしてもいいですか?",
"reply_to": "へんしん:",
- "replies_list": "へんしん:"
+ "replies_list": "へんしん:",
+ "mute_conversation": "スレッドをミュートする",
+ "unmute_conversation": "スレッドをミュートするのをやめる"
},
"user_card": {
"approve": "うけいれ",
@@ -531,6 +537,8 @@
"remote_follow": "リモートフォロー",
"report": "つうほう",
"statuses": "ステータス",
+ "subscribe": "サブスクライブ",
+ "unsubscribe": "サブスクライブをやめる",
"unblock": "ブロックをやめる",
"unblock_progress": "ブロックをとりけしています...",
"block_progress": "ブロックしています...",
@@ -595,5 +603,12 @@
"GiB": "GiB",
"TiB": "TiB"
}
+ },
+ "search": {
+ "people": "ひとびと",
+ "hashtags": "ハッシュタグ",
+ "person_talking": "{count} にんが、はなしています",
+ "people_talking": "{count} にんが、はなしています",
+ "no_results": "みつかりませんでした"
}
}
diff --git a/src/i18n/ja_pedantic.json b/src/i18n/ja_pedantic.json
index 992a2fa6..42bb53d4 100644
--- a/src/i18n/ja_pedantic.json
+++ b/src/i18n/ja_pedantic.json
@@ -78,6 +78,7 @@
"timeline": "タイムライン",
"twkn": "接続しているすべてのネットワーク",
"user_search": "ユーザーを探す",
+ "search": "検索",
"who_to_follow": "おすすめユーザー",
"preferences": "設定"
},
@@ -105,6 +106,9 @@
"expired": "投票は {0} 前に終了しました",
"not_enough_options": "相異なる選択肢が不足しています"
},
+ "stickers": {
+ "add_sticker": "ステッカーを追加"
+ },
"interactions": {
"favs_repeats": "リピートとお気に入り",
"follows": "新しいフォロワー",
@@ -506,7 +510,9 @@
"pinned": "ピン留め",
"delete_confirm": "本当にこのステータスを削除してもよろしいですか?",
"reply_to": "返信",
- "replies_list": "返信:"
+ "replies_list": "返信:",
+ "mute_conversation": "スレッドをミュート",
+ "unmute_conversation": "スレッドのミュートを解除"
},
"user_card": {
"approve": "受け入れ",
@@ -531,6 +537,8 @@
"remote_follow": "リモートフォロー",
"report": "通報",
"statuses": "ステータス",
+ "subscribe": "購読",
+ "unsubscribe": "購読を解除",
"unblock": "ブロック解除",
"unblock_progress": "ブロックを解除しています...",
"block_progress": "ブロックしています...",
@@ -595,5 +603,12 @@
"GiB": "GiB",
"TiB": "TiB"
}
+ },
+ "search": {
+ "people": "人々",
+ "hashtags": "ハッシュタグ",
+ "person_talking": "{count} 人が話しています",
+ "people_talking": "{count} 人が話しています",
+ "no_results": "見つかりませんでした"
}
}
diff --git a/src/i18n/messages.js b/src/i18n/messages.js
index 404a4079..ca02ae48 100644
--- a/src/i18n/messages.js
+++ b/src/i18n/messages.js
@@ -32,6 +32,7 @@ const messages = {
pt: require('./pt.json'),
ro: require('./ro.json'),
ru: require('./ru.json'),
+ te: require('./te.json'),
zh: require('./zh.json')
}
diff --git a/src/i18n/te.json b/src/i18n/te.json
new file mode 100644
index 00000000..f0953d97
--- /dev/null
+++ b/src/i18n/te.json
@@ -0,0 +1,352 @@
+{
+ "chat.title": "చాట్",
+ "features_panel.chat": "చాట్",
+ "features_panel.gopher": "గోఫర్",
+ "features_panel.media_proxy": "మీడియా ప్రాక్సీ",
+ "features_panel.scope_options": "స్కోప్ ఎంపికలు",
+ "features_panel.text_limit": "వచన పరిమితి",
+ "features_panel.title": "లక్షణాలు",
+ "features_panel.who_to_follow": "ఎవరిని అనుసరించాలి",
+ "finder.error_fetching_user": "వినియోగదారుని పొందడంలో లోపం",
+ "finder.find_user": "వినియోగదారుని కనుగొనండి",
+ "general.apply": "వర్తించు",
+ "general.submit": "సమర్పించు",
+ "general.more": "మరిన్ని",
+ "general.generic_error": "ఒక తప్పిదం సంభవించినది",
+ "general.optional": "ఐచ్చికం",
+ "image_cropper.crop_picture": "చిత్రాన్ని కత్తిరించండి",
+ "image_cropper.save": "దాచు",
+ "image_cropper.save_without_cropping": "కత్తిరించకుండా సేవ్ చేయి",
+ "image_cropper.cancel": "రద్దుచేయి",
+ "login.login": "లాగిన్",
+ "login.description": "OAuth తో లాగిన్ అవ్వండి",
+ "login.logout": "లాగౌట్",
+ "login.password": "సంకేతపదము",
+ "login.placeholder": "ఉదా. lain",
+ "login.register": "నమోదు చేసుకోండి",
+ "login.username": "వాడుకరి పేరు",
+ "login.hint": "చర్చలో చేరడానికి లాగిన్ అవ్వండి",
+ "media_modal.previous": "ముందరి పుట",
+ "media_modal.next": "తరువాత",
+ "nav.about": "గురించి",
+ "nav.back": "వెనక్కి",
+ "nav.chat": "స్థానిక చాట్",
+ "nav.friend_requests": "అనుసరించడానికి అభ్యర్థనలు",
+ "nav.mentions": "ప్రస్తావనలు",
+ "nav.dms": "నేరుగా పంపిన సందేశాలు",
+ "nav.public_tl": "ప్రజా కాలక్రమం",
+ "nav.timeline": "కాలక్రమం",
+ "nav.twkn": "మొత్తం తెలిసిన నెట్వర్క్",
+ "nav.user_search": "వాడుకరి శోధన",
+ "nav.who_to_follow": "ఎవరిని అనుసరించాలి",
+ "nav.preferences": "ప్రాధాన్యతలు",
+ "notifications.broken_favorite": "తెలియని స్థితి, దాని కోసం శోధిస్తోంది...",
+ "notifications.favorited_you": "మీ స్థితిని ఇష్టపడ్డారు",
+ "notifications.followed_you": "మిమ్మల్ని అనుసరించారు",
+ "notifications.load_older": "పాత నోటిఫికేషన్లను లోడ్ చేయండి",
+ "notifications.notifications": "ప్రకటనలు",
+ "notifications.read": "చదివాను!",
+ "notifications.repeated_you": "మీ స్థితిని పునరావృతం చేసారు",
+ "notifications.no_more_notifications": "ఇక నోటిఫికేషన్లు లేవు",
+ "post_status.new_status": "క్రొత్త స్థితిని పోస్ట్ చేయండి",
+ "post_status.account_not_locked_warning": "మీ ఖాతా {౦} కాదు. ఎవరైనా మిమ్మల్ని అనుసరించి అనుచరులకు మాత్రమే ఉద్దేశించిన పోస్టులను చూడవచ్చు.",
+ "post_status.account_not_locked_warning_link": "తాళం వేయబడినది",
+ "post_status.attachments_sensitive": "జోడింపులను సున్నితమైనవిగా గుర్తించండి",
+ "post_status.content_type.text/plain": "సాధారణ అక్షరాలు",
+ "post_status.content_type.text/html": "హెచ్‌టిఎమ్ఎల్",
+ "post_status.content_type.text/markdown": "మార్క్డౌన్",
+ "post_status.content_warning": "విషయం (ఐచ్ఛికం)",
+ "post_status.default": "ఇప్పుడే విజయవాడలో దిగాను.",
+ "post_status.direct_warning": "ఈ పోస్ట్ మాత్రమే పేర్కొన్న వినియోగదారులకు మాత్రమే కనిపిస్తుంది.",
+ "post_status.posting": "పోస్ట్ చేస్తున్నా",
+ "post_status.scope.direct": "ప్రత్యక్ష - పేర్కొన్న వినియోగదారులకు మాత్రమే పోస్ట్ చేయబడుతుంది",
+ "post_status.scope.private": "అనుచరులకు మాత్రమే - అనుచరులకు మాత్రమే పోస్ట్ చేయబడుతుంది",
+ "post_status.scope.public": "పబ్లిక్ - ప్రజా కాలక్రమాలకు పోస్ట్ చేయబడుతుంది",
+ "post_status.scope.unlisted": "జాబితా చేయబడనిది - ప్రజా కాలక్రమాలకు పోస్ట్ చేయవద్దు",
+ "registration.bio": "బయో",
+ "registration.email": "ఈ మెయిల్",
+ "registration.fullname": "ప్రదర్శన పేరు",
+ "registration.password_confirm": "పాస్వర్డ్ నిర్ధారణ",
+ "registration.registration": "నమోదు",
+ "registration.token": "ఆహ్వాన టోకెన్",
+ "registration.captcha": "కాప్చా",
+ "registration.new_captcha": "కొత్త కాప్చా పొందుటకు చిత్రం మీద క్లిక్ చేయండి",
+ "registration.username_placeholder": "ఉదా. lain",
+ "registration.fullname_placeholder": "ఉదా. Lain Iwakura",
+ "registration.bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.",
+ "registration.validations.username_required": "ఖాళీగా విడిచిపెట్టరాదు",
+ "registration.validations.fullname_required": "ఖాళీగా విడిచిపెట్టరాదు",
+ "registration.validations.email_required": "ఖాళీగా విడిచిపెట్టరాదు",
+ "registration.validations.password_required": "ఖాళీగా విడిచిపెట్టరాదు",
+ "registration.validations.password_confirmation_required": "ఖాళీగా విడిచిపెట్టరాదు",
+ "registration.validations.password_confirmation_match": "సంకేతపదం వలె ఉండాలి",
+ "settings.app_name": "అనువర్తన పేరు",
+ "settings.attachmentRadius": "జోడింపులు",
+ "settings.attachments": "జోడింపులు",
+ "settings.autoload": "క్రిందికి స్క్రోల్ చేయబడినప్పుడు స్వయంచాలక లోడింగ్ని ప్రారంభించు",
+ "settings.avatar": "అవతారం",
+ "settings.avatarAltRadius": "అవతారాలు (ప్రకటనలు)",
+ "settings.avatarRadius": "అవతారాలు",
+ "settings.background": "బ్యాక్‌గ్రౌండు",
+ "settings.bio": "బయో",
+ "settings.blocks_tab": "బ్లాక్‌లు",
+ "settings.btnRadius": "బటన్లు",
+ "settings.cBlue": "నీలం (ప్రత్యుత్తరం, అనుసరించండి)",
+ "settings.cGreen": "Green (Retweet)",
+ "settings.cOrange": "ఆరెంజ్ (ఇష్టపడు)",
+ "settings.cRed": "Red (Cancel)",
+ "settings.change_password": "పాస్‌వర్డ్ మార్చండి",
+ "settings.change_password_error": "మీ పాస్వర్డ్ను మార్చడంలో సమస్య ఉంది.",
+ "settings.changed_password": "పాస్వర్డ్ విజయవంతంగా మార్చబడింది!",
+ "settings.collapse_subject": "Collapse posts with subjects",
+ "settings.composing": "Composing",
+ "settings.confirm_new_password": "కొత్త పాస్వర్డ్ను నిర్ధారించండి",
+ "settings.current_avatar": "మీ ప్రస్తుత అవతారం",
+ "settings.current_password": "ప్రస్తుత పాస్వర్డ్",
+ "settings.current_profile_banner": "మీ ప్రస్తుత ప్రొఫైల్ బ్యానర్",
+ "settings.data_import_export_tab": "Data Import / Export",
+ "settings.default_vis": "Default visibility scope",
+ "settings.delete_account": "Delete Account",
+ "settings.delete_account_description": "మీ ఖాతా మరియు మీ అన్ని సందేశాలను శాశ్వతంగా తొలగించండి.",
+ "settings.delete_account_error": "There was an issue deleting your account. If this persists please contact your instance administrator.",
+ "settings.delete_account_instructions": "ఖాతా తొలగింపును నిర్ధారించడానికి దిగువ ఇన్పుట్లో మీ పాస్వర్డ్ను టైప్ చేయండి.",
+ "settings.avatar_size_instruction": "అవతార్ చిత్రాలకు సిఫార్సు చేసిన కనీస పరిమాణం 150x150 పిక్సెల్స్.",
+ "settings.export_theme": "Save preset",
+ "settings.filtering": "వడపోత",
+ "settings.filtering_explanation": "All statuses containing these words will be muted, one per line",
+ "settings.follow_export": "Follow export",
+ "settings.follow_export_button": "Export your follows to a csv file",
+ "settings.follow_export_processing": "Processing, you'll soon be asked to download your file",
+ "settings.follow_import": "Follow import",
+ "settings.follow_import_error": "అనుచరులను దిగుమతి చేయడంలో లోపం",
+ "settings.follows_imported": "Follows imported! Processing them will take a while.",
+ "settings.foreground": "Foreground",
+ "settings.general": "General",
+ "settings.hide_attachments_in_convo": "సంభాషణలలో జోడింపులను దాచు",
+ "settings.hide_attachments_in_tl": "కాలక్రమంలో జోడింపులను దాచు",
+ "settings.hide_muted_posts": "మ్యూట్ చేసిన వినియోగదారుల యొక్క పోస్ట్లను దాచిపెట్టు",
+ "settings.max_thumbnails": "Maximum amount of thumbnails per post",
+ "settings.hide_isp": "Hide instance-specific panel",
+ "settings.preload_images": "Preload images",
+ "settings.use_one_click_nsfw": "కేవలం ఒక క్లిక్ తో NSFW జోడింపులను తెరవండి",
+ "settings.hide_post_stats": "Hide post statistics (e.g. the number of favorites)",
+ "settings.hide_user_stats": "Hide user statistics (e.g. the number of followers)",
+ "settings.hide_filtered_statuses": "Hide filtered statuses",
+ "settings.import_followers_from_a_csv_file": "Import follows from a csv file",
+ "settings.import_theme": "Load preset",
+ "settings.inputRadius": "Input fields",
+ "settings.checkboxRadius": "Checkboxes",
+ "settings.instance_default": "(default: {value})",
+ "settings.instance_default_simple": "(default)",
+ "settings.interface": "Interface",
+ "settings.interfaceLanguage": "Interface language",
+ "settings.invalid_theme_imported": "The selected file is not a supported Pleroma theme. No changes to your theme were made.",
+ "settings.limited_availability": "మీ బ్రౌజర్లో అందుబాటులో లేదు",
+ "settings.links": "Links",
+ "settings.lock_account_description": "మీ ఖాతాను ఆమోదించిన అనుచరులకు మాత్రమే పరిమితం చేయండి",
+ "settings.loop_video": "Loop videos",
+ "settings.loop_video_silent_only": "Loop only videos without sound (i.e. Mastodon's \"gifs\")",
+ "settings.mutes_tab": "మ్యూట్ చేయబడినవి",
+ "settings.play_videos_in_modal": "మీడియా వీక్షికలో నేరుగా వీడియోలను ప్లే చేయి",
+ "settings.use_contain_fit": "అటాచ్మెంట్ సూక్ష్మచిత్రాలను కత్తిరించవద్దు",
+ "settings.name": "Name",
+ "settings.name_bio": "పేరు & బయో",
+ "settings.new_password": "కొత్త సంకేతపదం",
+ "settings.notification_visibility": "చూపించవలసిన నోటిఫికేషన్ రకాలు",
+ "settings.notification_visibility_follows": "Follows",
+ "settings.notification_visibility_likes": "ఇష్టాలు",
+ "settings.notification_visibility_mentions": "ప్రస్తావనలు",
+ "settings.notification_visibility_repeats": "పునఃప్రసారాలు",
+ "settings.no_rich_text_description": "అన్ని పోస్ట్ల నుండి రిచ్ టెక్స్ట్ ఫార్మాటింగ్ను స్ట్రిప్ చేయండి",
+ "settings.no_blocks": "బ్లాక్స్ లేవు",
+ "settings.no_mutes": "మ్యూట్లు లేవు",
+ "settings.hide_follows_description": "నేను ఎవరిని అనుసరిస్తున్నానో చూపించవద్దు",
+ "settings.hide_followers_description": "నన్ను ఎవరు అనుసరిస్తున్నారో చూపవద్దు",
+ "settings.show_admin_badge": "నా ప్రొఫైల్ లో అడ్మిన్ బ్యాడ్జ్ చూపించు",
+ "settings.show_moderator_badge": "నా ప్రొఫైల్లో మోడరేటర్ బ్యాడ్జ్ని చూపించు",
+ "settings.nsfw_clickthrough": "Enable clickthrough NSFW attachment hiding",
+ "settings.oauth_tokens": "OAuth tokens",
+ "settings.token": "Token",
+ "settings.refresh_token": "Refresh Token",
+ "settings.valid_until": "Valid Until",
+ "settings.revoke_token": "Revoke",
+ "settings.panelRadius": "Panels",
+ "settings.pause_on_unfocused": "Pause streaming when tab is not focused",
+ "settings.presets": "Presets",
+ "settings.profile_background": "Profile Background",
+ "settings.profile_banner": "Profile Banner",
+ "settings.profile_tab": "Profile",
+ "settings.radii_help": "Set up interface edge rounding (in pixels)",
+ "settings.replies_in_timeline": "Replies in timeline",
+ "settings.reply_link_preview": "Enable reply-link preview on mouse hover",
+ "settings.reply_visibility_all": "Show all replies",
+ "settings.reply_visibility_following": "Only show replies directed at me or users I'm following",
+ "settings.reply_visibility_self": "Only show replies directed at me",
+ "settings.saving_err": "Error saving settings",
+ "settings.saving_ok": "Settings saved",
+ "settings.security_tab": "Security",
+ "settings.scope_copy": "Copy scope when replying (DMs are always copied)",
+ "settings.set_new_avatar": "Set new avatar",
+ "settings.set_new_profile_background": "Set new profile background",
+ "settings.set_new_profile_banner": "Set new profile banner",
+ "settings.settings": "Settings",
+ "settings.subject_input_always_show": "Always show subject field",
+ "settings.subject_line_behavior": "Copy subject when replying",
+ "settings.subject_line_email": "Like email: \"re: subject\"",
+ "settings.subject_line_mastodon": "Like mastodon: copy as is",
+ "settings.subject_line_noop": "Do not copy",
+ "settings.post_status_content_type": "Post status content type",
+ "settings.stop_gifs": "Play-on-hover GIFs",
+ "settings.streaming": "Enable automatic streaming of new posts when scrolled to the top",
+ "settings.text": "Text",
+ "settings.theme": "Theme",
+ "settings.theme_help": "Use hex color codes (#rrggbb) to customize your color theme.",
+ "settings.theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.",
+ "settings.theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.",
+ "settings.tooltipRadius": "Tooltips/alerts",
+ "settings.upload_a_photo": "Upload a photo",
+ "settings.user_settings": "User Settings",
+ "settings.values.false": "no",
+ "settings.values.true": "yes",
+ "settings.notifications": "Notifications",
+ "settings.enable_web_push_notifications": "Enable web push notifications",
+ "settings.style.switcher.keep_color": "Keep colors",
+ "settings.style.switcher.keep_shadows": "Keep shadows",
+ "settings.style.switcher.keep_opacity": "Keep opacity",
+ "settings.style.switcher.keep_roundness": "Keep roundness",
+ "settings.style.switcher.keep_fonts": "Keep fonts",
+ "settings.style.switcher.save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme. When all checkboxes unset, exporting theme will save everything.",
+ "settings.style.switcher.reset": "Reset",
+ "settings.style.switcher.clear_all": "Clear all",
+ "settings.style.switcher.clear_opacity": "Clear opacity",
+ "settings.style.common.color": "Color",
+ "settings.style.common.opacity": "Opacity",
+ "settings.style.common.contrast.hint": "Contrast ratio is {ratio}, it {level} {context}",
+ "settings.style.common.contrast.level.aa": "meets Level AA guideline (minimal)",
+ "settings.style.common.contrast.level.aaa": "meets Level AAA guideline (recommended)",
+ "settings.style.common.contrast.level.bad": "doesn't meet any accessibility guidelines",
+ "settings.style.common.contrast.context.18pt": "for large (18pt+) text",
+ "settings.style.common.contrast.context.text": "for text",
+ "settings.style.common_colors._tab_label": "Common",
+ "settings.style.common_colors.main": "Common colors",
+ "settings.style.common_colors.foreground_hint": "See \"Advanced\" tab for more detailed control",
+ "settings.style.common_colors.rgbo": "Icons, accents, badges",
+ "settings.style.advanced_colors._tab_label": "Advanced",
+ "settings.style.advanced_colors.alert": "Alert background",
+ "settings.style.advanced_colors.alert_error": "Error",
+ "settings.style.advanced_colors.badge": "Badge background",
+ "settings.style.advanced_colors.badge_notification": "Notification",
+ "settings.style.advanced_colors.panel_header": "Panel header",
+ "settings.style.advanced_colors.top_bar": "Top bar",
+ "settings.style.advanced_colors.borders": "Borders",
+ "settings.style.advanced_colors.buttons": "Buttons",
+ "settings.style.advanced_colors.inputs": "Input fields",
+ "settings.style.advanced_colors.faint_text": "Faded text",
+ "settings.style.radii._tab_label": "Roundness",
+ "settings.style.shadows._tab_label": "Shadow and lighting",
+ "settings.style.shadows.component": "Component",
+ "settings.style.shadows.override": "Override",
+ "settings.style.shadows.shadow_id": "Shadow #{value}",
+ "settings.style.shadows.blur": "Blur",
+ "settings.style.shadows.spread": "Spread",
+ "settings.style.shadows.inset": "Inset",
+ "settings.style.shadows.hint": "For shadows you can also use --variable as a color value to use CSS3 variables. Please note that setting opacity won't work in this case.",
+ "settings.style.shadows.filter_hint.always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.",
+ "settings.style.shadows.filter_hint.drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.",
+ "settings.style.shadows.filter_hint.avatar_inset": "Please note that combining both inset and non-inset shadows on avatars might give unexpected results with transparent avatars.",
+ "settings.style.shadows.filter_hint.spread_zero": "Shadows with spread > 0 will appear as if it was set to zero",
+ "settings.style.shadows.filter_hint.inset_classic": "Inset shadows will be using {0}",
+ "settings.style.shadows.components.panel": "Panel",
+ "settings.style.shadows.components.panelHeader": "Panel header",
+ "settings.style.shadows.components.topBar": "Top bar",
+ "settings.style.shadows.components.avatar": "User avatar (in profile view)",
+ "settings.style.shadows.components.avatarStatus": "User avatar (in post display)",
+ "settings.style.shadows.components.popup": "Popups and tooltips",
+ "settings.style.shadows.components.button": "Button",
+ "settings.style.shadows.components.buttonHover": "Button (hover)",
+ "settings.style.shadows.components.buttonPressed": "Button (pressed)",
+ "settings.style.shadows.components.buttonPressedHover": "Button (pressed+hover)",
+ "settings.style.shadows.components.input": "Input field",
+ "settings.style.fonts._tab_label": "Fonts",
+ "settings.style.fonts.help": "Select font to use for elements of UI. For \"custom\" you have to enter exact font name as it appears in system.",
+ "settings.style.fonts.components.interface": "Interface",
+ "settings.style.fonts.components.input": "Input fields",
+ "settings.style.fonts.components.post": "Post text",
+ "settings.style.fonts.components.postCode": "Monospaced text in a post (rich text)",
+ "settings.style.fonts.family": "Font name",
+ "settings.style.fonts.size": "Size (in px)",
+ "settings.style.fonts.weight": "Weight (boldness)",
+ "settings.style.fonts.custom": "Custom",
+ "settings.style.preview.header": "Preview",
+ "settings.style.preview.content": "Content",
+ "settings.style.preview.error": "Example error",
+ "settings.style.preview.button": "Button",
+ "settings.style.preview.text": "A bunch of more {0} and {1}",
+ "settings.style.preview.mono": "content",
+ "settings.style.preview.input": "Just landed in L.A.",
+ "settings.style.preview.faint_link": "helpful manual",
+ "settings.style.preview.fine_print": "Read our {0} to learn nothing useful!",
+ "settings.style.preview.header_faint": "This is fine",
+ "settings.style.preview.checkbox": "I have skimmed over terms and conditions",
+ "settings.style.preview.link": "a nice lil' link",
+ "settings.version.title": "Version",
+ "settings.version.backend_version": "Backend Version",
+ "settings.version.frontend_version": "Frontend Version",
+ "timeline.collapse": "Collapse",
+ "timeline.conversation": "Conversation",
+ "timeline.error_fetching": "Error fetching updates",
+ "timeline.load_older": "Load older statuses",
+ "timeline.no_retweet_hint": "Post is marked as followers-only or direct and cannot be repeated",
+ "timeline.repeated": "repeated",
+ "timeline.show_new": "Show new",
+ "timeline.up_to_date": "Up-to-date",
+ "timeline.no_more_statuses": "No more statuses",
+ "timeline.no_statuses": "No statuses",
+ "status.reply_to": "Reply to",
+ "status.replies_list": "Replies:",
+ "user_card.approve": "Approve",
+ "user_card.block": "Block",
+ "user_card.blocked": "Blocked!",
+ "user_card.deny": "Deny",
+ "user_card.favorites": "Favorites",
+ "user_card.follow": "Follow",
+ "user_card.follow_sent": "Request sent!",
+ "user_card.follow_progress": "Requesting…",
+ "user_card.follow_again": "Send request again?",
+ "user_card.follow_unfollow": "Unfollow",
+ "user_card.followees": "Following",
+ "user_card.followers": "Followers",
+ "user_card.following": "Following!",
+ "user_card.follows_you": "Follows you!",
+ "user_card.its_you": "It's you!",
+ "user_card.media": "Media",
+ "user_card.mute": "Mute",
+ "user_card.muted": "Muted",
+ "user_card.per_day": "per day",
+ "user_card.remote_follow": "Remote follow",
+ "user_card.statuses": "Statuses",
+ "user_card.unblock": "Unblock",
+ "user_card.unblock_progress": "Unblocking...",
+ "user_card.block_progress": "Blocking...",
+ "user_card.unmute": "Unmute",
+ "user_card.unmute_progress": "Unmuting...",
+ "user_card.mute_progress": "Muting...",
+ "user_profile.timeline_title": "User Timeline",
+ "user_profile.profile_does_not_exist": "Sorry, this profile does not exist.",
+ "user_profile.profile_loading_error": "Sorry, there was an error loading this profile.",
+ "who_to_follow.more": "More",
+ "who_to_follow.who_to_follow": "Who to follow",
+ "tool_tip.media_upload": "Upload Media",
+ "tool_tip.repeat": "Repeat",
+ "tool_tip.reply": "Reply",
+ "tool_tip.favorite": "Favorite",
+ "tool_tip.user_settings": "User Settings",
+ "upload.error.base": "Upload failed.",
+ "upload.error.file_too_big": "File too big [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
+ "upload.error.default": "Try again later",
+ "upload.file_size_units.B": "B",
+ "upload.file_size_units.KiB": "KiB",
+ "upload.file_size_units.MiB": "MiB",
+ "upload.file_size_units.GiB": "GiB",
+ "upload.file_size_units.TiB": "TiB"
+}
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index 7d5d5a67..e863d8a5 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -430,6 +430,10 @@ export const mutations = {
const newStatus = state.allStatusesObject[status.id]
newStatus.pinned = status.pinned
},
+ setMuted (state, status) {
+ const newStatus = state.allStatusesObject[status.id]
+ newStatus.muted = status.muted
+ },
setRetweeted (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id]
@@ -564,6 +568,14 @@ const statuses = {
rootState.api.backendInteractor.unpinOwnStatus(statusId)
.then((status) => commit('setPinned', status))
},
+ muteConversation ({ rootState, commit }, statusId) {
+ return rootState.api.backendInteractor.muteConversation(statusId)
+ .then((status) => commit('setMuted', status))
+ },
+ unmuteConversation ({ rootState, commit }, statusId) {
+ return rootState.api.backendInteractor.unmuteConversation(statusId)
+ .then((status) => commit('setMuted', status))
+ },
retweet ({ rootState, commit }, status) {
// Optimistic retweeting...
commit('setRetweeted', { status, value: true })
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 84616dd1..083d4f4f 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -67,6 +67,8 @@ const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials'
const MASTODON_REPORT_USER_URL = '/api/v1/reports'
const MASTODON_PIN_OWN_STATUS = id => `/api/v1/statuses/${id}/pin`
const MASTODON_UNPIN_OWN_STATUS = id => `/api/v1/statuses/${id}/unpin`
+const MASTODON_MUTE_CONVERSATION = id => `/api/v1/statuses/${id}/mute`
+const MASTODON_UNMUTE_CONVERSATION = id => `/api/v1/statuses/${id}/unmute`
const MASTODON_SEARCH_2 = `/api/v2/search`
const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search'
@@ -252,6 +254,16 @@ const unpinOwnStatus = ({ id, credentials }) => {
.then((data) => parseStatus(data))
}
+const muteConversation = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_MUTE_CONVERSATION(id), credentials, method: 'POST' })
+ .then((data) => parseStatus(data))
+}
+
+const unmuteConversation = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_UNMUTE_CONVERSATION(id), credentials, method: 'POST' })
+ .then((data) => parseStatus(data))
+}
+
const blockUser = ({ id, credentials }) => {
return fetch(MASTODON_BLOCK_USER_URL(id), {
headers: authHeaders(credentials),
@@ -920,6 +932,8 @@ const apiService = {
unfollowUser,
pinOwnStatus,
unpinOwnStatus,
+ muteConversation,
+ unmuteConversation,
blockUser,
unblockUser,
fetchUser,
diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js
index bdfe0465..846d9415 100644
--- a/src/services/backend_interactor_service/backend_interactor_service.js
+++ b/src/services/backend_interactor_service/backend_interactor_service.js
@@ -117,6 +117,8 @@ const backendInteractorService = credentials => {
const fetchPinnedStatuses = (id) => apiService.fetchPinnedStatuses({ credentials, id })
const pinOwnStatus = (id) => apiService.pinOwnStatus({ credentials, id })
const unpinOwnStatus = (id) => apiService.unpinOwnStatus({ credentials, id })
+ const muteConversation = (id) => apiService.muteConversation({ credentials, id })
+ const unmuteConversation = (id) => apiService.unmuteConversation({ credentials, id })
const getCaptcha = () => apiService.getCaptcha()
const register = (params) => apiService.register({ credentials, params })
@@ -178,6 +180,8 @@ const backendInteractorService = credentials => {
fetchPinnedStatuses,
pinOwnStatus,
unpinOwnStatus,
+ muteConversation,
+ unmuteConversation,
tagUser,
untagUser,
addRight,
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 8b4d2594..6cc1851d 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -65,6 +65,7 @@ export const parseUser = (data) => {
if (relationship) {
output.follows_you = relationship.followed_by
+ output.requested = relationship.requested
output.following = relationship.following
output.statusnet_blocking = relationship.blocking
output.muted = relationship.muting
@@ -240,6 +241,7 @@ export const parseStatus = (data) => {
output.external_url = data.url
output.poll = data.poll
output.pinned = data.pinned
+ output.muted = data.muted
} else {
output.favorited = data.favorited
output.fave_num = data.fave_num
diff --git a/src/services/follow_manipulate/follow_manipulate.js b/src/services/follow_manipulate/follow_manipulate.js
index b2486e7c..529fdb9b 100644
--- a/src/services/follow_manipulate/follow_manipulate.js
+++ b/src/services/follow_manipulate/follow_manipulate.js
@@ -2,17 +2,17 @@ const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => {
setTimeout(() => {
store.state.api.backendInteractor.fetchUser({ id: user.id })
.then((user) => store.commit('addNewUsers', [user]))
- .then(() => resolve([user.following, attempt]))
+ .then(() => resolve([user.following, user.requested, user.locked, attempt]))
.catch((e) => reject(e))
}, 500)
-}).then(([following, attempt]) => {
- if (!following && attempt <= 3) {
+}).then(([following, sent, locked, attempt]) => {
+ if (!following && !(locked && sent) && attempt <= 3) {
// If we BE reports that we still not following that user - retry,
// increment attempts by one
return fetchUser(++attempt, user, store)
} else {
// If we run out of attempts, just return whatever status is.
- return following
+ return sent
}
})
@@ -21,14 +21,10 @@ export const requestFollow = (user, store) => new Promise((resolve, reject) => {
.then((updated) => {
store.commit('updateUserRelationship', [updated])
- // For locked users we just mark it that we sent the follow request
- if (updated.locked) {
- resolve({ sent: true })
- }
-
- if (updated.following) {
- // If we get result immediately, just stop.
- resolve({ sent: false })
+ if (updated.following || (user.locked && user.requested)) {
+ // If we get result immediately or the account is locked, just stop.
+ resolve({ sent: updated.requested })
+ return
}
// But usually we don't get result immediately, so we ask server
@@ -39,14 +35,8 @@ export const requestFollow = (user, store) => new Promise((resolve, reject) => {
// Recursive Promise, it will call itself up to 3 times.
return fetchUser(1, user, store)
- .then((following) => {
- if (following) {
- // We confirmed and everything's good.
- resolve({ sent: false })
- } else {
- // If after all the tries, just treat it as if user is locked
- resolve({ sent: false })
- }
+ .then((sent) => {
+ resolve({ sent })
})
})
})
diff --git a/yarn.lock b/yarn.lock
index 13d94a23..d6c8b4a6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1387,10 +1387,29 @@ cache-base@^1.0.1:
union-value "^1.0.0"
unset-value "^1.0.0"
+caller-callsite@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+ integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
+ dependencies:
+ callsites "^2.0.0"
+
+caller-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+ integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
+ dependencies:
+ caller-callsite "^2.0.0"
+
callsite@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
+callsites@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+ integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@@ -1817,6 +1836,16 @@ cosmiconfig@^2.1.0, cosmiconfig@^2.1.1:
parse-json "^2.2.0"
require-from-string "^1.1.0"
+cosmiconfig@^5.0.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+ integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
+ dependencies:
+ import-fresh "^2.0.0"
+ is-directory "^0.3.1"
+ js-yaml "^3.13.1"
+ parse-json "^4.0.0"
+
create-ecdh@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
@@ -2390,7 +2419,7 @@ errno@^0.1.3, errno@~0.1.7:
dependencies:
prr "~1.0.1"
-error-ex@^1.2.0:
+error-ex@^1.2.0, error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
dependencies:
@@ -3503,6 +3532,21 @@ immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+import-cwd@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
+ integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=
+ dependencies:
+ import-from "^2.1.0"
+
+import-fresh@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+ integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
+ dependencies:
+ caller-path "^2.0.0"
+ resolve-from "^3.0.0"
+
import-fresh@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390"
@@ -3510,6 +3554,13 @@ import-fresh@^3.0.0:
parent-module "^1.0.0"
resolve-from "^4.0.0"
+import-from@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1"
+ integrity sha1-M1238qev/VOqpHHUuAId7ja387E=
+ dependencies:
+ resolve-from "^3.0.0"
+
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -3949,7 +4000,7 @@ js-yaml@3.x, js-yaml@^3.4.3:
argparse "^1.0.7"
esprima "^4.0.0"
-js-yaml@^3.13.0:
+js-yaml@^3.13.0, js-yaml@^3.13.1:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
dependencies:
@@ -3979,7 +4030,7 @@ json-loader@^0.5.4:
version "0.5.7"
resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d"
-json-parse-better-errors@^1.0.2:
+json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -5315,6 +5366,14 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
+parse-json@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+ integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+ dependencies:
+ error-ex "^1.3.1"
+ json-parse-better-errors "^1.0.1"
+
parseqs@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
@@ -5541,6 +5600,14 @@ postcss-load-config@^1.1.0:
postcss-load-options "^1.2.0"
postcss-load-plugins "^2.3.0"
+postcss-load-config@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003"
+ integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==
+ dependencies:
+ cosmiconfig "^5.0.0"
+ import-cwd "^2.0.0"
+
postcss-load-options@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/postcss-load-options/-/postcss-load-options-1.2.0.tgz#b098b1559ddac2df04bc0bb375f99a5cfe2b6d8c"
@@ -5555,6 +5622,16 @@ postcss-load-plugins@^2.3.0:
cosmiconfig "^2.1.1"
object-assign "^4.1.0"
+postcss-loader@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d"
+ integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==
+ dependencies:
+ loader-utils "^1.1.0"
+ postcss "^7.0.0"
+ postcss-load-config "^2.0.0"
+ schema-utils "^1.0.0"
+
postcss-merge-idents@^2.1.5:
version "2.1.7"
resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
@@ -5740,6 +5817,15 @@ postcss@^6.0.1, postcss@^6.0.8:
source-map "^0.6.1"
supports-color "^5.4.0"
+postcss@^7.0.0:
+ version "7.0.17"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f"
+ integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==
+ dependencies:
+ chalk "^2.4.2"
+ source-map "^0.6.1"
+ supports-color "^6.1.0"
+
postcss@^7.0.5:
version "7.0.8"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.8.tgz#2a3c5f2bdd00240cd0d0901fd998347c93d36696"
@@ -6203,6 +6289,11 @@ requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+resolve-from@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+ integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@@ -6844,7 +6935,7 @@ supports-color@^5.3.0, supports-color@^5.4.0:
dependencies:
has-flag "^3.0.0"
-supports-color@^6.0.0:
+supports-color@^6.0.0, supports-color@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
dependencies: