diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2021-03-15 11:48:15 +0100 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2021-05-30 10:28:56 +0200 |
commit | 77cc60e0edd627d3bbed63f0e34b13822b387baf (patch) | |
tree | cb8c26f64f3fb88175dd8ad90d1347865321dfb6 /src/services | |
parent | 21c5e699ffa394c45094e898af0f5192cf239bee (diff) |
New member button and virtual list
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'src/services')
-rw-r--r-- | src/services/circles.d.ts | 101 | ||||
-rw-r--r-- | src/services/circles.ts (renamed from src/services/circles.js) | 62 | ||||
-rw-r--r-- | src/services/collaborationAutocompletion.js | 141 |
3 files changed, 280 insertions, 24 deletions
diff --git a/src/services/circles.d.ts b/src/services/circles.d.ts new file mode 100644 index 00000000..3f8de3ee --- /dev/null +++ b/src/services/circles.d.ts @@ -0,0 +1,101 @@ +/** + * @copyright Copyright (c) 2021 John Molakvoæ <skjnldsv@protonmail.com> + * + * @author John Molakvoæ <skjnldsv@protonmail.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/>. + * + */ +import { MemberLevel, MemberType } from '../models/constants'; +interface MemberPairs { + id: string; + type: MemberType; +} +/** + * Get the circles list without the members + * + * @returns {Array} + */ +export declare const getCircles: () => Promise<any>; +/** + * Create a new circle + * + * @param {string} name the circle name + * @returns {Object} + */ +export declare const createCircle: (name: string) => Promise<any>; +/** + * Delete an existing circle + * + * @param {string} circleId the circle name + * @returns {Object} + */ +export declare const deleteCircle: (circleId: string) => Promise<any>; +/** + * Join a circle + * + * @param {string} circleId the circle name + * @returns {Array} + */ +export declare const joinCircle: (circleId: string) => Promise<any>; +/** + * Leave a circle + * + * @param {string} circleId the circle name + * @returns {Array} + */ +export declare const leaveCircle: (circleId: string) => Promise<any>; +/** + * Get the circle members without the members + * + * @param {string} circleId the circle id + * @returns {Array} + */ +export declare const getCircleMembers: (circleId: string) => Promise<any>; +/** + * Search a potential circle member + * + * @param {string} term the search query + * @returns {Array} + */ +export declare const searchMember: (term: string) => Promise<any>; +/** + * Add a circle member + * + * @param {string} circleId the circle id + * @param {string} members the member id + * @returns {Array} + */ +export declare const addMembers: (circleId: string, members: Array<MemberPairs>) => Promise<any>; +/** + * Delete a circle member + * + * @param {string} circleId the circle id + * @param {string} memberId the member id + * @returns {Array} + */ +export declare const deleteMember: (circleId: string, memberId: string) => Promise<unknown[]>; +/** + * change a member level + * @see levels file src/models/constants.js + * + * @param {string} circleId the circle id + * @param {string} memberId the member id + * @param {number} level the new member level + * @returns {Array} + */ +export declare const changeMemberLevel: (circleId: string, memberId: string, level: MemberLevel) => Promise<unknown[]>; +export {}; diff --git a/src/services/circles.js b/src/services/circles.ts index 544a944b..5d30c42e 100644 --- a/src/services/circles.js +++ b/src/services/circles.ts @@ -22,9 +22,12 @@ import axios from '@nextcloud/axios' import { generateOcsUrl } from '@nextcloud/router' -import { CIRCLES_MEMBER_LEVELS } from '../models/constants' +import { MemberLevel, MemberLevels, MemberType } from '../models/constants' -const baseApi = generateOcsUrl('apps/circles', 2) +interface MemberPairs { + id: string, + type: MemberType +} /** * Get the circles list without the members @@ -32,7 +35,7 @@ const baseApi = generateOcsUrl('apps/circles', 2) * @returns {Array} */ export const getCircles = async function() { - const response = await axios.get(baseApi + 'circles') + const response = await axios.get(generateOcsUrl('apps/circles/circles')) return response.data.ocs.data } @@ -42,8 +45,8 @@ export const getCircles = async function() { * @param {string} name the circle name * @returns {Object} */ -export const createCircle = async function(name) { - const response = await axios.post(baseApi + 'circles', { +export const createCircle = async function(name: string) { + const response = await axios.post(generateOcsUrl('apps/circles/circles'), { name, }) return response.data.ocs.data @@ -55,8 +58,8 @@ export const createCircle = async function(name) { * @param {string} circleId the circle name * @returns {Object} */ -export const deleteCircle = async function(circleId) { - const response = await axios.delete(baseApi + `circles/${circleId}`) +export const deleteCircle = async function(circleId: string) { + const response = await axios.delete(generateOcsUrl('apps/circles/circles/{circleId}', { circleId })) return response.data.ocs.data } @@ -66,8 +69,8 @@ export const deleteCircle = async function(circleId) { * @param {string} circleId the circle name * @returns {Array} */ -export const joinCircle = async function(circleId) { - const response = await axios.put(baseApi + `circles/${circleId}/join`) +export const joinCircle = async function(circleId: string) { + const response = await axios.put(generateOcsUrl('apps/circles/circles/{circleId}/join', { circleId })) return response.data.ocs.data } @@ -77,8 +80,8 @@ export const joinCircle = async function(circleId) { * @param {string} circleId the circle name * @returns {Array} */ -export const leaveCircle = async function(circleId) { - const response = await axios.put(baseApi + `circles/${circleId}/leave`) +export const leaveCircle = async function(circleId: string) { + const response = await axios.put(generateOcsUrl('apps/circles/circles/{circleId}/leave', { circleId })) return response.data.ocs.data } @@ -88,21 +91,32 @@ export const leaveCircle = async function(circleId) { * @param {string} circleId the circle id * @returns {Array} */ -export const getCircleMembers = async function(circleId) { - const response = await axios.get(baseApi + `circles/${circleId}/members`) - return Object.values(response.data.ocs.data) +export const getCircleMembers = async function(circleId: string) { + const response = await axios.get(generateOcsUrl('apps/circles/circles/{circleId}/members', { circleId })) + return response.data.ocs.data +} + +/** + * Search a potential circle member + * + * @param {string} term the search query + * @returns {Array} + */ +export const searchMember = async function(term: string) { + const response = await axios.get(generateOcsUrl('apps/circles/search?term={term}', { term })) + return response.data.ocs.data } /** * Add a circle member * * @param {string} circleId the circle id - * @param {string} memberId the member id + * @param {string} members the member id * @returns {Array} */ -export const addMember = async function(circleId, memberId) { - const response = await axios.delete(baseApi + `circles/${circleId}/members/${memberId}`) - return Object.values(response.data.ocs.data) +export const addMembers = async function(circleId: string, members: Array<MemberPairs>) { + const response = await axios.post(generateOcsUrl('apps/circles/circles/{circleId}/members/multi', { circleId }), { members }) + return response.data.ocs.data } /** @@ -112,8 +126,8 @@ export const addMember = async function(circleId, memberId) { * @param {string} memberId the member id * @returns {Array} */ -export const deleteMember = async function(circleId, memberId) { - const response = await axios.delete(baseApi + `circles/${circleId}/members/${memberId}`) +export const deleteMember = async function(circleId: string, memberId: string) { + const response = await axios.delete(generateOcsUrl('apps/circles/circles/{circleId}/members/{memberId}', { circleId, memberId })) return Object.values(response.data.ocs.data) } @@ -126,12 +140,12 @@ export const deleteMember = async function(circleId, memberId) { * @param {number} level the new member level * @returns {Array} */ -export const changeMemberLevel = async function(circleId, memberId, level) { - if (!(level in CIRCLES_MEMBER_LEVELS)) { - throw new Error('Invalid level. Valid levels are', CIRCLES_MEMBER_LEVELS) +export const changeMemberLevel = async function(circleId: string, memberId: string, level: MemberLevel) { + if (!(level in MemberLevels)) { + throw new Error('Invalid level.') } - const response = await axios.put(baseApi + `circles/${circleId}/members/${memberId}}/level`, { + const response = await axios.put(generateOcsUrl('apps/circles/circles/{circleId}/members/{memberId}/level', { circleId, memberId }), { level, }) return Object.values(response.data.ocs.data) diff --git a/src/services/collaborationAutocompletion.js b/src/services/collaborationAutocompletion.js new file mode 100644 index 00000000..f7200387 --- /dev/null +++ b/src/services/collaborationAutocompletion.js @@ -0,0 +1,141 @@ + +/** + * @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> + * + * @author John Molakvoæ <skjnldsv@protonmail.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/>. + * + */ + +import axios from '@nextcloud/axios' +import { generateOcsUrl } from '@nextcloud/router' + +const maxAutocompleteResults = parseInt(OC.config['sharing.maxAutocompleteResults'], 10) || 25 + +export const shareType = [ + OC.Share.SHARE_TYPE_USER, + OC.Share.SHARE_TYPE_GROUP, + // OC.Share.SHARE_TYPE_REMOTE, + // OC.Share.SHARE_TYPE_REMOTE_GROUP, + OC.Share.SHARE_TYPE_CIRCLE, + // OC.Share.SHARE_TYPE_ROOM, + // OC.Share.SHARE_TYPE_GUEST, + // OC.Share.SHARE_TYPE_DECK, + OC.Share.SHARE_TYPE_EMAIL, +] + +/** + * Get suggestions + * + * @param {string} search the search query + */ +export const getSuggestions = async function(search) { + const request = await axios.get(generateOcsUrl('apps/files_sharing/api/v1/sharees'), { + params: { + format: 'json', + itemType: 'file', + search, + perPage: maxAutocompleteResults, + shareType, + }, + }) + + const data = request.data.ocs.data + const exact = request.data.ocs.data.exact + data.exact = [] // removing exact from general results + + // flatten array of arrays + const rawExactSuggestions = Object.values(exact).reduce((arr, elem) => arr.concat(elem), []) + const rawSuggestions = Object.values(data).reduce((arr, elem) => arr.concat(elem), []) + + // remove invalid data and format to user-select layout + const exactSuggestions = rawExactSuggestions + .filter(result => typeof result === 'object') + .map(share => formatResults(share)) + // sort by type so we can get user&groups first... + .sort((a, b) => a.shareType - b.shareType) + const suggestions = rawSuggestions + .filter(result => typeof result === 'object') + .map(share => formatResults(share)) + // sort by type so we can get user&groups first... + .sort((a, b) => a.shareType - b.shareType) + + const allSuggestions = exactSuggestions.concat(suggestions) + + // Count occurances of display names in order to provide a distinguishable description if needed + const nameCounts = allSuggestions.reduce((nameCounts, result) => { + if (!result.displayName) { + return nameCounts + } + if (!nameCounts[result.displayName]) { + nameCounts[result.displayName] = 0 + } + nameCounts[result.displayName]++ + return nameCounts + }, {}) + + const finalResults = allSuggestions.map(item => { + // Make sure that items with duplicate displayName get the shareWith applied as a description + if (nameCounts[item.displayName] > 1 && !item.desc) { + return { ...item, desc: item.shareWithDisplayNameUnique } + } + return item + }) + + console.info('suggestions', finalResults) + + return finalResults +} + +/** + * Get the sharing recommendations + */ +export const getRecommendations = async function() { + const request = await axios.get(generateOcsUrl('apps/files_sharing/api/v1/sharees_recommended'), { + params: { + format: 'json', + itemType: 'file', + shareType, + }, + }) + + // flatten array of arrays + const exact = request.data.ocs.data.exact + const recommendations = Object.values(exact).reduce((arr, elem) => arr.concat(elem), []) + + // remove invalid data and format to user-select layout + const finalResults = recommendations + .map(share => formatResults(share)) + + console.info('recommendations', finalResults) + + return finalResults +} + +const formatResults = function(result) { + const type = `picker-${result.value.shareType}` + return { + label: result.label, + id: `${type}-${result.value.shareWith}`, + // If this is a user, set as user for avatar display by UserBubble + user: result.value.shareType === OC.Share.SHARE_TYPE_USER + ? result.value.shareWith + : null, + type, + ...result.value, + } +} |