77 <section class =" member-list" >
88 <NcEmptyContent v-if =" loading " class="empty-content" :name =" t (' contacts' , ' Loading members list …' )" >
99 <template #icon >
10- <IconLoading :size =" 20 " />
10+ <NcLoadingIcon :size =" 20 " />
1111 </template >
1212 </NcEmptyContent >
1313
6262import { showError , showWarning } from ' @nextcloud/dialogs'
6363import { subscribe } from ' @nextcloud/event-bus'
6464import { t } from ' @nextcloud/l10n'
65- import { NcEmptyContent } from ' @nextcloud/vue'
65+ import { NcEmptyContent , NcLoadingIcon } from ' @nextcloud/vue'
66+ import { refDebounced } from ' @vueuse/core'
6667import { VList } from ' virtua/vue'
67- import { defineComponent } from ' vue'
68+ import { defineComponent , ref } from ' vue'
6869import IconContact from ' vue-material-design-icons/AccountMultipleOutline.vue'
70+ import IconSearch from ' vue-material-design-icons/Magnify.vue'
71+ import IconAdd from ' vue-material-design-icons/Plus.vue'
6972import EntityPicker from ' ../EntityPicker/EntityPicker.vue'
7073import MemberGridItem from ' ./MemberGridItem.vue'
7174import IsMobileMixin from ' ../../mixins/IsMobileMixin.ts'
7275import RouterMixin from ' ../../mixins/RouterMixin.js'
73- import { CIRCLES_MEMBER_GROUPING , SHARES_TYPES_MEMBER_MAP } from ' ../../models/constants.ts'
76+ import {
77+ CIRCLES_MEMBER_GROUPING ,
78+ CIRCLES_MEMBER_LEVELS ,
79+ MAX_MEMBERS_TO_RENDER ,
80+ MemberLevels ,
81+ SHARES_TYPES_MEMBER_MAP ,
82+ } from ' ../../models/constants.ts'
7483import { getRecommendations , getSuggestions } from ' ../../services/collaborationAutocompletion.js'
7584
7685export default defineComponent ({
7786 name: ' MemberList' ,
7887
7988 components: {
89+ IconSearch ,
90+ NcTextField ,
91+ NcSelect ,
92+ IconAdd ,
93+ MemberGridItem ,
94+ NcButton ,
8095 EntityPicker ,
8196 IconContact ,
8297 MemberGridItem ,
8398 NcEmptyContent ,
8499 VList ,
100+ NcLoadingIcon ,
101+ ContentHeading ,
85102 },
86103
87104 mixins: [IsMobileMixin , RouterMixin ],
@@ -98,8 +115,35 @@ export default defineComponent({
98115 },
99116 },
100117
118+ setup() {
119+ const searchQuery = ref (' ' )
120+ const clearSearchField = () => {
121+ searchQuery .value = ' '
122+ }
123+ const searchQueryDebounced = refDebounced (searchQuery , 500 )
124+
125+ const searchRole = ref (null )
126+ const roles = Object .entries (CIRCLES_MEMBER_LEVELS ).map (([id , label ]) => ({
127+ id: Number (id ),
128+ label ,
129+ }))
130+ roles .unshift ({
131+ id: Number (MemberLevels .NONE ),
132+ label: t (' contacts' , ' Pending' ),
133+ })
134+
135+ return {
136+ searchQuery ,
137+ searchQueryDebounced ,
138+ clearSearchField ,
139+ searchRole ,
140+ roles ,
141+ }
142+ },
143+
101144 data() {
102145 return {
146+ loadingList: false ,
103147 pickerLoading: false ,
104148 showPicker: false ,
105149 showPickerIntro: true ,
@@ -118,12 +162,30 @@ export default defineComponent({
118162 /**
119163 * Return the current circle
120164 *
121- * @return {object }
165+ * @return {Circle }
122166 */
123167 circle() {
124168 return this .$store .getters .getCircle (this .selectedCircle )
125169 },
126170
171+ members() {
172+ return Object .values (this .$store .getters .getCircle (this .circle .id )?.members || [])
173+ },
174+
175+ membershipTooLargeMessage() {
176+ if (this .searchQueryDebounced || this .searchRole ?.id ) {
177+ const searchQuery = this .searchQueryDebounced || ' -'
178+ const searchRole = this .searchRole ?.label || ' any'
179+ return ` Search results (query: ${searchQuery }, role: ${searchRole }) contains too many entries. `
180+ }
181+
182+ return ' Users list too large'
183+ },
184+
185+ isMembersLisTooLarge() {
186+ return this .flatList .length > MAX_MEMBERS_TO_RENDER
187+ },
188+
127189 // Decode HTML entities in the circle display name so apostrophes (') and other
128190 // HTML-encoded chars (e.g. ') are shown correctly in the picker labels.
129191 decodedTeamName(): string {
@@ -168,6 +230,24 @@ export default defineComponent({
168230 },
169231 },
170232
233+ watch: {
234+ searchQueryDebounced(value ) {
235+ this .fetchCircleMembers ()
236+ },
237+
238+ searchRole(value ) {
239+ this .fetchCircleMembers ()
240+ },
241+
242+ ' circle.id' : {
243+ handler() {
244+ this .fetchCircleMembers ()
245+ },
246+
247+ immediate: true ,
248+ },
249+ },
250+
171251 mounted() {
172252 subscribe (' contacts:circles:append' , this .onShowPicker )
173253 subscribe (' guests:user:created' , this .onGuestCreated )
@@ -179,6 +259,8 @@ export default defineComponent({
179259 },
180260
181261 methods: {
262+ t ,
263+
182264 /**
183265 * Measure the circle details header height from the DOM
184266 * and keep it updated via ResizeObserver.
@@ -296,6 +378,26 @@ export default defineComponent({
296378 const results = await getSuggestions (guest .username , this .circle )
297379 this .$refs .entityPicker .onClick (results [0 ])
298380 },
381+
382+ async fetchCircleMembers() {
383+ if (! this .circle ?.canManageMembers ) {
384+ return
385+ }
386+
387+ this .loadingList = true
388+ const payload = { circleId: this .circle .id , searchQuery: this .searchQuery || null , role: this .searchRole ?.id }
389+ this .logger .debug (' Fetching members for' , payload )
390+
391+ try {
392+ await this .$store .dispatch (' getCircleMembers' , payload )
393+ console .log (' debug: getCircleMembers' , this .list )
394+ } catch (error ) {
395+ console .error (error )
396+ showError (t (' contacts' , ' There was an error fetching the member list' ))
397+ } finally {
398+ this .loadingList = false
399+ }
400+ },
299401 },
300402})
301403 </script >
0 commit comments