Commit b6194775 authored by HJ's avatar HJ 🐼

Merge remote-tracking branch 'upstream/develop' into streaming

* upstream/develop: (51 commits)
  toggle_activation api is also deprecated
  use vuex action
  refactor toggleActivationStatus
  replace setActivationStatus api with new one
  use flex for stickers
  i18n/update-ja_easy
  Use a centralized fallback for missing values and use instance.federating instead of instance.federation.enabled
  Add fallback in case BE does not report federating status in nodeinfo
  The value we are looking for is federationPolicy.enabled, not federationPolicy.federating
  Logic should be to hide TWKN if not federating OR if instance is not public
  Finally trust eslint
  More lint
  More lint
  Lint
  mfa: removed unused code
  increase icon width a little bit in the nav panel
  add icons to nav panel
  Revert "Merge branch 'revert-96cab6d8' into 'develop'"
  Support "native" captcha
  Revert "Merge branch 'oauth-extra-scopes' into 'develop'"
  ...
parents 585702b1 f7029a27
Pipeline #21464 passed with stages
in 4 minutes and 3 seconds
{
"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
}
......@@ -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.
......@@ -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 () {
......
......@@ -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);
}
......@@ -31,6 +31,7 @@
</div>
<div class="item">
<router-link
v-if="!hideSitename"
class="site-name"
:to="{ name: 'root' }"
active-class="home"
......@@ -40,6 +41,7 @@
</div>
<div class="item right">
<search-bar
v-if="currentUser || !privateMode"
class="nav-icon mobile-hidden"
@toggled="onSearchBarToggled"
@click.stop.native
......
......@@ -108,6 +108,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
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 })
......
......@@ -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 {
......
......@@ -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
......
......@@ -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
......
......@@ -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: {
......
......@@ -17,6 +17,7 @@
<i class="button-icon icon-menu" />
</a>
<router-link
v-if="!hideSitename"
class="site-name"
:to="{ name: 'root' }"
active-class="home"
......
......@@ -71,12 +71,7 @@ const ModerationTools = {
}
},
toggleActivationStatus () {
const store = this.$store
const status = !!this.user.deactivated
store.state.api.backendInteractor.setActivationStatus({ user: 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
......
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
......@@ -4,22 +4,22 @@
<ul>
<li v-if="currentUser">
<router-link :to="{ name: 'friends' }">
{{ $t("nav.timeline") }}
<i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
</router-link>
</li>
<li v-if="currentUser">
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
{{ $t("nav.interactions") }}
<i class="button-icon icon-bell-alt" /> {{ $t("nav.interactions") }}
</router-link>
</li>
<li v-if="currentUser">
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
{{ $t("nav.dms") }}
<i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
</router-link>
</li>
<li v-if="currentUser && currentUser.locked">
<router-link :to="{ name: 'friend-requests' }">
{{ $t("nav.friend_requests") }}
<i class="button-icon icon-user-plus" /> {{ $t("nav.friend_requests") }}
<span
v-if="followRequestCount > 0"
class="badge follow-request-count"
......@@ -28,19 +28,19 @@
</span>
</router-link>
</li>
<li>
<li v-if="currentUser || !privateMode">
<router-link :to="{ name: 'public-timeline' }">
{{ $t("nav.public_tl") }}
<i class="button-icon icon-users" /> {{ $t("nav.public_tl") }}
</router-link>
</li>
<li>
<li v-if="federating && !privateMode">
<router-link :to="{ name: 'public-external-timeline' }">
{{ $t("nav.twkn") }}
<i class="button-icon icon-globe" /> {{ $t("nav.twkn") }}
</router-link>
</li>
<li>
<router-link :to="{ name: 'about' }">
{{ $t("nav.about") }}
<i class="button-icon icon-info-circled" /> {{ $t("nav.about") }}
</router-link>
</li>
</ul>
......@@ -113,4 +113,8 @@
}
}
}
.nav-panel .button-icon:before {
width: 1.1em;
}
</style>
......@@ -172,7 +172,7 @@
for="captcha-label"
>{{ $t('captcha') }}</label>
<template v-if="captcha.type == 'kocaptcha'">
<template v-if="['kocaptcha', 'native'].includes(captcha.type)">
<img
:src="captcha.url"
@click="setCaptcha"
......
......@@ -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: {
......
......@@ -27,7 +27,7 @@
class="side-drawer-logo-wrapper"
>
<img :src="logo">
<span>{{ sitename }}</span>
<span v-if="!hideSitename">{{ sitename }}</span>
</div>
</div>
<ul>
......@@ -36,7 +36,7 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'login' }">
{{ $t("login.login") }}
<i class="button-icon icon-login" /> {{ $t("login.login") }}
</router-link>
</li>
<li
......@@ -44,7 +44,7 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
{{ $t("nav.dms") }}
<i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
</router-link>
</li>
<li
......@@ -52,7 +52,7 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
{{ $t("nav.interactions") }}
<i class="button-icon icon-bell-alt" /> {{ $t("nav.interactions") }}
</router-link>
</li>
</ul>
......@@ -62,7 +62,7 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'friends' }">
{{ $t("nav.timeline") }}
<i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
</router-link>
</li>
<li
......@@ -70,7 +70,7 @@
@click="toggleDrawer"
>
<router-link to="/friend-requests">
{{ $t("nav.friend_requests") }}
<i class="button-icon icon-user-plus" /> {{ $t("nav.friend_requests") }}
<span
v-if="followRequestCount > 0"
class="badge follow-request-count"
......@@ -79,14 +79,20 @@
</span>
</router-link>
</li>
<li @click="toggleDrawer">
<li
v-if="currentUser || !privateMode"
@click="toggleDrawer"
>
<router-link to="/main/public">
{{ $t("nav.public_tl") }}
<i class="button-icon icon-users" /> {{ $t("nav.public_tl") }}
</router-link>
</li>
<li @click="toggleDrawer">
<li
v-if="federating && !privateMode"
@click="toggleDrawer"
>
<router-link to="/main/all">
{{ $t("nav.twkn") }}
<i class="button-icon icon-globe" /> {{ $t("nav.twkn") }}
</router-link>
</li>
<li
......@@ -94,14 +100,17 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'chat' }">
{{ $t("nav.chat") }}
<i class="button-icon icon-chat" /> {{ $t("nav.chat") }}
</router-link>
</li>
</ul>
<ul>
<li @click="toggleDrawer">
<li
v-if="currentUser || !privateMode"
@click="toggleDrawer"
>
<router-link :to="{ name: 'search' }">
{{ $t("nav.search") }}
<i class="button-icon icon-search" /> {{ $t("nav.search") }}
</router-link>
</li>
<li
......@@ -109,17 +118,17 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'who-to-follow' }">
{{ $t("nav.who_to_follow") }}
<i class="button-icon icon-user-plus" /> {{ $t("nav.who_to_follow") }}
</router-link>
</li>
<li @click="toggleDrawer">
<router-link :to="{ name: 'settings' }">
{{ $t("settings.settings") }}
<i class="button-icon icon-cog" /> {{ $t("settings.settings") }}
</router-link>
</li>
<li @click="toggleDrawer">
<router-link :to="{ name: 'about'}">
{{ $t("nav.about") }}
<i class="button-icon icon-info-circled" /> {{ $t("nav.about") }}
</router-link>
</li>
<li
......@@ -130,7 +139,7 @@
href="/pleroma/admin/#/login-pleroma"
target="_blank"
>
{{ $t("nav.administration") }}
<i class="button-icon icon-gauge" /> {{ $t("nav.administration") }}
</a>
</li>
<li
......@@ -141,7 +150,7 @@
href="#"
@click="doLogout"
>
{{ $t("login.logout") }}
<i class="button-icon icon-logout" /> {{ $t("login.logout") }}
</a>
</li>
</ul>
......@@ -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 {
......
......@@ -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));
}
}
}
}
......
......@@ -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
},
......
......@@ -11,15 +11,22 @@
>
{{ $t('timeline.error_fetching') }}
</div>
<div
v-else-if="errorData"
class="loadmore-error alert error"
@click.prevent
>
{{ errorData.statusText }}
</div>
<button
v-if="timeline.newStatusCount > 0 && !timelineError"
v-if="timeline.newStatusCount > 0 && !timelineError && !errorData"
class="loadmore-button"
@click.prevent="showNewStatuses"
>
{{ $t('timeline.show_new') }}{{ newStatusCountStr }}
</button>
<div
v-if="!timeline.newStatusCount > 0 && !timelineError"
v-if="!timeline.newStatusCount > 0 && !timelineError && !errorData"
class="loadmore-text faint"
@click.prevent
>
......@@ -67,12 +74,18 @@
{{ $t('timeline.no_more_statuses') }}
</div>
<a
v-else-if="!timeline.loading"
v-else-if="!timeline.loading && !errorData"
href="#"
@click.prevent="fetchOlderStatuses()"
>
<div class="new-status-notification text-center panel-footer">{{ $t('timeline.load_older') }}</div>
</a>
<a
v-else-if="errorData"
href="#"
>
<div class="new-status-notification text-center panel-footer">{{ errorData.error }}</div>
</a>
<div
v-else
class="new-status-notification text-center panel-footer"
......@@ -93,17 +106,4 @@
opacity: 1;
}
}
.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);
}
</style>
......@@ -65,7 +65,7 @@ const withLoadMore = ({
}
}
},
render (createElement) {
render (h) {
const props = {
props: {
...this.$props,
......@@ -74,7 +74,7 @@ const withLoadMore = ({
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value))
return (
<div class="with-load-more">
<WrappedComponent {...props}>
......
......@@ -49,7 +49,7 @@ const withSubscription = ({
}
}
},
render (createElement) {
render (h) {
if (!this.error && !this.loading) {
const props = {
props: {
...