diff --git a/.babelrc b/.babelrc
index bc2b0e31a..3c732dd1b 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,5 +1,5 @@
{
- "presets": ["es2015", "stage-2", "env"],
- "plugins": ["transform-runtime", "lodash", "transform-vue-jsx"],
+ "presets": ["@babel/preset-env"],
+ "plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-transform-vue-jsx"],
"comments": false
}
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
index 353635372..0a9bbd7ab 100644
--- a/docs/CONFIGURATION.md
+++ b/docs/CONFIGURATION.md
@@ -77,6 +77,9 @@ Use custom image for NSFW'd images
### `showFeaturesPanel`
Show panel showcasing instance features/settings to logged-out visitors
+### `hideSitename`
+Hide instance name in header
+
## Indirect configuration
Some features are configured depending on how backend is configured. In general the approach is "if backend allows it there's no need to hide it, if backend doesn't allow it there's no need to show it.
@@ -96,3 +99,6 @@ Setting this will change the warning text that is displayed for direct messages.
ATTENTION: If you actually want the behavior to change. You will need to set the appropriate option at the backend. See the backend documentation for information about that.
DO NOT activate this without checking the backend configuration first!
+
+### Private Mode
+If the `private` instance setting is enabled in the backend, features that are not accessible without authentication, such as the timelines and search will be disabled for unauthenticated users.
diff --git a/package.json b/package.json
index 3c9598eb2..38936b237 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,8 @@
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
},
"dependencies": {
+ "@babel/runtime": "^7.7.6",
"@chenfengyuan/vue-qrcode": "^1.0.0",
- "babel-plugin-add-module-exports": "^0.2.1",
- "babel-plugin-lodash": "^3.2.11",
"body-scroll-lock": "^2.6.4",
"chromatism": "^3.0.0",
"cropperjs": "^1.4.3",
@@ -40,20 +39,17 @@
"whatwg-fetch": "^2.0.3"
},
"devDependencies": {
- "@babel/polyfill": "^7.0.0",
+ "@babel/core": "^7.7.5",
+ "@babel/plugin-transform-runtime": "^7.7.6",
+ "@babel/preset-env": "^7.7.6",
+ "@babel/register": "^7.7.4",
+ "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",
+ "@vue/babel-plugin-transform-vue-jsx": "^1.1.2",
"@vue/test-utils": "^1.0.0-beta.26",
"autoprefixer": "^6.4.0",
- "babel-core": "^6.0.0",
"babel-eslint": "^7.0.0",
- "babel-helper-vue-jsx-merge-props": "^2.0.3",
- "babel-loader": "^7.0.0",
- "babel-plugin-syntax-jsx": "^6.18.0",
- "babel-plugin-transform-runtime": "^6.0.0",
- "babel-plugin-transform-vue-jsx": "3",
- "babel-preset-env": "^1.7.0",
- "babel-preset-es2015": "^6.0.0",
- "babel-preset-stage-2": "^6.0.0",
- "babel-register": "^6.0.0",
+ "babel-loader": "^8.0.6",
+ "babel-plugin-lodash": "^3.3.4",
"chai": "^3.5.0",
"chalk": "^1.1.3",
"chromedriver": "^2.21.2",
diff --git a/src/App.js b/src/App.js
index 04a40e304..61b5eec1c 100644
--- a/src/App.js
+++ b/src/App.js
@@ -90,6 +90,7 @@ export default {
},
sitename () { return this.$store.state.instance.name },
chat () { return this.$store.state.chat.channel.state === 'joined' },
+ hideSitename () { return this.$store.state.instance.hideSitename },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&
@@ -97,7 +98,8 @@ export default {
this.$store.state.instance.instanceSpecificPanelContent
},
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
- isMobileLayout () { return this.$store.state.interface.mobileLayout }
+ isMobileLayout () { return this.$store.state.interface.mobileLayout },
+ privateMode () { return this.$store.state.instance.private }
},
methods: {
scrollToTop () {
diff --git a/src/App.scss b/src/App.scss
index 925913f2d..754ca62e6 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -870,3 +870,16 @@ nav {
transform: rotate(359deg);
}
}
+
+.new-status-notification {
+ position:relative;
+ margin-top: -1px;
+ font-size: 1.1em;
+ border-width: 1px 0 0 0;
+ border-style: solid;
+ border-color: var(--border, $fallback--border);
+ padding: 10px;
+ z-index: 1;
+ background-color: $fallback--fg;
+ background-color: var(--panel, $fallback--fg);
+}
diff --git a/src/App.vue b/src/App.vue
index dbe842ec4..1b1c2648c 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -31,6 +31,7 @@
{
copyInstanceOption('alwaysShowSubjectInput')
copyInstanceOption('noAttachmentLinks')
copyInstanceOption('showFeaturesPanel')
+ copyInstanceOption('hideSitename')
return store.dispatch('setTheme', config['theme'])
}
@@ -218,12 +219,21 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })
store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: software.name === 'pleroma' })
+ const priv = metadata.private
+ store.dispatch('setInstanceOption', { name: 'private', value: priv })
+
const frontendVersion = window.___pleromafe_commit_hash
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
store.dispatch('setInstanceOption', { name: 'tagPolicyAvailable', value: metadata.federation.mrf_policies.includes('TagPolicy') })
const federation = metadata.federation
store.dispatch('setInstanceOption', { name: 'federationPolicy', value: federation })
+ store.dispatch('setInstanceOption', {
+ name: 'federating',
+ value: typeof federation.enabled === 'undefined'
+ ? true
+ : federation.enabled
+ })
const accounts = metadata.staffAccounts
await resolveStaffAccounts({ store, accounts })
diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js
index 1a00a1c14..a89317872 100644
--- a/src/components/follow_request_card/follow_request_card.js
+++ b/src/components/follow_request_card/follow_request_card.js
@@ -7,11 +7,11 @@ const FollowRequestCard = {
},
methods: {
approveUser () {
- this.$store.state.api.backendInteractor.approveUser(this.user.id)
+ this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
},
denyUser () {
- this.$store.state.api.backendInteractor.denyUser(this.user.id)
+ this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
}
}
diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js
index 0b574a048..0d8f1da65 100644
--- a/src/components/login_form/login_form.js
+++ b/src/components/login_form/login_form.js
@@ -58,7 +58,7 @@ const LoginForm = {
).then((result) => {
if (result.error) {
if (result.error === 'mfa_required') {
- this.requireMFA({ app: app, settings: result })
+ this.requireMFA({ settings: result })
} else if (result.identifier === 'password_reset_required') {
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
} else {
diff --git a/src/components/mfa_form/recovery_form.js b/src/components/mfa_form/recovery_form.js
index 7a3cc22d5..b25c65dd3 100644
--- a/src/components/mfa_form/recovery_form.js
+++ b/src/components/mfa_form/recovery_form.js
@@ -8,18 +8,23 @@ export default {
}),
computed: {
...mapGetters({
- authApp: 'authFlow/app',
authSettings: 'authFlow/settings'
}),
- ...mapState({ instance: 'instance' })
+ ...mapState({
+ instance: 'instance',
+ oauth: 'oauth'
+ })
},
methods: {
...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),
...mapActions({ login: 'authFlow/login' }),
clearError () { this.error = false },
submit () {
+ const { clientId, clientSecret } = this.oauth
+
const data = {
- app: this.authApp,
+ clientId,
+ clientSecret,
instance: this.instance.server,
mfaToken: this.authSettings.mfa_token,
code: this.code
diff --git a/src/components/mfa_form/totp_form.js b/src/components/mfa_form/totp_form.js
index 778bf8dce..b774f2d0c 100644
--- a/src/components/mfa_form/totp_form.js
+++ b/src/components/mfa_form/totp_form.js
@@ -7,18 +7,23 @@ export default {
}),
computed: {
...mapGetters({
- authApp: 'authFlow/app',
authSettings: 'authFlow/settings'
}),
- ...mapState({ instance: 'instance' })
+ ...mapState({
+ instance: 'instance',
+ oauth: 'oauth'
+ })
},
methods: {
...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),
...mapActions({ login: 'authFlow/login' }),
clearError () { this.error = false },
submit () {
+ const { clientId, clientSecret } = this.oauth
+
const data = {
- app: this.authApp,
+ clientId,
+ clientSecret,
instance: this.instance.server,
mfaToken: this.authSettings.mfa_token,
code: this.code
diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js
index 5a90c31f4..c1166a0cd 100644
--- a/src/components/mobile_nav/mobile_nav.js
+++ b/src/components/mobile_nav/mobile_nav.js
@@ -29,6 +29,7 @@ const MobileNav = {
unseenNotificationsCount () {
return this.unseenNotifications.length
},
+ hideSitename () { return this.$store.state.instance.hideSitename },
sitename () { return this.$store.state.instance.name }
},
methods: {
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index d1c24e56d..51f1d6362 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -17,6 +17,7 @@
{
+ store.state.api.backendInteractor.untagUser({ user: this.user, tag }).then(response => {
if (!response.ok) { return }
store.commit('untagUser', { user: this.user, tag })
})
} else {
- store.state.api.backendInteractor.tagUser(this.user, tag).then(response => {
+ store.state.api.backendInteractor.tagUser({ user: this.user, tag }).then(response => {
if (!response.ok) { return }
store.commit('tagUser', { user: this.user, tag })
})
@@ -59,24 +59,19 @@ const ModerationTools = {
toggleRight (right) {
const store = this.$store
if (this.user.rights[right]) {
- store.state.api.backendInteractor.deleteRight(this.user, right).then(response => {
+ store.state.api.backendInteractor.deleteRight({ user: this.user, right }).then(response => {
if (!response.ok) { return }
- store.commit('updateRight', { user: this.user, right: right, value: false })
+ store.commit('updateRight', { user: this.user, right, value: false })
})
} else {
- store.state.api.backendInteractor.addRight(this.user, right).then(response => {
+ store.state.api.backendInteractor.addRight({ user: this.user, right }).then(response => {
if (!response.ok) { return }
- store.commit('updateRight', { user: this.user, right: right, value: true })
+ store.commit('updateRight', { user: this.user, right, value: true })
})
}
},
toggleActivationStatus () {
- const store = this.$store
- const status = !!this.user.deactivated
- store.state.api.backendInteractor.setActivationStatus(this.user, status).then(response => {
- if (!response.ok) { return }
- store.commit('updateActivationStatus', { user: this.user, status: status })
- })
+ this.$store.dispatch('toggleActivationStatus', { user: this.user })
},
deleteUserDialog (show) {
this.showDeleteUserDialog = show
@@ -85,7 +80,7 @@ const ModerationTools = {
const store = this.$store
const user = this.user
const { id, name } = user
- store.state.api.backendInteractor.deleteUser(user)
+ store.state.api.backendInteractor.deleteUser({ user })
.then(e => {
this.$store.dispatch('markStatusesAsDeleted', status => user.id === status.user.id)
const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile'
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index 7f783acba..d92685858 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -1,20 +1,18 @@
+import { mapState } from 'vuex'
+
const NavPanel = {
created () {
if (this.currentUser && this.currentUser.locked) {
this.$store.dispatch('startFetchingFollowRequest')
}
},
- computed: {
- currentUser () {
- return this.$store.state.users.currentUser
- },
- chat () {
- return this.$store.state.chat.channel
- },
- followRequestCount () {
- return this.$store.state.api.followRequests.length
- }
- }
+ computed: mapState({
+ currentUser: state => state.users.currentUser,
+ chat: state => state.chat.channel,
+ followRequestCount: state => state.api.followRequests.length,
+ privateMode: state => state.instance.private,
+ federating: state => state.instance.federating
+ })
}
export default NavPanel
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 28589bb14..034259d9c 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -4,22 +4,22 @@
-
- {{ $t("nav.timeline") }}
+ {{ $t("nav.timeline") }}
-
- {{ $t("nav.interactions") }}
+ {{ $t("nav.interactions") }}
-
- {{ $t("nav.dms") }}
+ {{ $t("nav.dms") }}
-
- {{ $t("nav.friend_requests") }}
+ {{ $t("nav.friend_requests") }}
- -
+
-
- {{ $t("nav.public_tl") }}
+ {{ $t("nav.public_tl") }}
- -
+
-
- {{ $t("nav.twkn") }}
+ {{ $t("nav.twkn") }}
-
- {{ $t("nav.about") }}
+ {{ $t("nav.about") }}
@@ -113,4 +113,8 @@
}
}
}
+
+.nav-panel .button-icon:before {
+ width: 1.1em;
+}
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index 6c4054fd7..a89c0cdc5 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -47,6 +47,11 @@ const Notifications = {
components: {
Notification
},
+ created () {
+ const { dispatch } = this.$store
+
+ dispatch('fetchAndUpdateNotifications')
+ },
watch: {
unseenCount (count) {
if (count > 0) {
diff --git a/src/components/public_and_external_timeline/public_and_external_timeline.js b/src/components/public_and_external_timeline/public_and_external_timeline.js
index f614c13b0..cbd4491bd 100644
--- a/src/components/public_and_external_timeline/public_and_external_timeline.js
+++ b/src/components/public_and_external_timeline/public_and_external_timeline.js
@@ -10,7 +10,7 @@ const PublicAndExternalTimeline = {
this.$store.dispatch('startFetchingTimeline', { timeline: 'publicAndExternal' })
},
destroyed () {
- this.$store.dispatch('stopFetching', 'publicAndExternal')
+ this.$store.dispatch('stopFetchingTimeline', 'publicAndExternal')
}
}
diff --git a/src/components/public_timeline/public_timeline.js b/src/components/public_timeline/public_timeline.js
index 8976a99c4..66c40d3ab 100644
--- a/src/components/public_timeline/public_timeline.js
+++ b/src/components/public_timeline/public_timeline.js
@@ -10,7 +10,7 @@ const PublicTimeline = {
this.$store.dispatch('startFetchingTimeline', { timeline: 'public' })
},
destroyed () {
- this.$store.dispatch('stopFetching', 'public')
+ this.$store.dispatch('stopFetchingTimeline', 'public')
}
}
diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue
index 5bb06a4fe..222b67a87 100644
--- a/src/components/registration/registration.vue
+++ b/src/components/registration/registration.vue
@@ -172,7 +172,7 @@
for="captcha-label"
>{{ $t('captcha') }}
-
+
({ ...acc, [key]: value }), {}),
- // Special cases (need to transform values)
+ // Special cases (need to transform values or perform actions first)
muteWordsString: {
get () { return this.$store.getters.mergedConfig.muteWords.join('\n') },
set (value) {
@@ -93,6 +93,22 @@ const settings = {
value: filter(value.split('\n'), (word) => trim(word).length > 0)
})
}
+ },
+ useStreamingApi: {
+ get () { return this.$store.getters.mergedConfig.useStreamingApi },
+ set (value) {
+ const promise = value
+ ? this.$store.dispatch('enableMastoSockets')
+ : this.$store.dispatch('disableMastoSockets')
+
+ promise.then(() => {
+ this.$store.dispatch('setOption', { name: 'useStreamingApi', value })
+ }).catch((e) => {
+ console.error('Failed starting MastoAPI Streaming socket', e)
+ this.$store.dispatch('disableMastoSockets')
+ this.$store.dispatch('setOption', { name: 'useStreamingApi', value: false })
+ })
+ }
}
},
// Updating nested properties
diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue
index 31329e825..cef492f35 100644
--- a/src/components/settings/settings.vue
+++ b/src/components/settings/settings.vue
@@ -73,6 +73,15 @@
+
+
+ {{ $t('settings.useStreamingApi') }}
+
+
+ {{ $t('settings.useStreamingApiWarning') }}
+
+
+
{{ $t('settings.autoload') }}
diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js
index 0188cf3e4..2534eb8f6 100644
--- a/src/components/side_drawer/side_drawer.js
+++ b/src/components/side_drawer/side_drawer.js
@@ -33,11 +33,20 @@ const SideDrawer = {
logo () {
return this.$store.state.instance.logo
},
+ hideSitename () {
+ return this.$store.state.instance.hideSitename
+ },
sitename () {
return this.$store.state.instance.name
},
followRequestCount () {
return this.$store.state.api.followRequests.length
+ },
+ privateMode () {
+ return this.$store.state.instance.private
+ },
+ federating () {
+ return this.$store.state.instance.federating
}
},
methods: {
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index 214b8e0c9..3fba90585 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -27,7 +27,7 @@
class="side-drawer-logo-wrapper"
>
- {{ sitename }}
+ {{ sitename }}
@@ -36,7 +36,7 @@
@click="toggleDrawer"
>
- {{ $t("login.login") }}
+ {{ $t("login.login") }}
-
- {{ $t("nav.dms") }}
+ {{ $t("nav.dms") }}
-
- {{ $t("nav.interactions") }}
+ {{ $t("nav.interactions") }}
@@ -62,7 +62,7 @@
@click="toggleDrawer"
>
- {{ $t("nav.timeline") }}
+ {{ $t("nav.timeline") }}
- {{ $t("nav.friend_requests") }}
+ {{ $t("nav.friend_requests") }}
-
+
- {{ $t("nav.public_tl") }}
+ {{ $t("nav.public_tl") }}
-
+
- {{ $t("nav.twkn") }}
+ {{ $t("nav.twkn") }}
- {{ $t("nav.chat") }}
+ {{ $t("nav.chat") }}
- -
+
-
- {{ $t("nav.search") }}
+ {{ $t("nav.search") }}
-
- {{ $t("nav.who_to_follow") }}
+ {{ $t("nav.who_to_follow") }}
-
- {{ $t("settings.settings") }}
+ {{ $t("settings.settings") }}
-
- {{ $t("nav.about") }}
+ {{ $t("nav.about") }}
-
- {{ $t("nav.administration") }}
+ {{ $t("nav.administration") }}
-
- {{ $t("login.logout") }}
+ {{ $t("login.logout") }}
@@ -215,6 +224,10 @@
box-shadow: var(--panelShadow);
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
+
+ .button-icon:before {
+ width: 1.1em;
+ }
}
.side-drawer-logo-wrapper {
diff --git a/src/components/sticker_picker/sticker_picker.vue b/src/components/sticker_picker/sticker_picker.vue
index 323855b96..3863908ab 100644
--- a/src/components/sticker_picker/sticker_picker.vue
+++ b/src/components/sticker_picker/sticker_picker.vue
@@ -36,23 +36,23 @@
.sticker-picker {
width: 100%;
- position: relative;
- .tab-switcher {
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- }
- .sticker-picker-content {
- .sticker {
- display: inline-block;
- width: 20%;
- height: 20%;
- img {
- width: 100%;
- &:hover {
- filter: drop-shadow(0 0 5px var(--link, $fallback--link));
+ .contents {
+ min-height: 250px;
+ .sticker-picker-content {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 0 4px;
+ .sticker {
+ display: flex;
+ flex: 1 1 auto;
+ margin: 4px;
+ width: 56px;
+ height: 56px;
+ img {
+ height: 100%;
+ &:hover {
+ filter: drop-shadow(0 0 5px var(--link, $fallback--link));
+ }
}
}
}
diff --git a/src/components/tag_timeline/tag_timeline.js b/src/components/tag_timeline/tag_timeline.js
index 458eb1c51..400c6a4b2 100644
--- a/src/components/tag_timeline/tag_timeline.js
+++ b/src/components/tag_timeline/tag_timeline.js
@@ -19,7 +19,7 @@ const TagTimeline = {
}
},
destroyed () {
- this.$store.dispatch('stopFetching', 'tag')
+ this.$store.dispatch('stopFetchingTimeline', 'tag')
}
}
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 27a9a55ed..9a53acd68 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -36,7 +36,12 @@ const Timeline = {
}
},
computed: {
- timelineError () { return this.$store.state.statuses.error },
+ timelineError () {
+ return this.$store.state.statuses.error
+ },
+ errorData () {
+ return this.$store.state.statuses.errorData
+ },
newStatusCount () {
return this.timeline.newStatusCount
},
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index 93f6f5706..9777bd0c3 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -11,15 +11,22 @@
>
{{ $t('timeline.error_fetching') }}
+
+ {{ errorData.statusText }}
+
@@ -67,12 +74,18 @@
{{ $t('timeline.no_more_statuses') }}
+
+
+