Skip to content
Open
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
8 changes: 2 additions & 6 deletions src/components/Member/MemberItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ export default {
t,

...mapActions(useCirclesStore, [
'getCircleMembers',
'changeCircleMemberLevel',
'removeMemberFromCircle',
]),
Expand All @@ -267,9 +266,8 @@ export default {
if (this.circleId) {
this.isLoadingLevel = true
await this.changeCircleMemberLevel({ circleId: this.circleId, memberId: this.memberId, level })
.then(async () => {
.then(() => {
showSuccess(t('collectives', 'Member level changed'))
await this.getCircleMembers(this.circleId)
}).catch((error) => {
showError(t('collectives', 'Could not change member level'))
throw error
Expand All @@ -283,16 +281,14 @@ export default {
if (this.circleId) {
this.isLoadingLevel = true
await this.removeMemberFromCircle({ circleId: this.circleId, memberId: this.memberId })
.then(async () => {
.then(() => {
showSuccess(t('collectives', 'Member removed'))
await this.getCircleMembers(this.circleId)
}).catch((error) => {
showError(t('collectives', 'Could not remove member'))
throw error
}).finally(() => {
this.isLoadingLevel = false
})
this.isLoadingLevel = false
}
},

Expand Down
7 changes: 6 additions & 1 deletion src/components/Member/MemberPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ export default {
},

props: {
isLoading: {
type: Boolean,
default: false,
},

searchWithoutQuery: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -169,7 +174,7 @@ export default {
},

showCurrentSkeleton() {
return this.showCurrent && this.currentMembers.length === 0
return this.showCurrent && (this.currentMembers.length === 0 || this.isLoading)
},
},

Expand Down
9 changes: 7 additions & 2 deletions src/components/Nav/CollectiveMembersModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
:circleId="collective.circleId"
:currentUserIsAdmin
:currentMembers="circleMembersSorted(collective.circleId)"
:isLoading="isLoadingMembers"
:onClickSearched />
</div>
</NcModal>
Expand Down Expand Up @@ -63,7 +64,7 @@ export default {
],

computed: {
...mapState(useCirclesStore, ['circleMembersSorted']),
...mapState(useCirclesStore, ['circleMembersSorted', 'currentCircleMembersFullyLoaded']),
...mapState(useCollectivesStore, ['isCollectiveAdmin']),
...mapState(useRootStore, ['isPublic']),

Expand All @@ -82,6 +83,10 @@ export default {
showTeamLink() {
return this.hasContactsApp && !this.isPublic
},

isLoadingMembers() {
return !this.currentCircleMembersFullyLoaded
},
},

beforeMount() {
Expand All @@ -106,8 +111,8 @@ export default {
circleId: this.collective.circleId,
userId: member.id,
type: circlesMemberTypes[autocompleteSourcesToCircleMemberTypes[member.source]],
displayName: member.label,
})
await this.getCircleMembers(this.collective.circleId)
},
},
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Page/LandingPageWidgets/MembersWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import AccountMultiplePlusIcon from 'vue-material-design-icons/AccountMultiplePl
import ChevronDownIcon from 'vue-material-design-icons/ChevronDown.vue'
import SkeletonLoading from '../../SkeletonLoading.vue'
import WidgetHeading from './WidgetHeading.vue'
import { circlesMemberTypes } from '../../../constants.js'
import { CIRCLE_MEMBERS_PARTIAL_LIMIT, circlesMemberTypes } from '../../../constants.js'
import { useCirclesStore } from '../../../stores/circles.js'
import { useCollectivesStore } from '../../../stores/collectives.js'
import { usePagesStore } from '../../../stores/pages.js'
Expand Down Expand Up @@ -208,7 +208,7 @@ export default {
const avatarHeight = defaultClickableArea + 12
if (membersWidth) {
const maxMembers = Math.floor(membersWidth / avatarHeight) - 1
this.showMembersCount = Math.min(this.sortedMembers.length, maxMembers)
this.showMembersCount = Math.min(this.sortedMembers.length, maxMembers, CIRCLE_MEMBERS_PARTIAL_LIMIT)
}
},

Expand Down
10 changes: 9 additions & 1 deletion src/components/Page/TextEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import SkeletonLoading from '../SkeletonLoading.vue'
import { useEditor } from '../../composables/useEditor.ts'
import { useReader } from '../../composables/useReader.ts'
import pageContentMixin from '../../mixins/pageContentMixin.js'
import { useCirclesStore } from '../../stores/circles.js'
import { useCollectivesStore } from '../../stores/collectives.js'
import { usePagesStore } from '../../stores/pages.js'
import { useRootStore } from '../../stores/root.js'
Expand Down Expand Up @@ -121,9 +122,15 @@ export default {
this.initEditMode()
})

this.textEditWatcher = this.$watch('isTextEdit', (val) => {
this.textEditWatcher = this.$watch('isTextEdit', async (val) => {
if (val === false) {
this.stopEdit()
} else if (val === true) {
// Load full circle members for autocomplete when entering edit mode
const circlesStore = useCirclesStore()
if (!circlesStore.currentCircleMembersFullyLoaded && !this.isPublic) {
await this.getCircleMembers(this.currentCollective.circleId)
}
}
})
subscribe('collectives:attachment:insert', this.insertAttachment)
Expand All @@ -144,6 +151,7 @@ export default {
...mapActions(useRootStore, ['load', 'done']),
...mapActions(useVersionsStore, ['getVersions']),
...mapActions(usePagesStore, ['setTextEdit', 'setTextPreview', 'touchPage']),
...mapActions(useCirclesStore, ['getCircleMembers']),

insertAttachment({ name }) {
// inspired by the fixedEncodeURIComponent function suggested in
Expand Down
3 changes: 3 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export const pageModes = {
MODE_EDIT: 1,
}

// Circle members
export const CIRCLE_MEMBERS_PARTIAL_LIMIT = 15

export const editorApiReaderFileId = 'READER_FILE_ID'
export const editorApiUpdateReadonlyBarProps = 'UPDATE_READONLY_BAR_PROPS'
export const editorApiAttachments = 'ATTACHMENTS'
Expand Down
62 changes: 56 additions & 6 deletions src/stores/circles.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
import { useLocalStorage } from '@vueuse/core'
import { defineStore } from 'pinia'
import { circlesMemberTypes } from '../constants.js'
import { circlesMemberTypes, memberLevels } from '../constants.js'
import { sortMembersByLevelAndType } from '../util/circles.ts'
import { useCollectivesStore } from './collectives.js'
import { useRootStore } from './root.js'
Expand All @@ -17,7 +17,9 @@ const STORE_PREFIX = 'collectives/pinia/circles/'
export const useCirclesStore = defineStore('circles', {
state: () => ({
circles: useLocalStorage(STORE_PREFIX + 'circles', []),
circlesMembers: useLocalStorage(STORE_PREFIX + 'circlesMembers', {}),
circlesMembers: {},
circlesMembersFullyLoaded: {},
circlesMembersPending: {},
}),

getters: {
Expand Down Expand Up @@ -59,6 +61,12 @@ export const useCirclesStore = defineStore('circles', {
}
return users
},

currentCircleMembersFullyLoaded: (state) => {
const collectivesStore = useCollectivesStore()
const currentCircleId = collectivesStore.currentCollective?.circleId
return state.circlesMembersFullyLoaded[currentCircleId] || false
},
},

actions: {
Expand Down Expand Up @@ -108,10 +116,23 @@ export const useCirclesStore = defineStore('circles', {
* Get members of a team
*
* @param {string} circleId ID of the team
* @param {number} limit Limit of members to fetch, 0 for all members
*/
async getCircleMembers(circleId) {
const response = await axios.get(generateOcsUrl(`apps/circles/circles/${circleId}/members?fullDetails=true`))
this.circlesMembers[circleId] = response.data.ocs.data
async getCircleMembers(circleId, limit = 0) {
// Skip if already fully loaded or request is pending
if (this.circlesMembersFullyLoaded[circleId] || this.circlesMembersPending[circleId]) {
return
}
this.circlesMembersPending[circleId] = true
try {
const response = await axios.get(generateOcsUrl(`apps/circles/circles/${circleId}/members?fullDetails=true&limit=${limit}`))
this.circlesMembers[circleId] = response.data.ocs.data
if (limit === 0) {
this.circlesMembersFullyLoaded[circleId] = true
}
} finally {
this.circlesMembersPending[circleId] = false
}
},

/**
Expand All @@ -121,12 +142,32 @@ export const useCirclesStore = defineStore('circles', {
* @param {string} params.circleId ID of the team
* @param {string} params.userId User ID of the member to be added
* @param {number} params.type Type of the member to be added
* @param {string} params.displayName Display name of the member to be added
*/
async addMemberToCircle({ circleId, userId, type }) {
async addMemberToCircle({ circleId, userId, type, displayName }) {
const response = await axios.post(
generateOcsUrl('apps/circles/circles/' + circleId + '/members'),
{ userId, type },
)
if (this.circlesMembers[circleId]) {
const newMember = {
id: `temp-${userId}`,
singleId: `temp-${userId}`,
circleId,
userId,
displayName,
userType: type,
level: memberLevels.LEVEL_MEMBER,
}
const existingMembers = this.circlesMembers[circleId]
if (existingMembers.length > 0) {
newMember.circle = existingMembers[0].circle
}
if (type === circlesMemberTypes.TYPE_CIRCLE) {
newMember.basedOn = { source: circlesMemberTypes.TYPE_CIRCLE }
}
this.circlesMembers[circleId].push(newMember)
}
return response.data.ocs.data
},

Expand Down Expand Up @@ -154,6 +195,9 @@ export const useCirclesStore = defineStore('circles', {
*/
async removeMemberFromCircle({ circleId, memberId }) {
const response = await axios.delete(generateOcsUrl('apps/circles/circles/' + circleId + '/members/' + memberId))
if (this.circlesMembers[circleId]) {
this.circlesMembers[circleId] = this.circlesMembers[circleId].filter((m) => m.id !== memberId)
}
return response.data.ocs.data
},

Expand All @@ -170,6 +214,12 @@ export const useCirclesStore = defineStore('circles', {
generateOcsUrl('apps/circles/circles/' + circleId + '/members/' + memberId + '/level'),
{ level },
)
if (this.circlesMembers[circleId]) {
const member = this.circlesMembers[circleId].find((m) => m.id === memberId)
if (member) {
member.level = level
}
}
return response.data.ocs.data
},

Expand Down
5 changes: 3 additions & 2 deletions src/views/CollectiveView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import CollectiveContainer from '../components/CollectiveContainer.vue'
import CollectiveNotFound from '../components/CollectiveNotFound.vue'
import PageList from '../components/PageList.vue'
import { useNetworkState } from '../composables/useNetworkState.ts'
import { sessionUpdateInterval } from '../constants.js'
import { CIRCLE_MEMBERS_PARTIAL_LIMIT, pageModes, sessionUpdateInterval } from '../constants.js'
import { useCirclesStore } from '../stores/circles.js'
import { useCollectivesStore } from '../stores/collectives.js'
import { usePagesStore } from '../stores/pages.js'
Expand Down Expand Up @@ -221,7 +221,8 @@ export default {
promises.push(this.getTemplates(setLoading))
}
if (!this.isPublic) {
promises.push(this.getCircleMembers(this.currentCollective.circleId))
const limit = this.currentCollective.pageMode === pageModes.MODE_EDIT ? 0 : CIRCLE_MEMBERS_PARTIAL_LIMIT
promises.push(this.getCircleMembers(this.currentCollective.circleId, limit))
}
}

Expand Down
Loading