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

feat:select existing talk room for conversations on calendar events #6595

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
192 changes: 192 additions & 0 deletions src/components/Editor/AddTalkModal.vue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Would be nice to have an empty content loading state while the conversations are being fetched.

Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<template>
<NcModal size="normal" class="modal" :name="t('calendar', 'Select a Talk Room')">
<div class="modal-content">
<h2>{{ t('calendar', 'Select a talk room from the list') }}</h2>
<div class="talk-room-list">
<ul>
<li v-for="conversation in talkConversations"
:key="conversation.id"
class="talk-room-list__item">
<span>{{ conversation.displayName }}</span>
<NcButton @click="selectConversation(conversation)">
{{ t('calendar', 'Select') }}
</NcButton>
</li>
</ul>

Check warning on line 15 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L11-L15

Added lines #L11 - L15 were not covered by tests
</div>
<div class="sticky-footer">
<NcButton :disabled="creatingTalkRoom" @click="createTalkRoom">
<template #icon>
<IconAdd :size="20" />
</template>
{{ t('calendar', 'Create a new conversation') }}
</NcButton>
</div>
</div>
</NcModal>
</template>

<script>
import {
NcButton,
NcModal,
} from '@nextcloud/vue'
import axios from '@nextcloud/axios'

Check warning on line 34 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L33-L34

Added lines #L33 - L34 were not covered by tests
import { createTalkRoom } from '../../services/talkService.js'
import { showError, showSuccess } from '@nextcloud/dialogs'
import { generateOcsUrl } from '@nextcloud/router'
import IconAdd from 'vue-material-design-icons/Plus.vue'
import useCalendarObjectInstanceStore from '../../store/calendarObjects.js'
import { mapStores } from 'pinia'

// Ref https://github.com/nextcloud/spreed/blob/main/docs/constants.md
const CONVERSATION_TYPE_GROUP = 2
const CONVERSATION_TYPE_PUBLIC = 3

Check warning on line 44 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L43-L44

Added lines #L43 - L44 were not covered by tests
const CONVERSATION_OBJECT_TYPE_VIDEO_VERIFICATION = 'share:password'
const PARTICIPANT_TYPE_OWNER = 1
const PARTICIPANT_TYPE_MODERATOR = 2

export default {

Check warning on line 49 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L47-L49

Added lines #L47 - L49 were not covered by tests
name: 'AddTalkModal',
components: {
NcButton,
NcModal,

Check warning on line 53 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L52-L53

Added lines #L52 - L53 were not covered by tests
IconAdd,
},
props: {
calendarObjectInstance: {
type: Object,

Check warning on line 58 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L56-L58

Added lines #L56 - L58 were not covered by tests
required: true,
},
conversations: {

Check warning on line 61 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L61

Added line #L61 was not covered by tests
type: Array,
required: true,
},
},
data() {
return {

Check warning on line 67 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L65-L67

Added lines #L65 - L67 were not covered by tests
talkConversations: [],
selectedConversation: null,

Check warning on line 69 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L69

Added line #L69 was not covered by tests
creatingTalkRoom: false,
}
},
computed: {

Check warning on line 73 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L73

Added line #L73 was not covered by tests
...mapStores(useCalendarObjectInstanceStore),
},

Check warning on line 75 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L75

Added line #L75 was not covered by tests
async mounted() {
await this.fetchTalkConversations()
},

Check warning on line 78 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L77-L78

Added lines #L77 - L78 were not covered by tests
methods: {
async fetchTalkConversations() {

Check warning on line 80 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L80

Added line #L80 was not covered by tests
try {
const response = await axios.get(generateOcsUrl('apps/spreed/api/v4/room'))
this.talkConversations = response.data.ocs.data.filter(conversation =>
(conversation.participantType === PARTICIPANT_TYPE_OWNER
|| conversation.participantType === PARTICIPANT_TYPE_MODERATOR)
&& (conversation.type === CONVERSATION_TYPE_GROUP
|| (conversation.type === CONVERSATION_TYPE_PUBLIC

Check warning on line 87 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L83-L87

Added lines #L83 - L87 were not covered by tests
&& conversation.objectType !== CONVERSATION_OBJECT_TYPE_VIDEO_VERIFICATION)),
)

Check warning on line 89 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L89

Added line #L89 was not covered by tests
} catch (error) {
console.error('Error fetching Talk conversations:', error)
showError(this.$t('calendar', 'Error fetching Talk conversations.'))
}
},

Check warning on line 94 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L92-L94

Added lines #L92 - L94 were not covered by tests
selectConversation(conversation) {
console.debug('Selected conversation:', conversation)
try {

Check warning on line 97 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L96-L97

Added lines #L96 - L97 were not covered by tests
// Apply the selected conversation URL to the location field
if ((this.calendarObjectInstance.location ?? '').trim() === '') {
this.calendarObjectInstanceStore.changeLocation({
calendarObjectInstance: this.calendarObjectInstance,

Check warning on line 101 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L100-L101

Added lines #L100 - L101 were not covered by tests
location: conversation.url,
})

Check warning on line 103 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L103

Added line #L103 was not covered by tests
showSuccess(this.$t('calendar', 'Successfully added Talk room link to location.'))
} else {
const NEW_LINE = '\r\n'
const updatedDescription = this.calendarObjectInstance.description

Check warning on line 107 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L106-L107

Added lines #L106 - L107 were not covered by tests
? this.calendarObjectInstance.description + NEW_LINE + NEW_LINE + conversation.url
: conversation.url

this.calendarObjectInstanceStore.changeDescription({
calendarObjectInstance: this.calendarObjectInstance,
description: updatedDescription,
})
showSuccess(this.$t('calendar', 'Successfully added Talk room link to description.'))

Check warning on line 115 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L112-L115

Added lines #L112 - L115 were not covered by tests
}

this.selectedConversation = conversation
} catch (error) {
console.error('Error applying conversation to event:', error)
showError(this.$t('calendar', 'Failed to apply Talk room.'))
} finally {
this.closeModal()
}
},
async createTalkRoom() {
const NEW_LINE = '\r\n'
try {
this.creatingTalkRoom = true
const url = await createTalkRoom(
this.calendarObjectInstance.title,

Check warning on line 131 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L130-L131

Added lines #L130 - L131 were not covered by tests
this.calendarObjectInstance.description,
)

if (!url) {
throw new Error('No URL returned from createTalkRoom')
}

if ((this.calendarObjectInstance.location ?? '').trim() === '') {
this.$emit('update-location', url)
showSuccess(this.$t('calendar', 'Successfully added Talk room link to location.'))
} else {

Check warning on line 142 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L141-L142

Added lines #L141 - L142 were not covered by tests
const newDescription = this.calendarObjectInstance.description
? this.calendarObjectInstance.description + NEW_LINE + NEW_LINE + url + NEW_LINE
: url

this.$emit('update-description', newDescription)
showSuccess(this.$t('calendar', 'Successfully added Talk room link to description.'))
}
} catch (error) {
console.error('Error creating Talk room:', error)
showError(this.$t('calendar', 'Error creating Talk room.'))
} finally {
this.creatingTalkRoom = false
}
},
closeModal() {
this.$emit('close')
},
},
}
</script>

<style lang="scss" scoped>
.talk-room-list {
flex: 1;
overflow-y: auto;
padding: 15px;

&__item {
display: flex;
gap: 5px;
align-items: center;
padding-bottom: 10px;
}

Check warning on line 175 in src/components/Editor/AddTalkModal.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AddTalkModal.vue#L174-L175

Added lines #L174 - L175 were not covered by tests
}

.sticky-footer {
position: sticky;
bottom: 0;
padding: 16px;
text-align: right;
}

h2 {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
height: 30px;
}
</style>
2 changes: 1 addition & 1 deletion src/store/calendarObjectInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ export default defineStore('calendarObjectInstance', {
* @param {string=} data.language Preferred language of the attendee
* @param {string=} data.timezoneId Preferred timezone of the attendee
* @param {object=} data.organizer Principal of the organizer to be set if not present
* @param {string|array} data.member Group membership(s)
* @param {string | Array} data.member Group membership(s)
*/
addAttendee({
calendarObjectInstance,
Expand Down
119 changes: 112 additions & 7 deletions src/views/EditSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,28 @@
@update-end-time="updateEndTime"
@update-end-timezone="updateEndTimezone"
@toggle-all-day="toggleAllDay" />

<PropertyText class="property-location"
:is-read-only="isReadOnly"
:prop-model="rfcProps.location"
:value="location"
:linkify-links="true"
@update:value="updateLocation" />
<div class="location-container">
<PropertyText class="property-location"
:is-read-only="isReadOnly"
:prop-model="rfcProps.location"
:value="location"
:linkify-links="true"
@update:value="updateLocation" />
<AddTalkModal v-if="isModalOpen"
:conversations="talkConversations"
:calendarObjectInstance="calendarObjectInstance"
@close="closeModal"

Check failure on line 95 in src/views/EditSidebar.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Expected "\t" character, but found " " character

Check failure on line 95 in src/views/EditSidebar.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Mixed spaces and tabs
@update-location="updateLocation"
@update-description="updateDescription"/>

Check failure on line 97 in src/views/EditSidebar.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Expected a space before '/>', but not found
<NcButton :disabled="isCreateTalkRoomButtonDisabled"

Check warning on line 98 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L98

Added line #L98 was not covered by tests
class="add-talk-button"
@click="openModal">
<template #icon>

Check warning on line 101 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L101

Added line #L101 was not covered by tests
<IconCheck :size="16" />
</template>
{{ t('calendar','Add Talk') }}

Check warning on line 104 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L104

Added line #L104 was not covered by tests
</NcButton>
</div>
<PropertyText class="property-description"
:is-read-only="isReadOnly"
:prop-model="rfcProps.description"
Expand Down Expand Up @@ -314,10 +329,15 @@
import useSettingsStore from '../store/settings.js'
import useCalendarObjectInstanceStore from '../store/calendarObjectInstance.js'
import { mapStores, mapState } from 'pinia'
import AddTalkModal from '../components/Editor/AddTalkModal.vue'
import { createTalkRoom, doesContainTalkLink } from '../services/talkService.js'
import { showError, showSuccess } from '@nextcloud/dialogs'
import IconCheck from 'vue-material-design-icons/Check.vue'

Check warning on line 335 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L332-L335

Added lines #L332 - L335 were not covered by tests

export default {
name: 'EditSidebar',
components: {
AddTalkModal,
ResourceList,
PropertyColor,
PropertySelectMultiple,
Expand Down Expand Up @@ -348,6 +368,7 @@
AttachmentsList,
CalendarPickerHeader,
PropertyTitle,
IconCheck,

Check warning on line 371 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L371

Added line #L371 was not covered by tests
},
mixins: [
EditorMixin,
Expand All @@ -361,6 +382,10 @@
showModalUsers: [],
sharedProgress: 0,
showPreloader: false,
isModalOpen: false,
talkConversations: [],
selectedConversation: null,

Check warning on line 387 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L386-L387

Added lines #L386 - L387 were not covered by tests

}
},
computed: {
Expand Down Expand Up @@ -406,6 +431,21 @@
currentUser() {
return this.principalsStore.getCurrentUserPrincipal || null
},
isCreateTalkRoomButtonDisabled() {
if (this.creatingTalkRoom) {
return true
}

if (doesContainTalkLink(this.calendarObjectInstance.location)) {
return true
}
return doesContainTalkLink(this.calendarObjectInstance.description)

},

Check warning on line 444 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L444

Added line #L444 was not covered by tests
isCreateTalkRoomButtonVisible() {
return this.talkEnabled
},

Check warning on line 447 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L447

Added line #L447 was not covered by tests

},
mounted() {
window.addEventListener('keydown', this.keyboardCloseEditor)
Expand All @@ -420,6 +460,61 @@
window.removeEventListener('keydown', this.keyboardDuplicateEvent)
},
methods: {
selectConversation(conversation) {
this.selectedConversation = conversation
this.applyConversationToEvent(conversation)
this.closeModal()
},
openModal() {
this.isModalOpen = true
},

updateLocation(url) {
this.calendarObjectInstance.location = url
},
updateDescription(description) {
this.calendarObjectInstance.description = description
},
closeModal() {
this.isModalOpen = false
},

async createTalkRoom() {

Check warning on line 482 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L482

Added line #L482 was not covered by tests
const NEW_LINE = '\r\n'
try {
this.creatingTalkRoom = true

Check warning on line 485 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L485

Added line #L485 was not covered by tests
const url = await createTalkRoom(
this.calendarObjectInstance.title,
this.calendarObjectInstance.description,
)

Check warning on line 490 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L489-L490

Added lines #L489 - L490 were not covered by tests
// Store in LOCATION property if it's missing/empty. Append to description otherwise.
if ((this.calendarObjectInstance.location ?? '').trim() === '') {
this.calendarObjectInstanceStore.changeLocation({
calendarObjectInstance: this.calendarObjectInstance,
location: url,
})
showSuccess(this.$t('calendar', 'Successfully appended link to talk room to location.'))
} else {

Check warning on line 498 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L497-L498

Added lines #L497 - L498 were not covered by tests
if (!this.calendarObjectInstance.description) {
this.calendarObjectInstanceStore.changeDescription({
calendarObjectInstance: this.calendarObjectInstance,
description: url,
})
} else {
this.calendarObjectInstanceStore.changeDescription({
calendarObjectInstance: this.calendarObjectInstance,
description: this.calendarObjectInstance.description + NEW_LINE + NEW_LINE + url + NEW_LINE,
})
}
showSuccess(this.$t('calendar', 'Successfully appended link to talk room to description.'))
}
} catch (error) {
showError(this.$t('calendar', 'Error creating Talk room'))
} finally {
this.creatingTalkRoom = false
}
},
/**
* Update the start and end date of this event
*
Expand Down Expand Up @@ -676,8 +771,18 @@
height: auto;
border-radius: var(--border-radius);
}
.location-container{
display: flex;

Check warning on line 775 in src/views/EditSidebar.vue

View check run for this annotation

Codecov / codecov/patch

src/views/EditSidebar.vue#L774-L775

Added lines #L774 - L775 were not covered by tests
align-items: center;
gap: 4px;
width: 100%;
}
.add-talk-button {
flex-shrink: 0;
}
.property-location {
margin-top: 10px;
flex-grow: 1;
}
.property-description {
margin-bottom: 10px;
Expand Down
Loading