mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 02:55:58 +01:00 
			
		
		
		
	fix: #12357, properly update lists and counters
This commit is contained in:
		| @@ -6,24 +6,24 @@ define('forum/groups/details', [ | ||||
| 	'components', | ||||
| 	'coverPhoto', | ||||
| 	'pictureCropper', | ||||
| 	'translator', | ||||
| 	'api', | ||||
| 	'slugify', | ||||
| 	'categorySelector', | ||||
| 	'bootbox', | ||||
| 	'alerts', | ||||
| 	'helpers', | ||||
| ], function ( | ||||
| 	memberList, | ||||
| 	iconSelect, | ||||
| 	components, | ||||
| 	coverPhoto, | ||||
| 	pictureCropper, | ||||
| 	translator, | ||||
| 	api, | ||||
| 	slugify, | ||||
| 	categorySelector, | ||||
| 	bootbox, | ||||
| 	alerts | ||||
| 	alerts, | ||||
| 	helpers | ||||
| ) { | ||||
| 	const Details = {}; | ||||
| 	let groupName; | ||||
| @@ -85,14 +85,19 @@ define('forum/groups/details', [ | ||||
| 					break; | ||||
|  | ||||
| 				case 'kick': | ||||
| 					translator.translate('[[groups:details.kick-confirm]]', function (translated) { | ||||
| 						bootbox.confirm(translated, function (confirm) { | ||||
| 							if (!confirm) { | ||||
| 								return; | ||||
| 							} | ||||
| 					bootbox.confirm('[[groups:details.kick-confirm]]', function (confirm) { | ||||
| 						if (!confirm) { | ||||
| 							return; | ||||
| 						} | ||||
|  | ||||
| 							api.del(`/groups/${ajaxify.data.group.slug}/membership/${uid}`, undefined).then(() => userRow.slideUp().remove()).catch(alerts.error); | ||||
| 						}); | ||||
| 						api.del(`/groups/${ajaxify.data.group.slug}/membership/${uid}`, undefined).then( | ||||
| 							() => { | ||||
| 								userRow.remove(); | ||||
| 								$('[component="group/member/count"]').text( | ||||
| 									helpers.humanReadableNumber(ajaxify.data.group.memberCount - 1) | ||||
| 								); | ||||
| 							} | ||||
| 						).catch(alerts.error); | ||||
| 					}); | ||||
| 					break; | ||||
|  | ||||
| @@ -105,29 +110,42 @@ define('forum/groups/details', [ | ||||
| 					break; | ||||
|  | ||||
| 				case 'join': | ||||
| 					api.put('/groups/' + ajaxify.data.group.slug + '/membership/' + (uid || app.user.uid), undefined).then(() => ajaxify.refresh()).catch(alerts.error); | ||||
| 					api.put('/groups/' + ajaxify.data.group.slug + '/membership/' + (uid || app.user.uid), undefined).then( | ||||
| 						() => ajaxify.refresh() | ||||
| 					).catch(alerts.error); | ||||
| 					break; | ||||
|  | ||||
| 				case 'leave': | ||||
| 					api.del('/groups/' + ajaxify.data.group.slug + '/membership/' + (uid || app.user.uid), undefined).then(() => ajaxify.refresh()).catch(alerts.error); | ||||
| 					api.del('/groups/' + ajaxify.data.group.slug + '/membership/' + (uid || app.user.uid), undefined).then( | ||||
| 						() => ajaxify.refresh() | ||||
| 					).catch(alerts.error); | ||||
| 					break; | ||||
|  | ||||
| 				case 'accept': | ||||
| 					api.put(`/groups/${ajaxify.data.group.slug}/pending/${uid}`).then(() => ajaxify.refresh()).catch(alerts.error); | ||||
| 					api.put(`/groups/${ajaxify.data.group.slug}/pending/${uid}`).then( | ||||
| 						() => { | ||||
| 							userRow.remove(); | ||||
| 							memberList.refresh(); | ||||
| 							updatePendingAlertVisibility(); | ||||
| 						} | ||||
| 					).catch(alerts.error); | ||||
| 					break; | ||||
|  | ||||
| 				case 'reject': | ||||
| 					api.del(`/groups/${ajaxify.data.group.slug}/pending/${uid}`).then(() => ajaxify.refresh()).catch(alerts.error); | ||||
| 					break; | ||||
|  | ||||
| 				case 'issueInvite': | ||||
| 					api.post(`/groups/${ajaxify.data.group.slug}/invites/${uid}`).then(() => ajaxify.refresh()).catch(alerts.error); | ||||
| 					api.del(`/groups/${ajaxify.data.group.slug}/pending/${uid}`).then( | ||||
| 						() => { | ||||
| 							userRow.remove(); | ||||
| 							memberList.refresh(); | ||||
| 							updatePendingAlertVisibility(); | ||||
| 						} | ||||
| 					).catch(alerts.error); | ||||
| 					break; | ||||
|  | ||||
| 				case 'acceptInvite': | ||||
| 					api.put(`/groups/${ajaxify.data.group.slug}/invites/${app.user.uid}`).then(() => { | ||||
| 						if (uid) { | ||||
| 							userRow.remove(); | ||||
| 							memberList.refresh(); | ||||
| 						} else { | ||||
| 							ajaxify.refresh(); | ||||
| 						} | ||||
| @@ -139,6 +157,8 @@ define('forum/groups/details', [ | ||||
| 					api.del(`/groups/${ajaxify.data.group.slug}/invites/${uid || app.user.uid}`).then(() => { | ||||
| 						if (uid) { | ||||
| 							userRow.remove(); | ||||
| 							updateInviteAlertVisibility(); | ||||
| 							memberList.refresh(); | ||||
| 						} else { | ||||
| 							ajaxify.refresh(); | ||||
| 						} | ||||
| @@ -268,6 +288,20 @@ define('forum/groups/details', [ | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	function updatePendingAlertVisibility() { | ||||
| 		$('[component="groups/pending/alert"]').toggleClass( | ||||
| 			'hidden', | ||||
| 			$('[component="groups/pending"] tbody tr').length > 0 | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	function updateInviteAlertVisibility() { | ||||
| 		$('[component="groups/invited/alert"]').toggleClass( | ||||
| 			'hidden', | ||||
| 			$('[component="groups/invited"] tbody tr').length > 0 | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	function handleMemberInvitations() { | ||||
| 		if (!ajaxify.data.group.isOwner) { | ||||
| 			return; | ||||
| @@ -275,8 +309,9 @@ define('forum/groups/details', [ | ||||
| 		async function updateList() { | ||||
| 			const data = await api.get(`/api/groups/${ajaxify.data.group.slug}`); | ||||
| 			const html = await app.parseAndTranslate('groups/details', 'group.invited', { group: data.group }); | ||||
| 			$('[component="groups/invited"] tbody tr').remove(); | ||||
| 			$('[component="groups/invited"] tbody').html(html); | ||||
| 			updateInviteAlertVisibility(); | ||||
| 			memberList.refresh(); | ||||
| 		} | ||||
| 		const searchInput = $('[component="groups/members/invite"]'); | ||||
| 		require(['autocomplete'], function (autocomplete) { | ||||
| @@ -305,21 +340,19 @@ define('forum/groups/details', [ | ||||
| 	} | ||||
|  | ||||
| 	function removeCover() { | ||||
| 		translator.translate('[[groups:remove-group-cover-confirm]]', function (translated) { | ||||
| 			bootbox.confirm(translated, function (confirm) { | ||||
| 				if (!confirm) { | ||||
| 					return; | ||||
| 				} | ||||
| 		bootbox.confirm('[[groups:remove-group-cover-confirm]]', function (confirm) { | ||||
| 			if (!confirm) { | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 				socket.emit('groups.cover.remove', { | ||||
| 					groupName: ajaxify.data.group.name, | ||||
| 				}, function (err) { | ||||
| 					if (!err) { | ||||
| 						ajaxify.refresh(); | ||||
| 					} else { | ||||
| 						alerts.error(err); | ||||
| 					} | ||||
| 				}); | ||||
| 			socket.emit('groups.cover.remove', { | ||||
| 				groupName: ajaxify.data.group.name, | ||||
| 			}, function (err) { | ||||
| 				if (!err) { | ||||
| 					ajaxify.refresh(); | ||||
| 				} else { | ||||
| 					alerts.error(err); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
|   | ||||
| @@ -1,19 +1,36 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| define('forum/groups/memberlist', ['api', 'bootbox', 'alerts'], function (api, bootbox, alerts) { | ||||
| define('forum/groups/memberlist', ['api', 'bootbox', 'alerts', 'helpers'], function (api, bootbox, alerts, helpers) { | ||||
| 	const MemberList = {}; | ||||
| 	let groupName; | ||||
| 	let templateName; | ||||
|  | ||||
| 	MemberList.init = function (_templateName) { | ||||
| 		templateName = _templateName || 'groups/details'; | ||||
| 		groupName = ajaxify.data.group.name; | ||||
|  | ||||
| 		handleMemberAdd(); | ||||
| 		handleMemberSearch(); | ||||
| 		handleMemberInfiniteScroll(); | ||||
| 	}; | ||||
|  | ||||
| 	MemberList.refresh = async function () { | ||||
| 		const { group } = await api.get(`/api/groups/${ajaxify.data.group.slug}`); | ||||
| 		const html = await parseAndTranslate(group.members); | ||||
| 		$('[component="groups/members"] tbody').html(html); | ||||
| 		$('[component="group/member/count"]').text( | ||||
| 			helpers.humanReadableNumber(group.memberCount) | ||||
| 		); | ||||
| 		$('[component="group/pending/count"]').text( | ||||
| 			helpers.humanReadableNumber(group.pending.length) | ||||
| 		); | ||||
| 		$('[component="group/invited/count"]').text( | ||||
| 			helpers.humanReadableNumber(group.invited.length) | ||||
| 		); | ||||
| 		ajaxify.data.group.members = group.members; | ||||
| 		ajaxify.data.group.memberCount = group.memberCount; | ||||
| 		ajaxify.data.group.invited = group.invited; | ||||
| 		ajaxify.data.group.pending = group.pending; | ||||
| 	}; | ||||
|  | ||||
| 	function handleMemberAdd() { | ||||
| 		$('[component="groups/members/add"]').on('click', function () { | ||||
| 			app.parseAndTranslate('admin/partials/groups/add-members', {}, function (html) { | ||||
| @@ -29,7 +46,7 @@ define('forum/groups/memberlist', ['api', 'bootbox', 'alerts'], function (api, b | ||||
| 								modal.find('[data-uid][data-selected]').each(function (index, el) { | ||||
| 									users.push(foundUsers[$(el).attr('data-uid')]); | ||||
| 								}); | ||||
| 								addUserToGroup(users, function () { | ||||
| 								addUsersToGroup(users).then(() => { | ||||
| 									modal.modal('hide'); | ||||
| 								}); | ||||
| 							}, | ||||
| @@ -65,99 +82,77 @@ define('forum/groups/memberlist', ['api', 'bootbox', 'alerts'], function (api, b | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	function addUserToGroup(users, callback) { | ||||
| 		function done() { | ||||
| 			users = users.filter(function (user) { | ||||
| 				return !$('[component="groups/members"] [data-uid="' + user.uid + '"]').length; | ||||
| 			}); | ||||
| 			parseAndTranslate(users, function (html) { | ||||
| 				$('[component="groups/members"] tbody').prepend(html); | ||||
| 			}); | ||||
| 			callback(); | ||||
| 		} | ||||
| 		const uids = users.map(function (user) { return user.uid; }); | ||||
| 		if (groupName === 'administrators') { | ||||
| 			socket.emit('admin.user.makeAdmins', uids, function (err) { | ||||
| 				if (err) { | ||||
| 					return alerts.error(err); | ||||
| 				} | ||||
| 				done(); | ||||
| 			}); | ||||
| 	async function addUsersToGroup(users) { | ||||
| 		const uids = users.map(u => u.uid); | ||||
| 		if (ajaxify.data.group.name === 'administrators') { | ||||
| 			await socket.emit('admin.user.makeAdmins', uids).catch(alerts.error); | ||||
| 		} else { | ||||
| 			Promise.all(uids.map(uid => api.put('/groups/' + ajaxify.data.group.slug + '/membership/' + uid))).then(done).catch(alerts.error); | ||||
| 			await Promise.all(uids.map(uid => api.put('/groups/' + ajaxify.data.group.slug + '/membership/' + uid))).catch(alerts.error); | ||||
| 		} | ||||
|  | ||||
| 		users = users.filter(user => !$('[component="groups/members"] [data-uid="' + user.uid + '"]').length); | ||||
| 		const html = await parseAndTranslate(users); | ||||
| 		$('[component="groups/members"] tbody').prepend(html); | ||||
| 	} | ||||
|  | ||||
| 	function handleMemberSearch() { | ||||
| 		const searchEl = $('[component="groups/members/search"]'); | ||||
| 		searchEl.on('keyup', utils.debounce(function () { | ||||
| 		searchEl.on('keyup', utils.debounce(async function () { | ||||
| 			const query = searchEl.val(); | ||||
| 			api.get(`/groups/${ajaxify.data.group.slug}/members`, { query }, function (err, results) { | ||||
| 				if (err) { | ||||
| 					return alerts.error(err); | ||||
| 				} | ||||
| 				parseAndTranslate(results.users, function (html) { | ||||
| 					$('[component="groups/members"] tbody').html(html); | ||||
| 					$('[component="groups/members"]').attr('data-nextstart', 20); | ||||
| 				}); | ||||
| 			}); | ||||
| 			const results = await api.get(`/groups/${ajaxify.data.group.slug}/members`, { query }); | ||||
| 			const html = await parseAndTranslate(results.users); | ||||
| 			$('[component="groups/members"] tbody').html(html); | ||||
| 			$('[component="groups/members"]').attr('data-nextstart', 20); | ||||
| 		}, 250)); | ||||
| 	} | ||||
|  | ||||
| 	function handleMemberInfiniteScroll() { | ||||
| 		$('[component="groups/members"]').on('scroll', function () { | ||||
| 		$('[component="groups/members"]').on('scroll', utils.debounce(function () { | ||||
| 			const $this = $(this); | ||||
| 			const bottom = ($this[0].scrollHeight - $this.innerHeight()) * 0.9; | ||||
|  | ||||
| 			if ($this.scrollTop() > bottom && !$('[component="groups/members/search"]').val()) { | ||||
| 				loadMoreMembers(); | ||||
| 			} | ||||
| 		}); | ||||
| 		}, 250)); | ||||
| 	} | ||||
|  | ||||
| 	function loadMoreMembers() { | ||||
| 	async function loadMoreMembers() { | ||||
| 		const members = $('[component="groups/members"]'); | ||||
| 		if (members.attr('loading')) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		members.attr('loading', 1); | ||||
| 		api.get(`/groups/${ajaxify.data.group.slug}/members`, { | ||||
| 		const data = await api.get(`/groups/${ajaxify.data.group.slug}/members`, { | ||||
| 			after: members.attr('data-nextstart'), | ||||
| 		}, function (err, data) { | ||||
| 			if (err) { | ||||
| 				return alerts.error(err); | ||||
| 			} | ||||
| 		}).catch(alerts.error); | ||||
|  | ||||
| 			if (data && data.users.length) { | ||||
| 				onMembersLoaded(data.users, function () { | ||||
| 					members.removeAttr('loading'); | ||||
| 					members.attr('data-nextstart', data.nextStart); | ||||
| 				}); | ||||
| 			} else { | ||||
| 				members.removeAttr('loading'); | ||||
| 			} | ||||
| 		}); | ||||
| 		if (data && data.users.length) { | ||||
| 			await onMembersLoaded(data.users); | ||||
| 			members.removeAttr('loading'); | ||||
| 			members.attr('data-nextstart', data.nextStart); | ||||
| 		} else { | ||||
| 			members.removeAttr('loading'); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function onMembersLoaded(users, callback) { | ||||
| 	async function onMembersLoaded(users) { | ||||
| 		users = users.filter(function (user) { | ||||
| 			return !$('[component="groups/members"] [data-uid="' + user.uid + '"]').length; | ||||
| 		}); | ||||
|  | ||||
| 		parseAndTranslate(users, function (html) { | ||||
| 			$('[component="groups/members"] tbody').append(html); | ||||
| 			callback(); | ||||
| 		}); | ||||
| 		const html = await parseAndTranslate(users); | ||||
| 		$('[component="groups/members"] tbody').append(html); | ||||
| 	} | ||||
|  | ||||
| 	function parseAndTranslate(users, callback) { | ||||
| 		app.parseAndTranslate(templateName, 'group.members', { | ||||
| 	async function parseAndTranslate(users) { | ||||
| 		return await app.parseAndTranslate(templateName, 'group.members', { | ||||
| 			group: { | ||||
| 				members: users, | ||||
| 				isOwner: ajaxify.data.group.isOwner, | ||||
| 			}, | ||||
| 		}, callback); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	return MemberList; | ||||
|   | ||||
| @@ -167,7 +167,7 @@ module.exports = function (utils, Benchpress, relative_path) { | ||||
| 		if (groupObj.isPending && groupObj.name !== 'administrators') { | ||||
| 			return `<button class="btn btn-warning disabled ${btnClass}"><i class="fa fa-clock-o"></i> [[groups:membership.invitation-pending]]</button>`; | ||||
| 		} else if (groupObj.isInvited) { | ||||
| 			return `<button class="btn btn-link" data-action="rejectInvite" data-group="${groupObj.displayName}">[[groups:membership.reject]]</button><button class="btn btn-success" data-action="acceptInvite" data-group="${groupObj.name}"><i class="fa fa-plus"></i> [[groups:membership.accept-invitation]]</button>`; | ||||
| 			return `<button class="btn btn-warning" data-action="rejectInvite" data-group="${groupObj.displayName}">[[groups:membership.reject]]</button><button class="btn btn-success" data-action="acceptInvite" data-group="${groupObj.name}"><i class="fa fa-plus"></i> [[groups:membership.accept-invitation]]</button>`; | ||||
| 		} else if (!groupObj.disableJoinRequests && groupObj.name !== 'administrators') { | ||||
| 			return `<button class="btn btn-success" data-action="join" data-group="${groupObj.displayName}"><i class="fa fa-plus"></i> [[groups:membership.join-group]]</button>`; | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user