Add dummy Talk sidebar to public share auth page

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez 2020-01-13 18:36:26 +01:00
Родитель c1c3377799
Коммит 833d96a8f5
8 изменённых файлов: 425 добавлений и 1 удалений

Просмотреть файл

@ -1 +1,2 @@
@import 'icons.scss';
@import 'publicshareauth.scss'

26
css/publicshareauth.scss Normal file
Просмотреть файл

@ -0,0 +1,26 @@
/* Special layout to include the Talk sidebar */
/* The original style of the body is kept until the layout has been adjusted to
* include the Talk sidebar. If only "#body-login" was used, immediately after
* load and before the sidebar was injected the original elements would be using
* the style for the adjusted layout, which is not the proper one for them, and
* this would cause the elements to "jump" to their final position once the
* layout was adjusted. */
#body-login.talk-sidebar-enabled {
flex-direction: row;
align-items: stretch;
}
/* #body-login should be used to override the #content rules set in server. */
#body-login #content {
flex-grow: 1;
flex-direction: column;
align-items: center;
height: auto;
overflow-x: hidden;
/* Override "padding-top: 50px" set in server. */
padding-top: 0;
}

Просмотреть файл

@ -64,7 +64,7 @@ class TemplateLoader {
}
Util::addStyle('spreed', 'merged-share-auth');
Util::addScript('spreed', 'merged-share-auth');
Util::addScript('spreed', 'talk-public-share-auth-sidebar');
}
}

Просмотреть файл

@ -0,0 +1,120 @@
<!--
- @copyright Copyright (c) 2020 Daniel Calviño Sánchez <danxuliu@gmail.com>
-
- @license GNU AGPL version 3 or any later version
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->
<template>
<div>
<!-- "submit-wrapper" is used to mimic the login button and thus get
automatic colouring of the confirm icon by the Theming app. -->
<div id="submit-wrapper" class="request-password-wrapper">
<input id="request-password-button"
class="primary"
type="button"
:value="t('spreed', 'Request password')"
:disabled="isRequestInProgress"
@click="requestPassword">
<div class="icon" :class="iconClass" />
</div>
<p v-if="hasRequestFailed" class="warning error-message">
{{ t('spreed', 'Error requesting the password.') }}
</p>
</div>
</template>
<script>
import { getPublicShareAuthConversationToken } from './services/publicShareAuthService'
export default {
name: 'PublicShareAuthRequestPasswordButton',
props: {
shareToken: {
type: String,
required: true,
},
},
data() {
return {
isRequestLoading: false,
hasRequestFailed: false,
}
},
computed: {
iconClass() {
return {
'icon-confirm-white': !this.isRequestInProgress,
'icon-loading-small-dark': this.isRequestInProgress,
}
},
token() {
return this.$store.getters.getToken()
},
isRequestInProgress() {
return this.isRequestLoading || !!this.token
},
},
methods: {
async requestPassword() {
this.hasRequestFailed = false
this.isRequestLoading = true
try {
const token = await getPublicShareAuthConversationToken(this.shareToken)
this.$store.dispatch('updateToken', token)
} catch (exception) {
this.hasRequestFailed = true
}
this.isRequestLoading = false
},
},
}
</script>
<style lang="scss" scoped>
/* Request password button has the appearance of the log in button */
.request-password-wrapper {
position: relative;
width: 280px;
margin: 16px auto;
}
.request-password-wrapper .icon {
position: absolute;
top: 23px;
right: 23px;
pointer-events: none;
}
input#request-password-button {
width: 269px;
padding: 10px 10px;
}
input#request-password-button:disabled ~ .icon {
opacity: 0.5;
}
</style>

Просмотреть файл

@ -0,0 +1,142 @@
<!--
- @copyright Copyright (c) 2020, Daniel Calviño Sánchez <danxuliu@gmail.com>
-
- @license GNU AGPL version 3 or any later version
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->
<template>
<aside v-if="isOpen" id="talk-sidebar">
<div v-if="!token" class="emptycontent">
<div class="icon icon-talk" />
<h2>{{ t('spreed', 'This conversation has ended') }}</h2>
</div>
<div v-else class="emptycontent">
<div class="icon icon-talk" />
<h2>Password requested</h2>
</div>
</aside>
</template>
<script>
import { EventBus } from './services/EventBus'
import { fetchConversation } from './services/conversationsService'
import { joinConversation } from './services/participantsService'
import { getSignaling } from './utils/webrtc/index'
export default {
name: 'PublicShareAuthSidebar',
data() {
return {
fetchCurrentConversationIntervalId: null,
isWaitingToClose: false,
}
},
computed: {
token() {
return this.$store.getters.getToken()
},
conversation() {
return this.$store.getters.conversations[this.token]
},
isOpen() {
return this.conversation || this.isWaitingToClose
},
},
watch: {
token(token) {
if (token) {
this.joinConversation()
}
},
conversation(conversation) {
if (!conversation) {
this.isWaitingToClose = true
window.setTimeout(() => { this.isWaitingToClose = false }, 5000)
}
},
},
methods: {
async joinConversation() {
await joinConversation(this.token)
// No need to wait for it, but fetching the conversation needs to be
// done once the user has joined the conversation (otherwise only
// limited data would be received if the user was not a participant
// of the conversation yet).
this.fetchCurrentConversation()
// FIXME The participant will not be updated with the server data
// when the conversation is got again (as "addParticipantOnce" is
// used), although that should not be a problem given that only the
// "inCall" flag (which is locally updated when joining and leaving
// a call) is currently used.
const signaling = await getSignaling()
if (signaling.url) {
EventBus.$on('shouldRefreshConversations', this.fetchCurrentConversation)
} else {
// The "shouldRefreshConversations" event is triggered only when
// the external signaling server is used; when the internal
// signaling server is used periodic polling has to be used
// instead.
this.fetchCurrentConversationIntervalId = window.setInterval(this.fetchCurrentConversation, 30000)
}
},
async fetchCurrentConversation() {
if (!this.token) {
return
}
try {
const response = await fetchConversation(this.token)
this.$store.dispatch('addConversation', response.data.ocs.data)
} catch (exception) {
window.clearInterval(this.fetchCurrentConversationIntervalId)
this.$store.dispatch('deleteConversationByToken', this.token)
this.$store.dispatch('updateToken', '')
}
},
},
}
</script>
<style lang="scss" scoped>
/* Properties based on the app-sidebar */
#talk-sidebar {
position: relative;
flex-shrink: 0;
width: 27vw;
min-width: 300px;
max-width: 500px;
background: var(--color-main-background);
border-left: 1px solid var(--color-border);
overflow-x: hidden;
overflow-y: auto;
z-index: 1500;
}
</style>

Просмотреть файл

@ -0,0 +1,93 @@
/**
* @copyright Copyright (c) 2020 Daniel Calviño Sánchez <danxuliu@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import Vue from 'vue'
import PublicShareAuthRequestPasswordButton from './PublicShareAuthRequestPasswordButton'
import PublicShareAuthSidebar from './PublicShareAuthSidebar'
// Store
import Vuex from 'vuex'
import store from './store'
// Utils
import { generateFilePath } from '@nextcloud/router'
import { getRequestToken } from '@nextcloud/auth'
// Directives
import { translate, translatePlural } from '@nextcloud/l10n'
// CSP config for webpack dynamic chunk loading
// eslint-disable-next-line
__webpack_nonce__ = btoa(getRequestToken())
// Correct the root of the app for chunk loading
// OC.linkTo matches the apps folders
// OC.generateUrl ensure the index.php (or not)
// We do not want the index.php since we're loading files
// eslint-disable-next-line
__webpack_public_path__ = generateFilePath('spreed', '', 'js/')
Vue.prototype.t = translate
Vue.prototype.n = translatePlural
Vue.prototype.OC = OC
Vue.prototype.OCA = OCA
Vue.use(Vuex)
function adjustLayout() {
const contentElement = document.createElement('div')
contentElement.setAttribute('id', 'content')
document.querySelector('body').append(contentElement)
contentElement.append(document.querySelector('.wrapper'))
contentElement.append(document.querySelector('footer'))
const requestPasswordElement = document.createElement('div')
requestPasswordElement.setAttribute('id', 'request-password')
document.querySelector('main').append(requestPasswordElement)
const talkSidebarElement = document.createElement('div')
talkSidebarElement.setAttribute('id', 'talk-sidebar')
document.querySelector('body').append(talkSidebarElement)
document.querySelector('body').classList.add('talk-sidebar-enabled')
}
adjustLayout()
function getShareToken() {
const shareTokenElement = document.getElementById('sharingToken')
return shareTokenElement.value
}
const requestPasswordVm = new Vue({
store,
propsData: {
shareToken: getShareToken(),
},
...PublicShareAuthRequestPasswordButton,
})
requestPasswordVm.$mount('#request-password')
const talkSidebarVm = new Vue({
store,
...PublicShareAuthSidebar,
})
talkSidebarVm.$mount(document.querySelector('#talk-sidebar'))

Просмотреть файл

@ -0,0 +1,41 @@
/**
*
* @copyright Copyright (c) 2020, Daniel Calviño Sánchez <danxuliu@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
/**
* Gets the public share auth conversation token for a given share token.
*
* @param {String} shareToken the token of the share
* @returns {String} the conversation token
* @throws {Exception} if the conversation token could not be got
*/
const getPublicShareAuthConversationToken = async function(shareToken) {
const response = await axios.post(generateOcsUrl('apps/spreed/api/v1', 2) + `publicshareauth`, {
shareToken,
})
return response.data.ocs.data.token
}
export {
getPublicShareAuthConversationToken,
}

Просмотреть файл

@ -14,6 +14,7 @@ module.exports = {
'talk': path.join(__dirname, 'src', 'main.js'),
'talk-files-sidebar': path.join(__dirname, 'src', 'mainFilesSidebar.js'),
'talk-files-sidebar-loader': path.join(__dirname, 'src', 'mainFilesSidebarLoader.js'),
'talk-public-share-auth-sidebar': path.join(__dirname, 'src', 'mainPublicShareAuthSidebar.js'),
'flow': path.join(__dirname, 'src', 'flow.js')
},
output: {