2727 class="filters"
2828 :class =" { ' hidden-visually' : isSearching } " >
2929 <template #icon >
30- <IconFilterOutline :size =" 20 " />
30+ <IconFilterCogOutline :size =" 20 " />
3131 </template >
3232 <NcActionCaption :name =" t (' spreed' , ' Filter conversations by' )" />
3333
7474 </template >
7575 {{ t('spreed', 'Clear filters') }}
7676 </NcActionButton >
77+
78+ <template v-if =" supportSortOrder " >
79+ <NcActionSeparator />
80+
81+ <NcActionCaption :name =" t (' spreed' , ' Sort conversations by' )" />
82+
83+ <NcActionButton
84+ closeAfterClick
85+ type="radio"
86+ :value =" CONVERSATION .SORT_ORDER .ACTIVITY "
87+ :modelValue =" sortOrder "
88+ @update :modelValue =" handleSortOrder " >
89+ <template #icon >
90+ <IconClockOutline :size =" 20 " />
91+ </template >
92+ {{ t('spreed', 'Recent activity') }}
93+ </NcActionButton >
94+
95+ <NcActionButton
96+ closeAfterClick
97+ type="radio"
98+ :value =" CONVERSATION .SORT_ORDER .ALPHABETICAL "
99+ :modelValue =" sortOrder "
100+ @update :modelValue =" handleSortOrder " >
101+ <template #icon >
102+ <IconSortAlphabeticalAscending :size =" 20 " />
103+ </template >
104+ {{ t('spreed', 'Alphabetical') }}
105+ </NcActionButton >
106+
107+ <NcActionSeparator />
108+
109+ <NcActionCaption :name =" t (' spreed' , ' Group conversations' )" />
110+
111+ <NcActionButton
112+ closeAfterClick
113+ type="checkbox"
114+ :modelValue =" groupMode === CONVERSATION .GROUP_MODE .PRIVATE_FIRST "
115+ @click =" handleGroupMode (CONVERSATION .GROUP_MODE .PRIVATE_FIRST )" >
116+ <template #icon >
117+ <IconAccountOutline :size =" 20 " />
118+ </template >
119+ {{ t('spreed', 'Private first') }}
120+ </NcActionButton >
121+
122+ <NcActionButton
123+ closeAfterClick
124+ type="checkbox"
125+ :modelValue =" groupMode === CONVERSATION .GROUP_MODE .GROUP_FIRST "
126+ @click =" handleGroupMode (CONVERSATION .GROUP_MODE .GROUP_FIRST )" >
127+ <template #icon >
128+ <IconAccountMultipleOutline :size =" 20 " />
129+ </template >
130+ {{ t('spreed', 'Groups first') }}
131+ </NcActionButton >
132+ </template >
77133 </NcActions >
78134 </TransitionWrapper >
79135
@@ -310,21 +366,25 @@ import { START_LOCATION } from 'vue-router'
310366import NcActionButton from ' @nextcloud/vue/components/NcActionButton'
311367import NcActionCaption from ' @nextcloud/vue/components/NcActionCaption'
312368import NcActions from ' @nextcloud/vue/components/NcActions'
369+ import NcActionSeparator from ' @nextcloud/vue/components/NcActionSeparator'
313370import NcAppNavigation from ' @nextcloud/vue/components/NcAppNavigation'
314371import NcAppNavigationCaption from ' @nextcloud/vue/components/NcAppNavigationCaption'
315372import NcAppNavigationItem from ' @nextcloud/vue/components/NcAppNavigationItem'
316373import NcButton from ' @nextcloud/vue/components/NcButton'
317374import NcChip from ' @nextcloud/vue/components/NcChip'
318375import NcCounterBubble from ' @nextcloud/vue/components/NcCounterBubble'
319376import NcEmptyContent from ' @nextcloud/vue/components/NcEmptyContent'
377+ import IconAccountMultipleOutline from ' vue-material-design-icons/AccountMultipleOutline.vue'
320378import IconAccountMultiplePlusOutline from ' vue-material-design-icons/AccountMultiplePlusOutline.vue'
379+ import IconAccountOutline from ' vue-material-design-icons/AccountOutline.vue'
321380import IconArchiveOutline from ' vue-material-design-icons/ArchiveOutline.vue'
322381import IconArrowLeft from ' vue-material-design-icons/ArrowLeft.vue'
323382import IconAt from ' vue-material-design-icons/At.vue'
324383import IconCalendarBlankOutline from ' vue-material-design-icons/CalendarBlankOutline.vue'
325384import IconChatPlusOutline from ' vue-material-design-icons/ChatPlusOutline.vue'
385+ import IconClockOutline from ' vue-material-design-icons/ClockOutline.vue'
326386import IconCogOutline from ' vue-material-design-icons/CogOutline.vue'
327- import IconFilterOutline from ' vue-material-design-icons/FilterOutline .vue'
387+ import IconFilterCogOutline from ' vue-material-design-icons/FilterCogOutline .vue'
328388import IconFilterRemoveOutline from ' vue-material-design-icons/FilterRemoveOutline.vue'
329389import IconFormatListBulleted from ' vue-material-design-icons/FormatListBulleted.vue'
330390import IconForumOutline from ' vue-material-design-icons/ForumOutline.vue'
@@ -334,6 +394,7 @@ import IconMessageOutline from 'vue-material-design-icons/MessageOutline.vue'
334394import IconNoteEditOutline from ' vue-material-design-icons/NoteEditOutline.vue'
335395import IconPhoneOutline from ' vue-material-design-icons/PhoneOutline.vue'
336396import IconPlus from ' vue-material-design-icons/Plus.vue'
397+ import IconSortAlphabeticalAscending from ' vue-material-design-icons/SortAlphabeticalAscending.vue'
337398import NewConversationDialog from ' ../NewConversationDialog/NewConversationDialog.vue'
338399import ThreadItem from ' ../RightSidebar/Threads/ThreadItem.vue'
339400import LoadingPlaceholder from ' ../UIShared/LoadingPlaceholder.vue'
@@ -375,6 +436,7 @@ const canModerateSipDialOut = hasTalkFeature('local', 'sip-support-dialout')
375436const canNoteToSelf = hasTalkFeature (' local' , ' note-to-self' )
376437const supportsArchive = hasTalkFeature (' local' , ' archived-conversations-v2' )
377438const supportThreads = hasTalkFeature (' local' , ' threads' )
439+ const supportSortOrder = getTalkConfig (' local' , ' conversations' , ' sort-order' ) !== undefined
378440
379441// TRANSLATORS The main home view
380442const HOME_BUTTON_LABEL = t (' spreed' , ' Home' )
@@ -407,6 +469,8 @@ export default {
407469 NcActions,
408470 NcActionButton,
409471 NcActionCaption,
472+ NcActionSeparator,
473+ NcEmptyContent,
410474 TransitionWrapper,
411475 ConversationsListVirtual,
412476 SearchConversationsResults,
@@ -415,7 +479,7 @@ export default {
415479 IconAt,
416480 IconMessageBadgeOutline,
417481 IconMessageOutline,
418- IconFilterOutline ,
482+ IconFilterCogOutline ,
419483 IconFilterRemoveOutline,
420484 IconArchiveOutline,
421485 IconArrowLeft,
@@ -428,7 +492,10 @@ export default {
428492 IconCogOutline,
429493 IconFormatListBulleted,
430494 IconNoteEditOutline,
431- NcEmptyContent,
495+ IconSortAlphabeticalAscending,
496+ IconClockOutline,
497+ IconAccountMultipleOutline,
498+ IconAccountOutline,
432499 },
433500
434501 setup () {
@@ -461,9 +528,11 @@ export default {
461528 canNoteToSelf,
462529 supportsArchive,
463530 supportThreads,
531+ supportSortOrder,
464532 showArchived,
465533 showThreadsList,
466534 settingsStore,
535+ CONVERSATION ,
467536 HOME_BUTTON_LABEL ,
468537 FILTER_LABELS ,
469538 actorStore: useActorStore (),
@@ -502,16 +571,50 @@ export default {
502571 },
503572
504573 computed: {
574+ sortOrder () {
575+ return this .settingsStore .sortOrder
576+ },
577+
578+ groupMode () {
579+ return this .settingsStore .groupMode
580+ },
581+
505582 conversationsList () {
506583 return this .$store .getters .conversationsList
507584 },
508585
509586 sortedConversationsList () {
510587 return this .filteredConversationsList .slice ()
511588 .sort ((conversation1 , conversation2 ) => {
589+ // Favorites always first
512590 if (conversation1 .isFavorite !== conversation2 .isFavorite ) {
513591 return conversation1 .isFavorite ? - 1 : 1
514592 }
593+
594+ // Group mode: groups first
595+ if (this .groupMode === CONVERSATION .GROUP_MODE .GROUPS_FIRST ) {
596+ const isGroup1 = conversation1 .type === CONVERSATION .TYPE .GROUP || conversation1 .type === CONVERSATION .TYPE .PUBLIC
597+ const isGroup2 = conversation2 .type === CONVERSATION .TYPE .GROUP || conversation2 .type === CONVERSATION .TYPE .PUBLIC
598+ if (isGroup1 !== isGroup2) {
599+ return isGroup1 ? - 1 : 1
600+ }
601+ }
602+
603+ // Group mode: private first
604+ if (this .groupMode === CONVERSATION .GROUP_MODE .PRIVATE_FIRST ) {
605+ const isPrivate1 = conversation1 .type === CONVERSATION .TYPE .ONE_TO_ONE || conversation1 .type === CONVERSATION .TYPE .ONE_TO_ONE_FORMER
606+ const isPrivate2 = conversation2 .type === CONVERSATION .TYPE .ONE_TO_ONE || conversation2 .type === CONVERSATION .TYPE .ONE_TO_ONE_FORMER
607+ if (isPrivate1 !== isPrivate2) {
608+ return isPrivate1 ? - 1 : 1
609+ }
610+ }
611+
612+ // Sort order
613+ if (this .sortOrder === CONVERSATION .SORT_ORDER .ALPHABETICAL ) {
614+ return (conversation1 .displayName || ' ' ).localeCompare (conversation2 .displayName || ' ' )
615+ }
616+
617+ // Default: activity (most recent first)
515618 return conversation2 .lastActivity - conversation1 .lastActivity
516619 })
517620 },
@@ -728,6 +831,19 @@ export default {
728831 this .$refs .invitationHandler .showModal ()
729832 },
730833
834+ handleSortOrder (newValue ) {
835+ this .settingsStore .updateSortOrder (newValue)
836+ },
837+
838+ handleGroupMode (newValue ) {
839+ if (newValue === this .groupMode ) {
840+ // Reset mode
841+ this .settingsStore .updateGroupMode (CONVERSATION .GROUP_MODE .NONE )
842+ } else {
843+ this .settingsStore .updateGroupMode (newValue)
844+ }
845+ },
846+
731847 handleFilter (filter ) {
732848 // Store the active filter
733849 if (filter === null ) {
0 commit comments