mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
feat(threads): thread and forum UI on web (#3252)
This commit is contained in:
1 parent
3f4160b138
commit
ba59a13149
258 files changed
+63052
-15599
No files matched your search
+2
@@ -42,6 +42,7 @@ export interface GuildSummary {
|
||||
name: string | null;
|
||||
icon: string | null;
|
||||
permissions?: string | null;
|
||||
threads_active?: true;
|
||||
}
|
||||
|
||||
export interface GuildWithPermissions {
|
||||
@@ -49,6 +50,7 @@ export interface GuildWithPermissions {
|
||||
name: string;
|
||||
icon: string | null;
|
||||
canAuthorizeBotInvite: boolean;
|
||||
threadsActive: boolean;
|
||||
}
|
||||
|
||||
export interface SignedInView {
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@
|
||||
|
||||
import type {GuildComboboxOption} from '@app/features/app/components/dialogs/shared/GuildComboboxRenderers';
|
||||
import {Endpoints} from '@app/features/app/constants/Endpoints';
|
||||
import {parseRequestedBotPermissions} from '@app/features/auth/components/pages/oauth_authorize_page/hooks/usePermissionSelection';
|
||||
import {
|
||||
type GuildSummary,
|
||||
type GuildWithPermissions,
|
||||
@@ -82,6 +83,7 @@ export function mapBotGuilds(
|
||||
userPermissions: permissionsValue,
|
||||
requestedPermissions,
|
||||
}),
|
||||
threadsActive: guild.threads_active === true,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -165,8 +167,9 @@ export function useBotGuilds(enabled: boolean, requestedPermissions: bigint): Bo
|
||||
return {status, guilds, options, labelById, error};
|
||||
}
|
||||
|
||||
export function useBotInviteDestinations(enabled: boolean, requestedPermissions: bigint): BotInviteDestinationsResult {
|
||||
export function useBotInviteDestinations(enabled: boolean, rawPermissions: string | null): BotInviteDestinationsResult {
|
||||
const {i18n} = useLingui();
|
||||
const requestedPermissions = useMemo(() => parseRequestedBotPermissions(rawPermissions, false), [rawPermissions]);
|
||||
const i18nRef = useRef(i18n);
|
||||
i18nRef.current = i18n;
|
||||
const [status, setStatus] = useState<BotGuildsStatus>(enabled ? 'loading' : 'idle');
|
||||
|
||||
+23
-15
@@ -5,9 +5,10 @@ import {
|
||||
type BotPermissionOption,
|
||||
formatBotPermissionsQuery,
|
||||
getAllBotPermissions,
|
||||
getPermissionFlagByKey,
|
||||
} from '@app/features/permissions/utils/PermissionUtils';
|
||||
import {normalizeBotInvitePermissions} from '@fluxer/constants/src/BotPermissionUtils';
|
||||
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {THREAD_AWARE_ALL_PERMISSIONS} from '@fluxer/constants/src/ThreadPermissionUtils';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {useCallback, useMemo, useState} from 'react';
|
||||
|
||||
@@ -22,25 +23,32 @@ export interface PermissionSelection {
|
||||
toBitfield: () => string | undefined;
|
||||
}
|
||||
|
||||
export function usePermissionSelection(rawPermissions: string | null): PermissionSelection {
|
||||
export function parseRequestedBotPermissions(rawPermissions: string | null, threadsActive: boolean): bigint {
|
||||
if (!rawPermissions) return 0n;
|
||||
try {
|
||||
const parsed = BigInt(rawPermissions);
|
||||
if (parsed < 0n) return 0n;
|
||||
return threadsActive
|
||||
? normalizeBotInvitePermissions(parsed, THREAD_AWARE_ALL_PERMISSIONS)
|
||||
: normalizeBotInvitePermissions(parsed);
|
||||
} catch (err) {
|
||||
logger.warn('Failed to parse requested permissions', err);
|
||||
return 0n;
|
||||
}
|
||||
}
|
||||
|
||||
export function usePermissionSelection(rawPermissions: string | null, threadsActive: boolean): PermissionSelection {
|
||||
const {i18n} = useLingui();
|
||||
const options = useMemo(() => getAllBotPermissions(i18n), [i18n.locale]);
|
||||
const requestedBitfield = useMemo(() => {
|
||||
if (!rawPermissions) return 0n;
|
||||
try {
|
||||
const parsed = BigInt(rawPermissions);
|
||||
if (parsed < 0n) return 0n;
|
||||
return normalizeBotInvitePermissions(parsed);
|
||||
} catch (err) {
|
||||
logger.warn('Failed to parse requested permissions', err);
|
||||
return 0n;
|
||||
}
|
||||
}, [rawPermissions]);
|
||||
const options = useMemo(() => getAllBotPermissions(i18n, {threads: threadsActive}), [i18n.locale, threadsActive]);
|
||||
const requestedBitfield = useMemo(
|
||||
() => parseRequestedBotPermissions(rawPermissions, threadsActive),
|
||||
[rawPermissions, threadsActive],
|
||||
);
|
||||
const requestedKeys = useMemo<ReadonlyArray<string>>(() => {
|
||||
if (!rawPermissions) return [];
|
||||
return options
|
||||
.filter((opt) => {
|
||||
const flag = Permissions[opt.id as keyof typeof Permissions];
|
||||
const flag = getPermissionFlagByKey(opt.id);
|
||||
return flag != null && (requestedBitfield & flag) === flag;
|
||||
})
|
||||
.map((opt) => opt.id);
|
||||
|
||||
+10
-2
@@ -31,6 +31,7 @@ import type {BotPermissionOption} from '@app/features/permissions/utils/Permissi
|
||||
import {http} from '@app/features/platform/transport/RestTransport';
|
||||
import {HttpError} from '@app/features/platform/types/EndpointError';
|
||||
import {failureMessage} from '@app/features/platform/utils/ResponseInspection';
|
||||
import ThreadGuilds from '@app/features/threads/state/ThreadGuilds';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
||||
@@ -183,8 +184,15 @@ export function useAuthorizeFlow(options: UseAuthorizeFlowOptions = {}): Authori
|
||||
const initialMissingClientId = !params;
|
||||
const publicAppState = useOAuthPublicApp(params?.clientId ?? null);
|
||||
const scopeSelection = useScopeSelection(scopes);
|
||||
const permissionSelection = usePermissionSelection(params?.permissions ?? null);
|
||||
const destinations = useBotInviteDestinations(hasBotScope, permissionSelection.requestedBitfield);
|
||||
const destinations = useBotInviteDestinations(hasBotScope, params?.permissions ?? null);
|
||||
const selectedDestinationTarget = parseBotInviteDestinationKey(selectedDestinationKey);
|
||||
const selectedGuildId = selectedDestinationTarget?.kind === 'guild' ? selectedDestinationTarget.id : null;
|
||||
const permissionSelection = usePermissionSelection(
|
||||
params?.permissions ?? null,
|
||||
selectedGuildId != null &&
|
||||
(ThreadGuilds.isActive(selectedGuildId) ||
|
||||
destinations.guilds.some((guild) => guild.id === selectedGuildId && guild.threadsActive)),
|
||||
);
|
||||
const initialReviewStep: ReviewStep = includeAccountStep ? 'account' : 'scopes';
|
||||
const preFetchValidationError = useMemo(() => {
|
||||
if (!params) return i18n._(MISSING_CLIENT_ID_DESCRIPTOR);
|
||||
|
||||
Reference in new issue
Block a user