This commit is contained in:
pa
2026-03-09 02:49:59 +09:00
parent 64b27ce7f1
commit 90a17bb0ba
39 changed files with 9487 additions and 4384 deletions

View File

@@ -0,0 +1,485 @@
import { ref } from 'vue';
import { toast } from 'vue-sonner';
import { debounce } from '../../../shared/utils';
import * as workerTimers from 'worker-timers';
/**
* Composable for group moderation data fetching, member management,
* searching, sorting and filtering.
*
* @param {object} deps
* @param {import('vue').Ref} deps.groupMemberModeration - store ref
* @param {import('vue').Ref} deps.currentUser - store ref
* @param {Function} deps.applyGroupMember - store action
* @param {Function} deps.handleGroupMember - store action
* @param {object} deps.tables - reactive table data objects
* @param {object} deps.tables.members
* @param {object} deps.tables.bans
* @param {object} deps.tables.invites
* @param {object} deps.tables.joinRequests
* @param {object} deps.tables.blocked
* @param {object} deps.tables.logs
* @param {object} deps.selection - from useGroupModerationSelection
* @param {object} deps.selection.selectedUsers
* @param {Function} deps.selection.setSelectedUsers
* @param {object} deps.groupRequest - API module
* @param {object} deps.userRequest - API module
*/
export function useGroupModerationData(deps) {
const {
groupMemberModeration,
currentUser,
applyGroupMember,
handleGroupMember,
tables,
selection,
groupRequest,
userRequest
} = deps;
const isGroupMembersLoading = ref(false);
const isGroupMembersDone = ref(false);
const memberFilter = ref({
id: null,
name: 'dialog.group.members.filters.everyone'
});
const memberSortOrder = ref({
id: '',
name: 'dialog.group.members.sorting.joined_at_desc',
value: 'joinedAt:desc'
});
const memberSearch = ref('');
const members = ref([]);
const loadMoreGroupMembersParams = ref({
n: 100,
offset: 0,
groupId: '',
sort: 'joinedAt:desc',
roleId: ''
});
// ── Members ──────────────────────────────────────────────────
async function getGroupMembers() {
members.value = [];
isGroupMembersDone.value = false;
loadMoreGroupMembersParams.value = {
sort: 'joinedAt:desc',
roleId: '',
n: 100,
offset: 0,
groupId: groupMemberModeration.value.id
};
if (memberSortOrder.value.value) {
loadMoreGroupMembersParams.value.sort = memberSortOrder.value.value;
}
if (memberFilter.value.id !== null) {
loadMoreGroupMembersParams.value.roleId = memberFilter.value.id;
}
await groupRequest
.getGroupMember({
groupId: groupMemberModeration.value.id,
userId: currentUser.value.id
})
.then((args) => {
args.ref = applyGroupMember(args.json);
if (args.json) {
args.json.user = currentUser.value;
if (memberFilter.value.id === null) {
members.value.push(args.json);
}
}
return args;
});
await loadMoreGroupMembers();
}
async function loadMoreGroupMembers() {
if (isGroupMembersDone.value || isGroupMembersLoading.value) {
return;
}
const params = loadMoreGroupMembersParams.value;
if (params.roleId === '') {
delete params.roleId;
}
memberSearch.value = '';
isGroupMembersLoading.value = true;
await groupRequest
.getGroupMembers(params)
.finally(() => {
isGroupMembersLoading.value = false;
})
.then((args) => {
for (const json of args.json) {
handleGroupMember({
json,
params: { groupId: args.params.groupId }
});
}
for (let i = 0; i < args.json.length; i++) {
const member = args.json[i];
if (member.userId === currentUser.value.id) {
if (members.value.length > 0 && members.value[0].userId === currentUser.value.id) {
members.value.splice(0, 1);
}
break;
}
}
if (args.json.length < params.n) {
isGroupMembersDone.value = true;
}
members.value = [...members.value, ...args.json];
tables.members.data = members.value.map((member) => ({
...member,
$selected: Boolean(selection.selectedUsers[member.userId])
}));
params.offset += params.n;
return args;
})
.catch((err) => {
isGroupMembersDone.value = true;
throw err;
});
}
async function loadAllGroupMembers() {
if (isGroupMembersLoading.value) {
return;
}
await getGroupMembers();
while (groupMemberModeration.value.visible && !isGroupMembersDone.value) {
isGroupMembersLoading.value = true;
await new Promise((resolve) => {
workerTimers.setTimeout(resolve, 1000);
});
isGroupMembersLoading.value = false;
await loadMoreGroupMembers();
}
}
async function setGroupMemberSortOrder(sortOrder) {
if (memberSortOrder.value === sortOrder) {
return;
}
memberSortOrder.value = sortOrder;
await getGroupMembers();
}
async function setGroupMemberFilter(filter) {
if (memberFilter.value === filter) {
return;
}
memberFilter.value = filter;
await getGroupMembers();
}
function groupMembersSearch() {
if (memberSearch.value.length < 3) {
tables.members.data = [];
isGroupMembersLoading.value = false;
return;
}
isGroupMembersLoading.value = true;
debounce(groupMembersSearchDebounced, 200)();
}
function groupMembersSearchDebounced() {
const groupId = groupMemberModeration.value.id;
const search = memberSearch.value;
tables.members.data = [];
if (memberSearch.value.length < 3) {
return;
}
isGroupMembersLoading.value = true;
groupRequest
.getGroupMembersSearch({
groupId,
query: search,
n: 100,
offset: 0
})
.then((args) => {
for (const json of args.json.results) {
handleGroupMember({
json,
params: { groupId: args.params.groupId }
});
}
if (groupId === args.params.groupId) {
tables.members.data = args.json.results.map((member) => ({
...member,
$selected: Boolean(selection.selectedUsers[member.userId])
}));
}
})
.finally(() => {
isGroupMembersLoading.value = false;
});
}
// ── Bans ─────────────────────────────────────────────────────
async function getAllGroupBans(groupId) {
tables.bans.data = [];
const params = { groupId, n: 100, offset: 0 };
const count = 50; // 5000 max
isGroupMembersLoading.value = true;
const fetchedBans = [];
try {
for (let i = 0; i < count; i++) {
const args = await groupRequest.getGroupBans(params);
if (args && args.json) {
if (groupMemberModeration.value.id !== args.params.groupId) {
continue;
}
args.json.forEach((json) => {
const ref = applyGroupMember(json);
fetchedBans.push(ref);
});
if (args.json.length < params.n) {
break;
}
params.offset += params.n;
} else {
break;
}
if (!groupMemberModeration.value.visible) {
break;
}
}
tables.bans.data = fetchedBans;
} catch {
toast.error('Failed to get group bans');
} finally {
isGroupMembersLoading.value = false;
}
}
// ── Invites / Join Requests / Blocked ────────────────────────
async function getAllGroupInvites(groupId) {
tables.invites.data = [];
const params = { groupId, n: 100, offset: 0 };
const count = 50; // 5000 max
isGroupMembersLoading.value = true;
let newData = [];
try {
for (let i = 0; i < count; i++) {
const args = await groupRequest.getGroupInvites(params);
if (args) {
if (groupMemberModeration.value.id !== args.params.groupId) {
return;
}
for (const json of args.json) {
const ref = applyGroupMember(json);
newData.push(ref);
}
}
params.offset += params.n;
if (args.json.length < params.n) {
break;
}
if (!groupMemberModeration.value.visible) {
break;
}
}
tables.invites.data = newData;
} catch {
toast.error('Failed to get group invites');
} finally {
isGroupMembersLoading.value = false;
}
}
async function getAllGroupJoinRequests(groupId) {
tables.joinRequests.data = [];
const params = { groupId, n: 100, offset: 0, blocked: false };
const count = 50; // 5000 max
isGroupMembersLoading.value = true;
let newData = [];
try {
for (let i = 0; i < count; i++) {
const args = await groupRequest.getGroupJoinRequests(params);
if (groupMemberModeration.value.id !== args.params.groupId) {
return;
}
for (const json of args.json) {
const ref = applyGroupMember(json);
newData.push(ref);
}
params.offset += params.n;
if (args.json.length < params.n) {
break;
}
if (!groupMemberModeration.value.visible) {
break;
}
}
tables.joinRequests.data = newData;
} catch {
toast.error('Failed to get group join requests');
} finally {
isGroupMembersLoading.value = false;
}
}
async function getAllGroupBlockedRequests(groupId) {
tables.blocked.data = [];
const params = { groupId, n: 100, offset: 0, blocked: true };
const count = 50; // 5000
isGroupMembersLoading.value = true;
let newData = [];
try {
for (let i = 0; i < count; i++) {
const args = await groupRequest.getGroupJoinRequests(params);
if (groupMemberModeration.value.id !== args.params.groupId) {
return;
}
for (const json of args.json) {
const ref = applyGroupMember(json);
newData.push(ref);
}
params.offset += params.n;
if (args.json.length < params.n) {
break;
}
if (!groupMemberModeration.value.visible) {
break;
}
}
tables.blocked.data = newData;
} catch {
toast.error('Failed to get group join requests');
} finally {
isGroupMembersLoading.value = false;
}
}
async function getAllGroupInvitesAndJoinRequests(groupId) {
try {
await Promise.all([
getAllGroupInvites(groupId),
getAllGroupJoinRequests(groupId),
getAllGroupBlockedRequests(groupId)
]);
} catch (error) {
console.error('Error fetching group invites/requests:', error);
}
}
// ── Logs ─────────────────────────────────────────────────────
async function getAllGroupLogs(groupId, eventTypes = []) {
tables.logs.data = [];
const params = { groupId, n: 100, offset: 0 };
if (eventTypes.length) {
params.eventTypes = eventTypes;
}
const count = 50; // 5000 max
isGroupMembersLoading.value = true;
let newData = [];
try {
for (let i = 0; i < count; i++) {
const args = await groupRequest.getGroupLogs(params);
if (args) {
if (groupMemberModeration.value.id !== args.params.groupId) {
continue;
}
for (const json of args.json.results) {
const existsInData = newData.some((dataItem) => dataItem.id === json.id);
if (!existsInData) {
newData.push(json);
}
}
}
params.offset += params.n;
if (!args.json.hasNext) {
break;
}
if (!groupMemberModeration.value.visible) {
break;
}
}
tables.logs.data = newData;
} catch {
toast.error('Failed to get group logs');
} finally {
isGroupMembersLoading.value = false;
}
}
// ── User Selection ───────────────────────────────────────────
async function addGroupMemberToSelection(userId) {
const D = groupMemberModeration.value;
let member = {};
const memberArgs = await groupRequest.getGroupMember({ groupId: D.id, userId });
if (memberArgs && memberArgs.json) {
member = applyGroupMember(memberArgs.json);
}
if (member && member.user) {
selection.setSelectedUsers(member.userId, member);
return;
}
const userArgs = await userRequest.getCachedUser({ userId });
member.userId = userArgs.json.id;
member.user = userArgs.json;
member.displayName = userArgs.json.displayName;
selection.setSelectedUsers(member.userId, member);
}
async function selectGroupMemberUserId(userIdInput) {
if (!userIdInput) return;
const regexUserId = /usr_[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}/g;
let match;
const userIdList = new Set();
while ((match = regexUserId.exec(userIdInput)) !== null) {
userIdList.add(match[0]);
}
if (userIdList.size === 0) {
userIdList.add(userIdInput);
}
const promises = [];
userIdList.forEach((userId) => {
promises.push(addGroupMemberToSelection(userId));
});
await Promise.allSettled(promises);
}
// ── Reset ────────────────────────────────────────────────────
function resetData() {
tables.members.data = [];
tables.bans.data = [];
tables.invites.data = [];
tables.joinRequests.data = [];
tables.blocked.data = [];
tables.logs.data = [];
memberSearch.value = '';
members.value = [];
isGroupMembersDone.value = false;
}
return {
isGroupMembersLoading,
isGroupMembersDone,
memberFilter,
memberSortOrder,
memberSearch,
members,
loadAllGroupMembers,
getGroupMembers,
setGroupMemberSortOrder,
setGroupMemberFilter,
groupMembersSearch,
selectGroupMemberUserId,
addGroupMemberToSelection,
getAllGroupBans,
getAllGroupLogs,
getAllGroupInvites,
getAllGroupJoinRequests,
getAllGroupBlockedRequests,
getAllGroupInvitesAndJoinRequests,
resetData
};
}