...
 
Commits (29)
{
"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
}
......@@ -8,7 +8,7 @@ import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_pan
import ChatPanel from './components/chat_panel/chat_panel.vue'
import MediaModal from './components/media_modal/media_modal.vue'
import SideDrawer from './components/side_drawer/side_drawer.vue'
import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'
import FloatingPostButton from './components/floating_post_button/floating_post_button.vue'
import MobileNav from './components/mobile_nav/mobile_nav.vue'
import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'
import PostStatusModal from './components/post_status_modal/post_status_modal.vue'
......@@ -27,7 +27,7 @@ export default {
ChatPanel,
MediaModal,
SideDrawer,
MobilePostStatusButton,
FloatingPostButton,
MobileNav,
UserReportingModal,
PostStatusModal
......
......@@ -118,7 +118,7 @@
:floating="true"
class="floating-chat mobile-hidden"
/>
<MobilePostStatusButton />
<FloatingPostButton v-if="currentUser && isMobileLayout" />
<UserReportingModal />
<PostStatusModal />
<portal-target name="modal" />
......
import { debounce } from 'lodash'
import 'javascript-detect-element-resize'
const MobilePostStatusButton = {
// NOTE: We use the hard-coded boundingClientRect obj of the button at the moment
// since the button moves vertically and it makes difficult to determine the boundingClientRect
const buttonRect = {
bottom: -21, // Can get the actual bottom value by adding window.innerHeight
right: -21, // Can get the actual right value by adding window.innerWidth
height: 70,
width: 70
}
const FloatingPostButton = {
data () {
return {
hidden: false,
scrollingDown: false,
inputActive: false,
oldScrollPos: 0,
amountScrolled: 0
hoverInlineReply: false,
oldScrollPos: 0
}
},
created () {
if (this.autohideFloatingPostButton) {
this.activateFloatingPostButtonAutohide()
}
window.addEventListener('resize', this.handleOSK)
},
destroyed () {
if (this.autohideFloatingPostButton) {
this.deactivateFloatingPostButtonAutohide()
}
window.removeEventListener('resize', this.handleOSK)
},
computed: {
isLoggedIn () {
return !!this.$store.state.users.currentUser
},
isHidden () {
return this.autohideFloatingPostButton && (this.hidden || this.inputActive)
return this.autohideFloatingPostButton && (this.scrollingDown || this.inputActive || this.hoverInlineReply)
},
autohideFloatingPostButton () {
return !!this.$store.getters.mergedConfig.autohideFloatingPostButton
},
postStatusForms () {
return this.$store.state.interface.postStatusForms
}
},
watch: {
......@@ -46,10 +53,14 @@ const MobilePostStatusButton = {
activateFloatingPostButtonAutohide () {
window.addEventListener('scroll', this.handleScrollStart)
window.addEventListener('scroll', this.handleScrollEnd)
window.addEventListener('resize', this.handleResize)
window.addResizeListener(document.body, this.checkHoverInlineReply)
},
deactivateFloatingPostButtonAutohide () {
window.removeEventListener('scroll', this.handleScrollStart)
window.removeEventListener('scroll', this.handleScrollEnd)
window.removeEventListener('resize', this.handleResize)
window.removeResizeListener(document.body, this.checkHoverInlineReply)
},
openPostForm () {
this.$store.dispatch('openPostStatusModal')
......@@ -74,20 +85,36 @@ const MobilePostStatusButton = {
this.inputActive = false
}
},
checkHoverInlineReply () {
this.hoverInlineReply = this.postStatusForms.some(form => {
const rect = form.getBoundingClientRect()
return rect.bottom > window.innerHeight + buttonRect.bottom - buttonRect.height &&
rect.right > window.innerWidth + buttonRect.right - buttonRect.width &&
rect.bottom - rect.height < window.innerHeight + buttonRect.bottom &&
rect.right - rect.width < window.innerWidth + buttonRect.right
})
},
handleScrollStart: debounce(function () {
if (window.scrollY > this.oldScrollPos) {
this.hidden = true
this.scrollingDown = true
} else {
this.hidden = false
this.scrollingDown = false
}
this.oldScrollPos = window.scrollY
}, 100, { leading: true, trailing: false }),
handleScrollEnd: debounce(function () {
this.hidden = false
this.scrollingDown = false
this.oldScrollPos = window.scrollY
}, 100, { leading: false, trailing: true })
this.checkHoverInlineReply()
}, 100, { leading: false, trailing: true }),
handleResize: debounce(function () {
this.handleOSK()
this.checkHoverInlineReply()
}, 100)
}
}
export default MobilePostStatusButton
export default FloatingPostButton
<template>
<div v-if="isLoggedIn">
<button
class="new-status-button"
class="floating-post-button"
:class="{ 'hidden': isHidden }"
@click="openPostForm"
>
<i class="icon-edit" />
</button>
</div>
</template>
<script src="./mobile_post_status_button.js"></script>
<script src="./floating_post_button.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.new-status-button {
.floating-post-button {
width: 5em;
height: 5em;
border-radius: 100%;
......@@ -46,10 +44,4 @@
}
}
@media all and (min-width: 801px) {
.new-status-button {
display: none;
}
}
</style>
......@@ -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
......
......@@ -48,6 +48,11 @@ const PostStatusForm = {
if (this.replyTo) {
this.$refs.textarea.focus()
}
this.$store.dispatch('addNewPostStatusForm', this)
},
beforeDestroy () {
this.$store.dispatch('removePostStatusForm', this)
},
data () {
const preset = this.$route.query.message
......@@ -374,6 +379,9 @@ const PostStatusForm = {
},
dismissScopeNotice () {
this.$store.dispatch('setOption', { name: 'hideScopeNotice', value: true })
},
getBoundingClientRect () {
return this.$refs.root.getBoundingClientRect()
}
}
}
......
......@@ -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: {
......@@ -59,7 +59,7 @@ const withSubscription = ({
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-subscription">
<WrappedComponent {...props}>
......
......@@ -7,7 +7,6 @@ const RECOVERY_STRATEGY = 'recovery'
// initial state
const state = {
app: null,
settings: {},
strategy: PASSWORD_STRATEGY,
initStrategy: PASSWORD_STRATEGY // default strategy from config
......@@ -16,14 +15,10 @@ const state = {
const resetState = (state) => {
state.strategy = state.initStrategy
state.settings = {}
state.app = null
}
// getters
const getters = {
app: (state, getters) => {
return state.app
},
settings: (state, getters) => {
return state.settings
},
......@@ -55,9 +50,8 @@ const mutations = {
requireToken (state) {
state.strategy = TOKEN_STRATEGY
},
requireMFA (state, { app, settings }) {
requireMFA (state, { settings }) {
state.settings = settings
state.app = app
state.strategy = TOTP_STRATEGY // default strategy of MFA
},
requireRecovery (state) {
......
......@@ -12,7 +12,8 @@ const defaultState = {
window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')
)
},
mobileLayout: false
mobileLayout: false,
postStatusForms: []
}
const interfaceMod = {
......@@ -35,6 +36,12 @@ const interfaceMod = {
},
setMobileLayout (state, value) {
state.mobileLayout = value
},
addNewPostStatusForm (state, instance) {
state.postStatusForms.push(instance)
},
removePostStatusForm (state, instance) {
del(state.postStatusForms, state.postStatusForms.indexOf(instance))
}
},
actions: {
......@@ -49,6 +56,12 @@ const interfaceMod = {
},
setMobileLayout ({ commit }, value) {
commit('setMobileLayout', value)
},
addNewPostStatusForm ({ commit }, instance) {
commit('addNewPostStatusForm', instance)
},
removePostStatusForm ({ commit }, instance) {
commit('removePostStatusForm', instance)
}
}
}
......
const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
const verifyOTPCode = ({ clientId, clientSecret, instance, mfaToken, code }) => {
const url = `${instance}/oauth/mfa/challenge`
const form = new window.FormData()
form.append('client_id', app.client_id)
form.append('client_secret', app.client_secret)
form.append('client_id', clientId)
form.append('client_secret', clientSecret)
form.append('mfa_token', mfaToken)
form.append('code', code)
form.append('challenge_type', 'totp')
......@@ -14,12 +14,12 @@ const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
}).then((data) => data.json())
}
const verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {
const verifyRecoveryCode = ({ clientId, clientSecret, instance, mfaToken, code }) => {
const url = `${instance}/oauth/mfa/challenge`
const form = new window.FormData()
form.append('client_id', app.client_id)
form.append('client_secret', app.client_secret)
form.append('client_id', clientId)
form.append('client_secret', clientSecret)
form.append('mfa_token', mfaToken)
form.append('code', code)
form.append('challenge_type', 'recovery')
......
require('babel-register')
require('@babel/register')
var config = require('../../config')
// http://nightwatchjs.org/guide#settings-file
......
This diff is collapsed.