Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move conversation menu options to a dialog #4576

Merged
merged 2 commits into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<PreventUnload :when="warnLeaving" />
<UploadEditor />
<SettingsDialog />
<ConversationSettingsDialog />
</Content>
</template>

Expand Down Expand Up @@ -65,6 +66,7 @@ import talkHashCheck from './mixins/talkHashCheck'
import { generateUrl } from '@nextcloud/router'
import UploadEditor from './components/UploadEditor'
import SettingsDialog from './components/SettingsDialog/SettingsDialog'
import ConversationSettingsDialog from './components/ConversationSettings/ConversationSettingsDialog'
import '@nextcloud/dialogs/styles/toast.scss'

export default {
Expand All @@ -77,6 +79,7 @@ export default {
RightSidebar,
UploadEditor,
SettingsDialog,
ConversationSettingsDialog,
},

mixins: [
Expand Down
124 changes: 124 additions & 0 deletions src/components/ConversationSettings/ConversationSettingsDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<!--
- @copyright Copyright (c) 2020 Vincent Petry <vincent@nextcloud.com>
-
- @author Vincent Petry <vincent@nextcloud.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>
<AppSettingsDialog
role="dialog"
:aria-label="t('spreed', 'Conversation settings')"
:open.sync="showSettings"
:show-navigation="false">
<AppSettingsSection
:title="t('spreed', 'Guests access')"
class="app-settings-section">
<LinkShareSettings ref="linkShareSettings" />
</AppSettingsSection>
<AppSettingsSection
v-if="canFullModerate"
:title="t('spreed', 'Meeting settings')"
class="app-settings-section">
<ModerationSettings />
</AppSettingsSection>
</AppSettingsDialog>
</template>

<script>
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { PARTICIPANT } from '../../constants'
import AppSettingsDialog from '@nextcloud/vue/dist/Components/AppSettingsDialog'
import AppSettingsSection from '@nextcloud/vue/dist/Components/AppSettingsSection'
import LinkShareSettings from './LinkShareSettings'
import ModerationSettings from './ModerationSettings'

export default {
name: 'ConversationSettingsDialog',

components: {
AppSettingsDialog,
AppSettingsSection,
LinkShareSettings,
ModerationSettings,
},

data() {
return {
showSettings: false,
}
},

computed: {
token() {
return this.$store.getters.getToken()
},
conversation() {
return this.$store.getters.conversation(this.token) || this.$store.getters.dummyConversation
},
participantType() {
return this.conversation.participantType
},
canFullModerate() {
return this.participantType === PARTICIPANT.TYPE.OWNER || this.participantType === PARTICIPANT.TYPE.MODERATOR
},
},

mounted() {
subscribe('show-conversation-settings', this.handleShowSettings)
},

methods: {
handleShowSettings() {
this.showSettings = true
this.$nextTick(() => {
this.$refs.linkShareSettings.focus()
})
},

beforeDestroy() {
unsubscribe('show-conversation-settings', this.handleShowSettings)
},
},
}
</script>
<style lang="scss" scoped>
::v-deep button.icon {
height: 32px;
width: 32px;
display: inline-block;
margin-left: 5px;
vertical-align: middle;
}

::v-deep .app-settings__content {
width: 450px;
}

::v-deep .app-settings-section__hint {
color: var(--color-text-lighter);
padding: 8px 0;
}

::v-deep .app-settings-subsection {
margin-top: 25px;

&:first-child {
margin-top: 0px;
}
}
</style>
229 changes: 229 additions & 0 deletions src/components/ConversationSettings/LinkShareSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
<!--
- @copyright Copyright (c) 2020 Vincent Petry <vincent@nextcloud.com>
-
- @author Vincent Petry <vincent@nextcloud.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>
<div class="app-settings-subsection">
<div id="link_share_settings_hint" class="app-settings-section__hint">
{{ t('spreed', 'Allow guests to use a public link to join this conversation.') }}
</div>
<div>
<input id="link_share_settings_toggle_guests"
ref="toggleGuests"
aria-describedby="link_share_settings_hint"
type="checkbox"
class="checkbox"
name="link_share_settings_toggle_guests"
:checked="isSharedPublicly"
:disabled="isSaving"
@change="toggleGuests">
<label for="link_share_settings_toggle_guests">{{ t('spreed', 'Allow guests') }}</label>
</div>
</div>
<div class="app-settings-subsection">
<div id="link_share_settings_password_hint" class="app-settings-section__hint">
{{ t('spreed', 'Set a password to restrict who can use the public link.') }}
</div>
<div v-show="isSharedPublicly">
<input id="link_share_settings_toggle_password"
ref="togglePassword"
aria-describedby="link_share_settings_password_hint"
type="checkbox"
class="checkbox"
:checked="conversation.hasPassword"
name="link_share_settings_toggle_password"
:disabled="isSaving"
@change="togglePassword">
<label for="link_share_settings_toggle_password">{{ t('spreed', 'Password protection') }}</label>
</div>
</div>
<div class="app-settings-subsection">
<div v-show="showPasswordField">
<form
:disabled="isSaving"
@submit.prevent="handleSetNewPassword">
<span class="icon-password" />
<input
id="link_share_settings_link_password"
ref="passwordField"
v-model="password"
aria-describedby="link_share_settings_password_hint"
type="password"
class="checkbox"
autocomplete="new-password"
name="link_share_settings_link_password"
:placeholder="t('spreed', 'Enter a password')"
:disabled="isSaving">
<button
id="link_share_settings_link_password_submit"
:aria-label="t('spreed', 'Save password')"
:disabled="isSaving"
type="submit"
class="icon icon-confirm-fade" />
</form>
</div>
</div>
<div class="app-settings-subsection">
<button
ref="copyLinkButton"
@click.prevent="handleCopyLink">
<span class="icon icon-clippy" />{{ t('spreed', 'Copy public link') }}
</button>
</div>
</div>
</template>

<script>
import { showError, showSuccess } from '@nextcloud/dialogs'
import { CONVERSATION } from '../../constants'
import {
setConversationPassword,
} from '../../services/conversationsService'
import { generateUrl } from '@nextcloud/router'

export default {
name: 'LinkShareSettings',

data() {
return {
// The conversation's password
password: '',
// Switch for the password-editing operation
showPasswordField: false,
isSaving: false,
}
},

computed: {
isSharedPublicly() {
return this.conversation.type === CONVERSATION.TYPE.PUBLIC
},

token() {
return this.$store.getters.getToken()
},

conversation() {
return this.$store.getters.conversation(this.token) || this.$store.getters.dummyConversation
},

linkToConversation() {
return window.location.protocol + '//' + window.location.host + generateUrl('/call/' + this.token)
},
},

methods: {
focus() {
this.$nextTick(() => {
this.$refs.toggleGuests.focus()
})
},

async setConversationPassword(newPassword) {
this.isSaving = true
try {
await setConversationPassword(this.token, newPassword)
if (newPassword !== '') {
showSuccess(t('spreed', 'Conversation password has been saved'))
} else {
showSuccess(t('spreed', 'Conversation password has been removed'))
}
} catch (e) {
console.error('Error saving conversation password', e)
showError(t('spreed', 'Error occurred while saving conversation password'))
}
this.isSaving = false
},

async toggleGuests() {
const enabled = this.conversation.type !== CONVERSATION.TYPE.PUBLIC
this.isSaving = true
try {
await this.$store.dispatch('toggleGuests', {
token: this.token,
allowGuests: enabled,
})

if (enabled) {
showSuccess(t('spreed', 'You allowed guests'))
} else {
showSuccess(t('spreed', 'You disallowed guests'))
}
} catch (e) {
if (enabled) {
showError(t('spreed', 'Error occurred while allowing guests'))
} else {
showError(t('spreed', 'Error occurred while disallowing guests'))
}
console.error('Error toggling guest mode', e)
}
this.isSaving = false
},

async togglePassword() {
if (this.$refs.togglePassword.checked) {
this.showPasswordField = true
this.$refs.passwordField.focus()
await this.handlePasswordEnable()
} else {
this.showPasswordField = false
await this.handlePasswordDisable()
}
},

async handlePasswordDisable() {
// disable the password protection for the current conversation
if (this.conversation.hasPassword) {
await this.setConversationPassword('')
}
this.password = ''
this.showPasswordField = false
},

async handlePasswordEnable() {
this.showPasswordField = true
},

async handleSetNewPassword() {
await this.setConversationPassword(this.password)
this.password = ''
this.showPasswordField = false
},

async handleCopyLink() {
try {
await this.$copyText(this.linkToConversation)
showSuccess(t('spreed', 'Conversation link copied to clipboard.'))
} catch (error) {
showError(t('spreed', 'The link could not be copied.'))
}
// workaround for https://github.com/Inndy/vue-clipboard2/issues/105
this.$refs.copyLinkButton.focus()
},
},
}
</script>

<style lang="scss" scoped>
.icon-clippy {
margin-right: 10px;
}
</style>
Loading