forked from coracle/flotilla
Migrate Reports dialog to ActionItems dialog, add room join requests to queue
This commit is contained in:
@@ -17,7 +17,7 @@ import {
|
||||
} from "@welshman/lib"
|
||||
import {Nip01Signer} from "@welshman/signer"
|
||||
import type {UploadTask} from "@welshman/editor"
|
||||
import type {TrustedEvent, EventContent, Profile} from "@welshman/util"
|
||||
import type {TrustedEvent, EventContent, Profile, PublishedRoomMeta} from "@welshman/util"
|
||||
import {
|
||||
DELETE,
|
||||
REPORT,
|
||||
@@ -52,6 +52,7 @@ import {
|
||||
editProfile,
|
||||
createProfile,
|
||||
uniqTags,
|
||||
ManagementMethod,
|
||||
} from "@welshman/util"
|
||||
import {Pool, AuthStatus, SocketStatus} from "@welshman/net"
|
||||
import {Router} from "@welshman/router"
|
||||
@@ -72,6 +73,8 @@ import {
|
||||
getPubkeyRelays,
|
||||
userBlossomServerList,
|
||||
getThunkError,
|
||||
addRoomMember,
|
||||
manageRelay,
|
||||
} from "@welshman/app"
|
||||
import {compressFile} from "@lib/html"
|
||||
import type {SettingsValues, SpaceNotificationSettings} from "@app/core/state"
|
||||
@@ -89,6 +92,7 @@ import {
|
||||
stripPrefix,
|
||||
relaysMostlyRestricted,
|
||||
deriveSocket,
|
||||
deriveSpaceMembers,
|
||||
} from "@app/core/state"
|
||||
|
||||
// Utils
|
||||
@@ -220,8 +224,7 @@ export const attemptRelayAccess = async (url: string, claim = "") => {
|
||||
}
|
||||
}
|
||||
|
||||
const thunk = publishJoinRequest({url, claim})
|
||||
const error = await waitForThunkError(thunk)
|
||||
const error = await waitForThunkError(publishJoinRequest({url, claim}))
|
||||
|
||||
if (shouldIgnoreError(error)) return
|
||||
if (!claim && error.includes("invite code size")) return
|
||||
@@ -699,3 +702,50 @@ export const updateProfile = ({
|
||||
|
||||
return publishThunk({event, relays})
|
||||
}
|
||||
|
||||
// Admin actions
|
||||
|
||||
export const addSpaceMembers = async (
|
||||
url: string,
|
||||
pubkeys: string[],
|
||||
): Promise<string | undefined> => {
|
||||
const spaceMembers = get(deriveSpaceMembers(url))
|
||||
const results = await Promise.all(
|
||||
pubkeys
|
||||
.filter(pubkey => !spaceMembers.includes(pubkey))
|
||||
.map(pubkey =>
|
||||
manageRelay(url, {
|
||||
method: ManagementMethod.AllowPubkey,
|
||||
params: [pubkey],
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
for (const {error} of results) {
|
||||
if (error) {
|
||||
return error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const addRoomMembers = async (
|
||||
url: string,
|
||||
room: PublishedRoomMeta,
|
||||
pubkeys: string[],
|
||||
): Promise<string | undefined> => {
|
||||
const error = await addSpaceMembers(url, pubkeys)
|
||||
|
||||
if (error) {
|
||||
return error
|
||||
}
|
||||
|
||||
const errors = await Promise.all(
|
||||
pubkeys.map(pubkey => waitForThunkError(addRoomMember(url, room, pubkey))),
|
||||
)
|
||||
|
||||
for (const error of errors) {
|
||||
if (error) {
|
||||
return error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ export const makeFeed = ({
|
||||
onForwardExhausted?: () => void
|
||||
at?: number
|
||||
}) => {
|
||||
const interval = int(WEEK)
|
||||
const controller = new AbortController()
|
||||
const events = writable<TrustedEvent[]>([])
|
||||
|
||||
let interval = int(WEEK)
|
||||
let buffer: TrustedEvent[] = []
|
||||
let backwardWindow = [at - interval, at]
|
||||
let forwardWindow = [at, at + interval]
|
||||
@@ -111,13 +111,20 @@ export const makeFeed = ({
|
||||
}),
|
||||
]
|
||||
|
||||
const loadTimeframe = (since: number, until: number) => {
|
||||
request({
|
||||
const loadTimeframe = async (since: number, until: number) => {
|
||||
const events = await request({
|
||||
relays: [url],
|
||||
autoClose: true,
|
||||
signal: controller.signal,
|
||||
filters: filters.map(filter => ({...filter, since, until})),
|
||||
})
|
||||
|
||||
// If we found nothing, accelerate
|
||||
if (events.length === 0) {
|
||||
interval = Math.round(interval * 1.1)
|
||||
} else {
|
||||
interval = int(WEEK)
|
||||
}
|
||||
}
|
||||
|
||||
const backwardScroller = createScroller({
|
||||
|
||||
+49
-2
@@ -31,6 +31,7 @@ import {
|
||||
groupBy,
|
||||
remove,
|
||||
simpleCache,
|
||||
removeUndefined,
|
||||
} from "@welshman/lib"
|
||||
import type {Override} from "@welshman/lib"
|
||||
import type {RepositoryUpdate} from "@welshman/net"
|
||||
@@ -99,6 +100,7 @@ import {
|
||||
REPOST,
|
||||
GENERIC_REPOST,
|
||||
asDecryptedEvent,
|
||||
getTagValue,
|
||||
getGroupTags,
|
||||
getListTags,
|
||||
getPubkeyTagValues,
|
||||
@@ -111,6 +113,7 @@ import {
|
||||
readRoomMeta,
|
||||
makeRoomMeta,
|
||||
ManagementMethod,
|
||||
sortEventsAsc,
|
||||
sortEventsDesc,
|
||||
getAddress,
|
||||
Address,
|
||||
@@ -287,7 +290,7 @@ export const deriveRelaySignedEvents = (url: string, filters: Filter[] = [{}]) =
|
||||
derived(
|
||||
[deriveRelay(url), deriveEventsForUrl(url, filters)],
|
||||
([relay, events]) => events,
|
||||
// khatru doesn't support relay.self, uncomment when it's ready
|
||||
// TODO: khatru doesn't support relay.self, uncomment when it's ready
|
||||
// filter(spec({pubkey: relay.self}), events)
|
||||
)
|
||||
|
||||
@@ -869,7 +872,7 @@ export const deriveRoomMembers = (url: string, h: string) => {
|
||||
|
||||
const members = new Set<string>()
|
||||
|
||||
for (const event of sortBy(e => -e.created_at, $events)) {
|
||||
for (const event of sortEventsAsc($events)) {
|
||||
const pubkeys = getPubkeyTagValues(event.tags)
|
||||
|
||||
if (event.kind === ROOM_ADD_MEMBER) {
|
||||
@@ -903,6 +906,50 @@ export const deriveRoomAdmins = (url: string, h: string) => {
|
||||
})
|
||||
}
|
||||
|
||||
// Action items (admin review queue)
|
||||
// const pendingJoins: TrustedEvent[] = []
|
||||
|
||||
export const deriveSpaceActionItems = (url: string) =>
|
||||
derived(
|
||||
deriveEventsForUrl(url, [
|
||||
{
|
||||
kinds: [REPORT, ROOM_JOIN, ROOM_LEAVE, ROOM_MEMBERS],
|
||||
},
|
||||
]),
|
||||
$events => {
|
||||
const getRoomId = (e: TrustedEvent) => getTagValue(e.kind === ROOM_MEMBERS ? "d" : "h", e.tags)
|
||||
const reports = $events.filter(e => e.kind === REPORT)
|
||||
const pendingJoins: TrustedEvent[] = []
|
||||
|
||||
// Room-level join requests — most recent per pubkey+h
|
||||
for (const [h, roomEvents] of groupBy(getRoomId, $events)) {
|
||||
if (!h) continue
|
||||
|
||||
const roomJoins = roomEvents.filter(spec({kind: ROOM_JOIN}))
|
||||
const roomLeaves = roomEvents.filter(spec({kind: ROOM_LEAVE}))
|
||||
const roomMembersEvent = roomEvents.find(spec({kind: ROOM_MEMBERS}))
|
||||
const roomMembers = getTagValues("p", roomMembersEvent?.tags ?? [])
|
||||
|
||||
pendingJoins.push(
|
||||
...removeUndefined(
|
||||
Array.from(groupBy(e => e.pubkey, roomJoins).values())
|
||||
.map(sortEventsDesc)
|
||||
.map(first),
|
||||
).filter(({pubkey, created_at}) => {
|
||||
if (roomMembers.includes(pubkey)) return false
|
||||
if (gt(roomMembersEvent?.created_at, created_at)) return false
|
||||
if (roomLeaves.some(e => e.pubkey === pubkey && e.created_at > created_at))
|
||||
return false
|
||||
|
||||
return true
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return sortEventsDesc([...reports, ...pendingJoins])
|
||||
},
|
||||
)
|
||||
|
||||
// User membership status
|
||||
|
||||
export enum MembershipStatus {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
ROOM_ADD_MEMBER,
|
||||
ROOM_REMOVE_MEMBER,
|
||||
ROOM_CREATE_PERMISSION,
|
||||
ROOM_JOIN,
|
||||
RELAY_MEMBERS,
|
||||
RELAY_ADD_MEMBER,
|
||||
RELAY_REMOVE_MEMBER,
|
||||
@@ -268,7 +269,7 @@ const syncSpace = (url: string, rooms: string[]) => {
|
||||
pullAndListen({
|
||||
url,
|
||||
signal: controller.signal,
|
||||
filters: [{kinds: [ROOM_META, ROOM_ADMINS, ROOM_MEMBERS]}],
|
||||
filters: [{kinds: [ROOM_META, ROOM_ADMINS, ROOM_MEMBERS, LIVEKIT_PARTICIPANTS, ROOM_JOIN]}],
|
||||
})
|
||||
|
||||
// Room-scoped kinds: add #h tags when we know which rooms the user is in.
|
||||
@@ -317,12 +318,6 @@ const syncSpace = (url: string, rooms: string[]) => {
|
||||
})
|
||||
}
|
||||
|
||||
pullAndListen({
|
||||
url,
|
||||
signal: controller.signal,
|
||||
filters: [{kinds: [LIVEKIT_PARTICIPANTS]}],
|
||||
})
|
||||
|
||||
return () => controller.abort()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user