$store.dispatch('fetchBlocks'),
select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),
- childPropName: 'items'
+ childPropName: 'items',
+ destroy: () => {}
})(SelectableList)
-const MuteList = withSubscription({
+const MuteList = withLoadMore({
fetch: (props, $store) => $store.dispatch('fetchMutes'),
select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),
- childPropName: 'items'
+ childPropName: 'items',
+ destroy: () => {}
})(SelectableList)
const DomainMuteList = withSubscription({
diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js
index c29e8d2a..46cb91e5 100644
--- a/src/components/settings_modal/tabs/profile_tab.js
+++ b/src/components/settings_modal/tabs/profile_tab.js
@@ -12,6 +12,7 @@ import InterfaceLanguageSwitcher from 'src/components/interface_language_switche
import BooleanSetting from '../helpers/boolean_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import localeService from 'src/services/locale/locale.service.js'
+import ChoiceSetting from '../helpers/choice_setting.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@@ -46,9 +47,16 @@ const ProfileTab = {
emailLanguage: this.$store.state.users.currentUser.language || '',
newPostTTLDays: this.$store.state.users.currentUser.status_ttl_days,
expirePosts: this.$store.state.users.currentUser.status_ttl_days !== null,
+ userAcceptsDirectMessagesFrom: this.$store.state.users.currentUser.accepts_direct_messages_from,
+ userAcceptsDirectMessagesFromOptions: ["everybody", "nobody", "people_i_follow"].map(mode => ({
+ key: mode,
+ value: mode,
+ label: this.$t(`settings.user_accepts_direct_messages_from_${mode}`)
+ }))
}
},
components: {
+ ChoiceSetting,
ScopeSelector,
ImageCropper,
EmojiInput,
@@ -126,7 +134,8 @@ const ProfileTab = {
fields_attributes: this.newFields.filter(el => el != null),
bot: this.bot,
show_role: this.showRole,
- status_ttl_days: this.expirePosts ? this.newPostTTLDays : -1
+ status_ttl_days: this.expirePosts ? this.newPostTTLDays : -1,
+ accepts_direct_messages_from: this.userAcceptsDirectMessagesFrom
/* eslint-enable camelcase */
}
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
index 8748b685..9f80582f 100644
--- a/src/components/settings_modal/tabs/profile_tab.vue
+++ b/src/components/settings_modal/tabs/profile_tab.vue
@@ -89,6 +89,15 @@
{{ $t('settings.bot') }}
+
+
+ {{ $t('settings.user_accepts_direct_messages_from') }}
+
+
{{ $t('settings.expire_posts_enabled') }}
@@ -102,6 +111,9 @@
class="expire-posts-days"
:placeholder="$t('settings.expire_posts_input_placeholder')"
/>
+
+
+
@{{ status.user.screen_name_ui }}
-
+ />
diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss
index d618f65e..434cb482 100644
--- a/src/components/status_body/status_body.scss
+++ b/src/components/status_body/status_body.scss
@@ -17,6 +17,9 @@
.emoji:hover {
transform: scale(1.4);
+ @media (prefers-reduced-motion: reduce) {
+ transition: unset;
+ }
transition: 0.05s;
}
diff --git a/src/components/still-image/still-image.js b/src/components/still-image/still-image.js
index 480de9fa..9c335d6c 100644
--- a/src/components/still-image/still-image.js
+++ b/src/components/still-image/still-image.js
@@ -11,8 +11,8 @@ const StillImage = {
],
data () {
return {
- stopGifs: this.$store.getters.mergedConfig.stopGifs,
- isAnimated: false
+ stopGifs: this.$store.getters.mergedConfig.stopGifs || window.matchMedia('(prefers-reduced-motion: reduce)').matches,
+ isAnimated: false,
}
},
computed: {
@@ -39,12 +39,25 @@ const StillImage = {
this.imageLoadError && this.imageLoadError()
},
detectAnimation (image) {
+ // If there are no file extensions, the mimetype isn't set, and no mediaproxy is available, we can't figure out
+ // the mimetype of the image.
+ const hasFileExtension = this.src.split('/').pop().includes('.') // TODO: Better check?
+ const mediaProxyAvailable = this.$store.state.instance.mediaProxyAvailable
+ if (!hasFileExtension && this.mimetype === undefined && !mediaProxyAvailable) {
+ // It's a bit aggressive to assume all images we can't find the mimetype of is animated, but necessary for
+ // people in need of reduced motion accessibility. As such, we'll consider those images animated if the user
+ // agent is set to prefer reduced motion. Otherwise, it'll just be used as an early exit.
+ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches)
+ this.isAnimated = true
+ return
+ }
+
if (this.mimetype === 'image/gif' || this.src.endsWith('.gif')) {
this.isAnimated = true
return
}
// harmless CORS errors without-- clean console with
- if (!this.$store.state.instance.mediaProxyAvailable) return
+ if (!mediaProxyAvailable) return
// Animated JPEGs?
if (!(this.src.endsWith('.webp') || this.src.endsWith('.png'))) return
// Browser Cache should ensure image doesn't get loaded twice if cache exists
diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue
index 61119482..2dd52346 100644
--- a/src/components/timeline_menu/timeline_menu.vue
+++ b/src/components/timeline_menu/timeline_menu.vue
@@ -62,6 +62,9 @@
border-top-right-radius: 0;
border-top-left-radius: 0;
transform: translateY(-100%);
+ @media (prefers-reduced-motion: reduce) {
+ transition: unset;
+ }
transition: transform 100ms;
}
@@ -89,6 +92,9 @@
svg {
margin-left: 0.6em;
+ @media (prefers-reduced-motion: reduce) {
+ transition: unset;
+ }
transition: transform 100ms;
}
diff --git a/src/components/timeline_menu/timeline_menu_content.js b/src/components/timeline_menu/timeline_menu_content.js
index ab277d74..80cf6937 100644
--- a/src/components/timeline_menu/timeline_menu_content.js
+++ b/src/components/timeline_menu/timeline_menu_content.js
@@ -8,6 +8,7 @@ import {
faHome,
faCircle
} from '@fortawesome/free-solid-svg-icons'
+import { federatedTimelineVisible, publicTimelineVisible, bubbleTimelineVisible } from '../../lib/timeline_visibility'
library.add(
faUsers,
@@ -24,8 +25,9 @@ const TimelineMenuContent = {
currentUser: state => state.users.currentUser,
privateMode: state => state.instance.private,
federating: state => state.instance.federating,
- showBubbleTimeline: state => (state.instance.localBubbleInstances.length > 0),
- publicTimelineVisibility: state => state.instance.publicTimelineVisibility,
+ publicTimelineVisible,
+ federatedTimelineVisible,
+ bubbleTimelineVisible,
})
}
}
diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue
index 0144f2fd..7c351721 100644
--- a/src/components/timeline_menu/timeline_menu_content.vue
+++ b/src/components/timeline_menu/timeline_menu_content.vue
@@ -16,23 +16,7 @@
>{{ $t("nav.home_timeline") }}
-