feat(expressions): remove the unfinished packs feature (#2250)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-31 16:34:22 +02:00
1 parent 0eef611b6d
commit b8f8d8d859
159 files changed
+42 -6863

No files matched your search

@@ -12,7 +12,7 @@ import {InviteAcceptFailedModal} from '@app/features/invite/components/alerts/In
import {InvitesDisabledModal} from '@app/features/invite/components/alerts/InvitesDisabledModal';
import {InviteAcceptModal} from '@app/features/invite/components/modals/InviteAcceptModal';
import Invites from '@app/features/invite/state/Invites';
import {isGroupDmInvite, isGuildInvite, isPackInvite} from '@app/features/invite/types/InviteTypes';
import {isGroupDmInvite, isGuildInvite} from '@app/features/invite/types/InviteTypes';
import GuildMembers from '@app/features/member/state/GuildMembers';
import {UserBannedFromGuildModal} from '@app/features/moderation/components/alerts/UserBannedFromGuildModal';
import {UserIpBannedFromGuildModal} from '@app/features/moderation/components/alerts/UserIpBannedFromGuildModal';
@@ -20,10 +20,9 @@ import * as NavigationCommands from '@app/features/navigation/commands/Navigatio
import {http} from '@app/features/platform/transport/RestTransport';
import {HttpError} from '@app/features/platform/types/EndpointError';
import {Logger} from '@app/features/platform/utils/AppLogger';
import {failureCode, failureMessage} from '@app/features/platform/utils/ResponseInspection';
import {failureCode} from '@app/features/platform/utils/ResponseInspection';
import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
import {modal} from '@app/features/ui/commands/ModalCommands';
import * as ToastCommands from '@app/features/ui/commands/ToastCommands';
import Users from '@app/features/user/state/Users';
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
import {ME} from '@fluxer/constants/src/AppConstants';
@@ -31,8 +30,7 @@ import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants';
import {GuildFeatures} from '@fluxer/constants/src/GuildConstants';
import type {Invite} from '@fluxer/schema/src/domains/invite/InviteSchemas';
import type {I18n} from '@lingui/core';
import {msg, plural} from '@lingui/core/macro';
import {Trans} from '@lingui/react/macro';
import {msg} from '@lingui/core/macro';
const ACCOUNT_VERIFICATION_REQUIRED_DESCRIPTOR = msg({
message: 'Account verification required',
@@ -44,74 +42,6 @@ const PLEASE_VERIFY_YOUR_ACCOUNT_BY_SETTING_AN_EMAIL_DESCRIPTOR = msg({
comment:
'Body of the error modal shown when an unclaimed (guest) account tries to accept a community invite. Tells the user to complete sign-up first.',
});
const EMOJI_PACK_LIMIT_REACHED_DESCRIPTOR = msg({
message: 'Emoji pack limit reached',
comment:
'Title of the error modal when installing an emoji pack via invite is rejected because the user is at the install limit.',
});
const YOU_HAVE_REACHED_THE_LIMIT_FOR_INSTALLING_EMOJI_DESCRIPTOR = msg({
message: "You're at the install limit. Remove one to add another.",
comment: 'Fallback body of the emoji pack install-limit error modal when no exact limit is known.',
});
const EMOJI_PACK_CREATION_LIMIT_REACHED_DESCRIPTOR = msg({
message: 'Emoji pack creation limit reached',
comment:
'Title of the error modal when creating a new emoji pack is rejected because the user is at the creation limit.',
});
const YOU_HAVE_REACHED_THE_LIMIT_FOR_CREATING_EMOJI_DESCRIPTOR = msg({
message: "You're at the creation limit. Delete one to create another.",
comment: 'Fallback body of the emoji pack creation-limit error modal when no exact limit is known.',
});
const STICKER_PACK_LIMIT_REACHED_DESCRIPTOR = msg({
message: 'Sticker pack limit reached',
comment:
'Title of the error modal when installing a sticker pack via invite is rejected because the user is at the install limit.',
});
const YOU_HAVE_REACHED_THE_LIMIT_FOR_INSTALLING_STICKER_DESCRIPTOR = msg({
message: "You're at the install limit. Remove one to add another.",
comment: 'Fallback body of the sticker pack install-limit error modal when no exact limit is known.',
});
const STICKER_PACK_CREATION_LIMIT_REACHED_DESCRIPTOR = msg({
message: 'Sticker pack creation limit reached',
comment:
'Title of the error modal when creating a new sticker pack is rejected because the user is at the creation limit.',
});
const YOU_HAVE_REACHED_THE_LIMIT_FOR_CREATING_STICKER_DESCRIPTOR = msg({
message: "You're at the creation limit. Delete one to create another.",
comment: 'Fallback body of the sticker pack creation-limit error modal when no exact limit is known.',
});
const CANNOT_INSTALL_EMOJI_PACK_DESCRIPTOR = msg({
message: 'Cannot install emoji pack',
comment: 'Title of the error modal when installing an emoji pack via invite is rejected for missing permissions.',
});
const CANNOT_INSTALL_STICKER_PACK_DESCRIPTOR = msg({
message: 'Cannot install sticker pack',
comment: 'Title of the error modal when installing a sticker pack via invite is rejected for missing permissions.',
});
const YOU_DON_T_HAVE_PERMISSION_TO_INSTALL_THIS_DESCRIPTOR = msg({
message: "You can't install this emoji pack.",
comment: 'Body of the error modal when installing an emoji pack via invite is rejected for missing permissions.',
});
const YOU_DON_T_HAVE_PERMISSION_TO_INSTALL_THIS_2_DESCRIPTOR = msg({
message: "You can't install this sticker pack.",
comment: 'Body of the error modal when installing a sticker pack via invite is rejected for missing permissions.',
});
const UNABLE_TO_INSTALL_EMOJI_PACK_DESCRIPTOR = msg({
message: 'Unable to install emoji pack',
comment: 'Generic title for the error modal when an emoji pack invite fails with an unrecognized error code.',
});
const UNABLE_TO_INSTALL_STICKER_PACK_DESCRIPTOR = msg({
message: 'Unable to install sticker pack',
comment: 'Generic title for the error modal when a sticker pack invite fails with an unrecognized error code.',
});
const FAILED_TO_INSTALL_THIS_EMOJI_PACK_PLEASE_TRY_DESCRIPTOR = msg({
message: "Couldn't install this emoji pack. Try again later.",
comment: 'Generic body for the error modal when an emoji pack invite fails with an unrecognized error code.',
});
const FAILED_TO_INSTALL_THIS_STICKER_PACK_PLEASE_TRY_DESCRIPTOR = msg({
message: "Couldn't install this sticker pack. Try again later.",
comment: 'Generic body for the error modal when a sticker pack invite fails with an unrecognized error code.',
});
const logger = new Logger('Invites');
const ACCEPT_INVITE_BODY = {} as Invite;
const isUnclaimedAccountInviteError = (code?: string): boolean => {
@@ -133,21 +63,6 @@ function guildInviteFeatures(invite: Invite | null): Array<string> {
return invite && isGuildInvite(invite) && Array.isArray(invite.guild.features) ? invite.guild.features : [];
}
function showPackInstalledToast(invite: Invite): void {
if (!isPackInvite(invite)) {
return;
}
ToastCommands.createToast({
type: 'success',
children:
invite.pack.type === 'emoji' ? (
<Trans>Emoji pack {invite.pack.name} has been installed.</Trans>
) : (
<Trans>Sticker pack {invite.pack.name} has been installed.</Trans>
),
});
}
function removeInviteIfMissing(code: string, responseErr: HttpError | null, errorCode?: string): void {
if (responseErr?.status === 404 || errorCode === APIErrorCodes.UNKNOWN_INVITE) {
logger.debug(`Invite ${code} not found, removing from store`);
@@ -283,11 +198,6 @@ export async function acceptAndTransitionToChannel(code: string, i18n: I18n): Pr
if (!invite) {
throw new Error(`Invite ${code} returned no data`);
}
if (isPackInvite(invite)) {
await accept(code);
showPackInstalledToast(invite);
return;
}
if (isGroupDmInvite(invite)) {
const channelId = invite.channel.id;
logger.debug(`Accepting group DM invite ${code} and opening channel ${channelId}`);
@@ -296,7 +206,7 @@ export async function acceptAndTransitionToChannel(code: string, i18n: I18n): Pr
return;
}
if (!isGuildInvite(invite)) {
throw new Error(`Invite ${code} is not a guild, group DM, or pack invite`);
throw new Error(`Invite ${code} is not a guild or group DM invite`);
}
const channelId = invite.channel.id;
const inviteTargetAllowed = shouldOpenInviteGuildChannel(invite.channel.type);
@@ -324,9 +234,6 @@ export async function acceptAndTransitionToChannel(code: string, i18n: I18n): Pr
const errorCode = failureCode(error);
logger.error(`Failed to accept invite and transition for code ${code}:`, error);
removeInviteIfMissing(code, responseErr, errorCode);
if (handlePackInviteError({invite, errorCode, responseErr, i18n})) {
throw error;
}
showGuildInviteAcceptFailure(i18n, invite, errorCode, responseErr);
throw error;
}
@@ -342,169 +249,6 @@ export async function openAcceptModal(code: string): Promise<void> {
);
}
interface HandlePackInviteErrorParams {
invite: Invite | null;
errorCode?: string;
responseErr?: HttpError | null;
i18n: I18n;
}
interface PackLimitPayload {
packType?: 'emoji' | 'sticker';
limit?: number;
action?: 'create' | 'install';
}
const getPackLimitPayload = (responseErr?: HttpError | null): PackLimitPayload | null => {
const body = responseErr?.body;
if (!body || typeof body !== 'object') return null;
const record = body as Record<string, unknown>;
const data = record.data;
if (!data || typeof data !== 'object') return null;
const dataRecord = data as Record<string, unknown>;
const limit = dataRecord.limit;
const packType = dataRecord.pack_type;
const action = dataRecord.action;
return {
packType: packType === 'emoji' || packType === 'sticker' ? packType : undefined,
limit: typeof limit === 'number' ? limit : undefined,
action: action === 'create' || action === 'install' ? action : undefined,
};
};
const buildPackLimitStrings = (
i18n: I18n,
packType: 'emoji' | 'sticker',
action: 'install' | 'create',
limit?: number,
): {title: string; message: string} => {
switch (packType) {
case 'emoji': {
switch (action) {
case 'install': {
const title = i18n._(EMOJI_PACK_LIMIT_REACHED_DESCRIPTOR);
const message =
typeof limit === 'number'
? plural(
{count: limit},
{
one: "You're at the limit of # installed emoji pack. Remove one to add another.",
other: "You're at the limit of # installed emoji packs. Remove one to add another.",
},
)
: i18n._(YOU_HAVE_REACHED_THE_LIMIT_FOR_INSTALLING_EMOJI_DESCRIPTOR);
return {title, message};
}
default: {
const title = i18n._(EMOJI_PACK_CREATION_LIMIT_REACHED_DESCRIPTOR);
const message =
typeof limit === 'number'
? plural(
{count: limit},
{
one: "You're at the limit of # emoji pack. Delete one to create another.",
other: "You're at the limit of # emoji packs. Delete one to create another.",
},
)
: i18n._(YOU_HAVE_REACHED_THE_LIMIT_FOR_CREATING_EMOJI_DESCRIPTOR);
return {title, message};
}
}
}
default: {
switch (action) {
case 'install': {
const title = i18n._(STICKER_PACK_LIMIT_REACHED_DESCRIPTOR);
const message =
typeof limit === 'number'
? plural(
{count: limit},
{
one: "You're at the limit of # installed sticker pack. Remove one to add another.",
other: "You're at the limit of # installed sticker packs. Remove one to add another.",
},
)
: i18n._(YOU_HAVE_REACHED_THE_LIMIT_FOR_INSTALLING_STICKER_DESCRIPTOR);
return {title, message};
}
default: {
const title = i18n._(STICKER_PACK_CREATION_LIMIT_REACHED_DESCRIPTOR);
const message =
typeof limit === 'number'
? plural(
{count: limit},
{
one: "You're at the limit of # sticker pack. Delete one to create another.",
other: "You're at the limit of # sticker packs. Delete one to create another.",
},
)
: i18n._(YOU_HAVE_REACHED_THE_LIMIT_FOR_CREATING_STICKER_DESCRIPTOR);
return {title, message};
}
}
}
}
};
export function handlePackInviteError(params: HandlePackInviteErrorParams): boolean {
const {invite, errorCode, responseErr, i18n} = params;
if (!invite || !isPackInvite(invite)) {
return false;
}
const isEmojiPack = invite.pack.type === 'emoji';
const cannotInstallTitle = isEmojiPack
? i18n._(CANNOT_INSTALL_EMOJI_PACK_DESCRIPTOR)
: i18n._(CANNOT_INSTALL_STICKER_PACK_DESCRIPTOR);
const cannotInstallMessage = isEmojiPack
? i18n._(YOU_DON_T_HAVE_PERMISSION_TO_INSTALL_THIS_DESCRIPTOR)
: i18n._(YOU_DON_T_HAVE_PERMISSION_TO_INSTALL_THIS_2_DESCRIPTOR);
const defaultTitle = isEmojiPack
? i18n._(UNABLE_TO_INSTALL_EMOJI_PACK_DESCRIPTOR)
: i18n._(UNABLE_TO_INSTALL_STICKER_PACK_DESCRIPTOR);
const defaultMessage = isEmojiPack
? i18n._(FAILED_TO_INSTALL_THIS_EMOJI_PACK_PLEASE_TRY_DESCRIPTOR)
: i18n._(FAILED_TO_INSTALL_THIS_STICKER_PACK_PLEASE_TRY_DESCRIPTOR);
if (errorCode === APIErrorCodes.MISSING_ACCESS) {
ModalCommands.push(
modal(() => (
<GenericErrorModal
title={cannotInstallTitle}
message={cannotInstallMessage}
data-flx="invite.invite-commands.handle-pack-invite-error.generic-error-modal"
/>
)),
);
return true;
}
if (errorCode === APIErrorCodes.MAX_PACKS) {
const payload = getPackLimitPayload(responseErr);
const packType = payload?.packType ?? invite.pack.type;
const action = payload?.action ?? 'install';
const limit = payload?.limit;
const {title, message} = buildPackLimitStrings(i18n, packType, action, limit);
ModalCommands.push(
modal(() => (
<GenericErrorModal
title={title}
message={message}
data-flx="invite.invite-commands.handle-pack-invite-error.generic-error-modal--2"
/>
)),
);
return true;
}
const fallbackMessage = responseErr ? failureMessage(responseErr) : null;
ModalCommands.push(
modal(() => (
<GenericErrorModal
title={defaultTitle}
message={fallbackMessage || defaultMessage}
data-flx="invite.invite-commands.handle-pack-invite-error.generic-error-modal--3"
/>
)),
);
return true;
}
export async function create(
channelId: string,
params?: {max_age?: number; max_uses?: number; temporary?: boolean},
@@ -1,50 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {Endpoints} from '@app/features/app/constants/Endpoints';
import {http} from '@app/features/platform/transport/RestTransport';
import {Logger} from '@app/features/platform/utils/AppLogger';
import type {PackInviteMetadataResponse} from '@fluxer/schema/src/domains/invite/InviteSchemas';
const logger = new Logger('PackInvites');
export interface CreatePackInviteParams {
packId: string;
maxUses?: number;
maxAge?: number;
unique?: boolean;
}
interface PackInviteRequestBody {
max_uses: number;
max_age: number;
unique: boolean;
}
function packInviteRequestBody(params: CreatePackInviteParams): PackInviteRequestBody {
return {
max_uses: params.maxUses ?? 0,
max_age: params.maxAge ?? 0,
unique: params.unique ?? false,
};
}
async function requestPackInvite(params: CreatePackInviteParams): Promise<PackInviteMetadataResponse> {
const response = await http.post<PackInviteMetadataResponse>(Endpoints.PACK_INVITES(params.packId), {
body: packInviteRequestBody(params),
});
return response.body;
}
function rethrowPackInviteFailure(packId: string, error: unknown): never {
logger.error(`Failed to create invite for pack ${packId}:`, error);
throw error;
}
export async function createInvite(params: CreatePackInviteParams): Promise<PackInviteMetadataResponse> {
try {
logger.debug(`Creating invite for pack ${params.packId}`);
return await requestPackInvite(params);
} catch (error) {
rethrowPackInviteFailure(params.packId, error);
}
}