From f88b0f69b2d14a6512e9874e5385db2f60517a64 Mon Sep 17 00:00:00 2001 From: Hampus Date: Wed, 12 Aug 2026 15:40:33 +0200 Subject: [PATCH] feat: remove Canary Testers guild integration (#1572) --- fluxer_admin/openapi-admin.json | 1 - fluxer_api/src/api/app/ControllerRegistry.ts | 2 - .../canary_tester/CanaryTesterController.ts | 61 ------ fluxer_api/src/api/openapi/openapi.json | 69 ------ .../rate_limit_configs/UserRateLimitConfig.ts | 4 - .../layout/app_layout/AppLayoutHooks.ts | 27 --- .../layout/app_layout/AppLayoutTypes.ts | 2 - .../app_layout/CanaryTesterDismissal.ts | 17 -- .../layout/app_layout/NagbarContainer.tsx | 9 - .../nagbars/CanaryTesterCtaNagbar.tsx | 167 --------------- .../app/config/I18nDisplayConstants.ts | 1 - .../src/features/app/constants/Endpoints.ts | 1 - .../commands/CanaryTesterCommands.ts | 32 --- .../hooks/useCanaryTesterDmInvite.ts | 26 --- .../direct_message/CanaryTesterDmItem.tsx | 198 ------------------ .../direct_message/DirectMessageList.tsx | 6 - .../devtools/components/NagbarControls.ts | 17 -- .../src/features/i18n/locales/ar/messages.po | 140 +++---------- .../src/features/i18n/locales/bg/messages.po | 140 +++---------- .../src/features/i18n/locales/cs/messages.po | 140 +++---------- .../src/features/i18n/locales/da/messages.po | 140 +++---------- .../src/features/i18n/locales/de/messages.po | 140 +++---------- .../src/features/i18n/locales/el/messages.po | 140 +++---------- .../features/i18n/locales/en-GB/messages.po | 141 +++---------- .../features/i18n/locales/en-US/messages.po | 140 +++---------- .../features/i18n/locales/es-419/messages.po | 140 +++---------- .../features/i18n/locales/es-ES/messages.po | 140 +++---------- .../src/features/i18n/locales/fi/messages.po | 140 +++---------- .../src/features/i18n/locales/fr/messages.po | 140 +++---------- .../src/features/i18n/locales/he/messages.po | 140 +++---------- .../src/features/i18n/locales/hi/messages.po | 140 +++---------- .../src/features/i18n/locales/hr/messages.po | 140 +++---------- .../src/features/i18n/locales/hu/messages.po | 140 +++---------- .../src/features/i18n/locales/id/messages.po | 140 +++---------- .../src/features/i18n/locales/it/messages.po | 140 +++---------- .../src/features/i18n/locales/ja/messages.po | 140 +++---------- .../src/features/i18n/locales/ko/messages.po | 140 +++---------- .../src/features/i18n/locales/lt/messages.po | 140 +++---------- .../src/features/i18n/locales/nl/messages.po | 140 +++---------- .../src/features/i18n/locales/no/messages.po | 140 +++---------- .../src/features/i18n/locales/pl/messages.po | 140 +++---------- .../features/i18n/locales/pt-BR/messages.po | 140 +++---------- .../src/features/i18n/locales/ro/messages.po | 140 +++---------- .../src/features/i18n/locales/ru/messages.po | 140 +++---------- .../features/i18n/locales/sv-SE/messages.po | 140 +++---------- .../src/features/i18n/locales/th/messages.po | 140 +++---------- .../src/features/i18n/locales/tr/messages.po | 140 +++---------- .../src/features/i18n/locales/uk/messages.po | 140 +++---------- .../src/features/i18n/locales/vi/messages.po | 140 +++---------- .../features/i18n/locales/zh-CN/messages.po | 140 +++---------- .../features/i18n/locales/zh-TW/messages.po | 140 +++---------- fluxer_app/src/features/ui/state/Nagbar.ts | 11 - packages/constants/src/ApiErrorCodes.ts | 1 - packages/constants/src/AppConstants.ts | 2 - .../auth/EmailVerificationRequiredError.ts | 6 - packages/errors/src/i18n/ErrorCodeMappings.ts | 1 - 56 files changed, 1156 insertions(+), 4266 deletions(-) delete mode 100644 fluxer_api/src/api/canary_tester/CanaryTesterController.ts delete mode 100644 fluxer_app/src/features/app/components/layout/app_layout/CanaryTesterDismissal.ts delete mode 100644 fluxer_app/src/features/app/components/layout/app_layout/nagbars/CanaryTesterCtaNagbar.tsx delete mode 100644 fluxer_app/src/features/canary_tester/commands/CanaryTesterCommands.ts delete mode 100644 fluxer_app/src/features/canary_tester/hooks/useCanaryTesterDmInvite.ts delete mode 100644 fluxer_app/src/features/channel/components/direct_message/CanaryTesterDmItem.tsx diff --git a/fluxer_admin/openapi-admin.json b/fluxer_admin/openapi-admin.json index 6bf04aa5a..cdc342149 100644 --- a/fluxer_admin/openapi-admin.json +++ b/fluxer_admin/openapi-admin.json @@ -9813,7 +9813,6 @@ "DISCRIMINATOR_REQUIRED", "EMAIL_SERVICE_NOT_TESTABLE", "EMAIL_VERIFICATION_REQUIRED", - "CANARY_TESTER_EMAIL_VERIFICATION_REQUIRED", "DIRECT_MESSAGE_EMAIL_VERIFICATION_REQUIRED", "FRIEND_REQUEST_EMAIL_VERIFICATION_REQUIRED", "GUILD_CREATION_EMAIL_VERIFICATION_REQUIRED", diff --git a/fluxer_api/src/api/app/ControllerRegistry.ts b/fluxer_api/src/api/app/ControllerRegistry.ts index 09802264d..4db31caa0 100644 --- a/fluxer_api/src/api/app/ControllerRegistry.ts +++ b/fluxer_api/src/api/app/ControllerRegistry.ts @@ -4,7 +4,6 @@ import {registerAdminControllers} from '../admin/controllers/index'; import {AuthController} from '../auth/AuthController'; import {BlueskyOAuthController} from '../bluesky/BlueskyOAuthController'; import {Config} from '../Config'; -import {CanaryTesterController} from '../canary_tester/CanaryTesterController'; import {ChannelController} from '../channel/ChannelController'; import type {APIConfig} from '../config/APIConfig'; import {ConnectionController} from '../connection/ConnectionController'; @@ -67,7 +66,6 @@ export function registerControllers(routes: HonoApp, config: APIConfig): void { TestHarnessController(routes); } UserController(routes); - CanaryTesterController(routes); registerInboundSmsWebhook(routes); WebhookController(routes); OAuth2Controller(routes); diff --git a/fluxer_api/src/api/canary_tester/CanaryTesterController.ts b/fluxer_api/src/api/canary_tester/CanaryTesterController.ts deleted file mode 100644 index 9814a17f6..000000000 --- a/fluxer_api/src/api/canary_tester/CanaryTesterController.ts +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -import {CANARY_TESTER_MIN_ACCOUNT_AGE_MS, CANARY_TESTERS_GUILD_ID} from '@fluxer/constants/src/AppConstants'; -import {JoinSourceTypes} from '@fluxer/constants/src/GuildConstants'; -import {CanaryTesterEmailVerificationRequiredError} from '@fluxer/errors/src/domains/auth/EmailVerificationRequiredError'; -import {AccountTooNewForGuildError} from '@fluxer/errors/src/domains/guild/AccountTooNewForGuildError'; -import {AccountSuspiciousActivityError} from '@fluxer/errors/src/domains/user/AccountSuspiciousActivityError'; -import {SuccessResponse} from '@fluxer/schema/src/domains/common/CommonParamSchemas'; -import {extractTimestampFromSnowflakeAsDateBigInt} from '@fluxer/snowflake/src/SnowflakeUtils'; -import {createGuildID} from '../BrandedTypes'; -import {DefaultUserOnly, LoginRequired} from '../middleware/AuthMiddleware'; -import {RateLimitMiddleware} from '../middleware/RateLimitMiddleware'; -import {OpenAPI} from '../middleware/ResponseTypeMiddleware'; -import {RateLimitConfigs} from '../RateLimitConfig'; -import type {HonoApp} from '../types/HonoEnv'; -import {getEffectiveSuspiciousFlags} from '../user/UserHelpers'; - -export function CanaryTesterController(app: HonoApp) { - app.post( - '/users/@me/canary-tester/join', - RateLimitMiddleware(RateLimitConfigs.USER_CANARY_TESTER_JOIN), - LoginRequired, - DefaultUserOnly, - OpenAPI({ - operationId: 'join_canary_testers', - summary: 'Join the canary testers guild', - description: - 'Adds the authenticated user to the hardcoded Fluxer Testers guild used for canary feedback. Restricted to non-bot users with verified email, an account at least 30 minutes old, no effective suspicious-activity flags, and not banned from the target guild. Rate-limited; surfaced via the canary nagbar.', - responseSchema: SuccessResponse, - statusCode: 200, - security: ['bearerToken', 'sessionToken'], - tags: ['Users'], - }), - async (ctx) => { - const user = ctx.get('user'); - const userId = user.id; - if (!user.email || !user.emailVerified) { - throw new CanaryTesterEmailVerificationRequiredError(); - } - const accountCreatedAt = extractTimestampFromSnowflakeAsDateBigInt(BigInt(userId.toString())); - if (Date.now() - accountCreatedAt.getTime() < CANARY_TESTER_MIN_ACCOUNT_AGE_MS) { - throw new AccountTooNewForGuildError(); - } - const effectiveFlags = getEffectiveSuspiciousFlags(user); - if (effectiveFlags !== 0) { - throw new AccountSuspiciousActivityError(effectiveFlags); - } - const guildService = ctx.get('guildService'); - const requestCache = ctx.get('requestCache'); - const guildId = createGuildID(BigInt(CANARY_TESTERS_GUILD_ID)); - await guildService.members.addUserToGuild({ - userId, - guildId, - sendJoinMessage: true, - requestCache, - joinSourceType: JoinSourceTypes.INSTANT_INVITE, - }); - return ctx.json({success: true} satisfies SuccessResponse); - }, - ); -} diff --git a/fluxer_api/src/api/openapi/openapi.json b/fluxer_api/src/api/openapi/openapi.json index 7728f31eb..47ad5b025 100644 --- a/fluxer_api/src/api/openapi/openapi.json +++ b/fluxer_api/src/api/openapi/openapi.json @@ -16130,73 +16130,6 @@ } } }, - "/users/@me/canary-tester/join": { - "post": { - "operationId": "join_canary_testers", - "summary": "Join the canary testers guild", - "tags": ["Users"], - "responses": { - "200": { - "description": "Success", - "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SuccessResponse"}}} - }, - "400": { - "description": "Bad Request - The request was malformed or contained invalid data", - "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}} - }, - "401": { - "description": "Unauthorized - Authentication is required or the token is invalid", - "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}} - }, - "403": { - "description": "Forbidden - You do not have permission to perform this action", - "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}} - }, - "429": { - "description": "Too Many Requests - You are being rate limited", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "code": {"type": "string", "enum": ["RATE_LIMITED"]}, - "message": {"type": "string"}, - "retry_after": {"type": "number", "description": "Seconds to wait before retrying"}, - "global": {"type": "boolean", "description": "Whether this is a global rate limit"} - }, - "required": ["code", "message", "retry_after"] - } - } - }, - "headers": { - "Retry-After": { - "description": "Number of seconds to wait before retrying (only on 429)", - "schema": {"type": "integer"} - }, - "X-RateLimit-Limit": { - "description": "The number of requests that can be made in the current window", - "schema": {"type": "integer"} - }, - "X-RateLimit-Remaining": { - "description": "The number of remaining requests that can be made", - "schema": {"type": "integer"} - }, - "X-RateLimit-Reset": { - "description": "Unix timestamp when the rate limit resets", - "schema": {"type": "integer"} - } - } - }, - "500": { - "description": "Internal Server Error - An unexpected error occurred", - "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}} - } - }, - "x-mint": {"metadata": {"title": "Join the canary testers guild"}}, - "description": "Adds the authenticated user to the hardcoded Fluxer Testers guild used for canary feedback. Restricted to non-bot users with verified email, an account at least 30 minutes old, no effective suspicious-activity flags, and not banned from the target guild. Rate-limited; surfaced via the canary nagbar.", - "security": [{"sessionToken": []}] - } - }, "/users/@me/channels": { "get": { "operationId": "list_private_channels", @@ -25206,7 +25139,6 @@ "DISCRIMINATOR_REQUIRED", "EMAIL_SERVICE_NOT_TESTABLE", "EMAIL_VERIFICATION_REQUIRED", - "CANARY_TESTER_EMAIL_VERIFICATION_REQUIRED", "DIRECT_MESSAGE_EMAIL_VERIFICATION_REQUIRED", "FRIEND_REQUEST_EMAIL_VERIFICATION_REQUIRED", "GUILD_CREATION_EMAIL_VERIFICATION_REQUIRED", @@ -25532,7 +25464,6 @@ "Email verification is required for this action.", "Email verification is required for this action.", "Email verification is required for this action.", - "Email verification is required for this action.", "Registration is closed on this instance.", "This registration is waiting for admin approval.", "This registration request was rejected.", diff --git a/fluxer_api/src/api/rate_limit_configs/UserRateLimitConfig.ts b/fluxer_api/src/api/rate_limit_configs/UserRateLimitConfig.ts index 5cdbf1479..a7c872d88 100644 --- a/fluxer_api/src/api/rate_limit_configs/UserRateLimitConfig.ts +++ b/fluxer_api/src/api/rate_limit_configs/UserRateLimitConfig.ts @@ -216,10 +216,6 @@ export const UserRateLimitConfigs = { bucket: 'user:harvest:download', config: {limit: 10, windowMs: ms('1 minute')}, } as RouteRateLimitConfig, - USER_CANARY_TESTER_JOIN: { - bucket: 'user:canary_tester:join', - config: {limit: 3, windowMs: ms('1 hour')}, - } as RouteRateLimitConfig, USER_ENTRANCE_SOUND_LIST: { bucket: 'user:entrance_sound:list', config: {limit: 30, windowMs: ms('1 minute')}, diff --git a/fluxer_app/src/features/app/components/layout/app_layout/AppLayoutHooks.ts b/fluxer_app/src/features/app/components/layout/app_layout/AppLayoutHooks.ts index 98151d07c..45511877f 100644 --- a/fluxer_app/src/features/app/components/layout/app_layout/AppLayoutHooks.ts +++ b/fluxer_app/src/features/app/components/layout/app_layout/AppLayoutHooks.ts @@ -6,16 +6,13 @@ import { type NagbarState, NagbarType, } from '@app/features/app/components/layout/app_layout/AppLayoutTypes'; -import {isCanaryTesterCtaDismissed} from '@app/features/app/components/layout/app_layout/CanaryTesterDismissal'; import {isScheduledMaintenanceNagbarDismissed} from '@app/features/app/components/layout/app_layout/ScheduledMaintenanceDismissal'; -import Config from '@app/features/app/config/Config'; import Initialization from '@app/features/app/state/Initialization'; import RuntimeConfig from '@app/features/app/state/RuntimeConfig'; import Authentication from '@app/features/auth/state/Authentication'; import Channels from '@app/features/channel/state/Channels'; import DeveloperOptions from '@app/features/devtools/state/DeveloperOptions'; import GatewayConnection from '@app/features/gateway/transport/GatewayConnection'; -import GuildMembers from '@app/features/member/state/GuildMembers'; import * as NotificationUtils from '@app/features/notification/utils/NotificationUtils'; import NativePermission from '@app/features/permissions/system/state/NativePermission'; import StreamerMode from '@app/features/streamer_mode/state/StreamerMode'; @@ -33,12 +30,10 @@ import { getVoiceSessionRestoreSnapshotKey, isRestorableVoiceChannelType, } from '@app/features/voice/utils/VoiceSessionRestoreUtils'; -import {CANARY_TESTER_MIN_ACCOUNT_AGE_MS, CANARY_TESTERS_GUILD_ID} from '@fluxer/constants/src/AppConstants'; import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants'; import {PRIVACY_POLICY_LAST_UPDATED, TERMS_OF_SERVICE_LAST_UPDATED} from '@fluxer/constants/src/PolicyConstants'; import {UserPremiumTypes} from '@fluxer/constants/src/UserConstants'; import {MS_PER_DAY} from '@fluxer/date_utils/src/DateConstants'; -import * as SnowflakeUtils from '@fluxer/snowflake/src/SnowflakeUtils'; import {useEffect, useMemo, useState} from 'react'; function sortNagbarsByPriority(a: NagbarState, b: NagbarState): number { @@ -168,21 +163,6 @@ export const useNagbarConditions = (): NagbarConditions => { if (isSelfHosted) return false; return !nagbarState.guildMembershipCtaDismissed; })(); - void nagbarState.canaryTesterCtaDismissalVersion; - const canShowCanaryTesterCta = (() => { - if (nagbarState.forceHideCanaryTesterCta) return false; - if (nagbarState.forceCanaryTesterCta) return true; - if (!user) return false; - if (isSelfHosted) return false; - if (Config.PUBLIC_RELEASE_CHANNEL !== 'canary') return false; - if (user.bot) return false; - if (!user.email || !user.verified) return false; - if (user.requiredActions && user.requiredActions.length > 0) return false; - if (SnowflakeUtils.age(user.id) < CANARY_TESTER_MIN_ACCOUNT_AGE_MS) return false; - if (GuildMembers.getMember(CANARY_TESTERS_GUILD_ID, user.id)) return false; - if (isCanaryTesterCtaDismissed()) return false; - return true; - })(); void nagbarState.scheduledMaintenanceDismissalVersion; const canShowScheduledMaintenance = (() => { if (nagbarState.forceHideScheduledMaintenance) return false; @@ -285,7 +265,6 @@ export const useNagbarConditions = (): NagbarConditions => { canShowVisionaryMfa, canShowVoiceSessionRestore, needsTermsAcceptance, - canShowCanaryTesterCta, canShowLinuxInputAccess, canShowSoftwareEncoder, canShowStreamerMode, @@ -384,12 +363,6 @@ export const useActiveNagbars = (conditions: NagbarConditions): Array = observer(({nagbar data-flx="app.app-layout.nagbar-container.voice-session-restore-nagbar" /> ); - case NagbarType.CANARY_TESTER_CTA: - return ( - - ); case NagbarType.LINUX_INPUT_ACCESS: return ( ( - - )), - ); -} -const JOIN_TESTERS_COMMUNITY_DESCRIPTOR = msg({ - message: 'Join {testersCommunityName}', - comment: 'CTA button label for joining the canary testers community. testersCommunityName is a community name.', -}); -const CANARY_TESTER_CTA_MESSAGE_DESCRIPTOR = msg({ - message: - "You're on {canaryReleaseName}. Join {testersCommunityName} to submit bug reports and feedback about upcoming builds.", - comment: - 'Nagbar body inviting canary release users to join the testers community. {canaryReleaseName} is the release channel name and {testersCommunityName} is the community name.', -}); -export const CanaryTesterCtaNagbar = observer(({isMobile}: {isMobile: boolean}) => { - const {i18n} = useLingui(); - const canaryReleaseChannelName = CANARY_RELEASE_CHANNEL_NAME; - const canaryTestersCommunityName = CANARY_TESTERS_COMMUNITY_NAME; - const [isSubmitting, setIsSubmitting] = useState(false); - const handleDismiss = () => { - dismissCanaryTesterCtaNagbar(); - NagbarState.bumpCanaryTesterCtaDismissed(); - }; - const handleJoin = async () => { - if (isSubmitting) return; - setIsSubmitting(true); - try { - await CanaryTesterCommands.joinCanaryTesters(); - ToastCommands.createToast({ - type: 'success', - children: i18n._(JOINED_TESTERS_COMMUNITY_DESCRIPTOR, { - testersCommunityName: canaryTestersCommunityName, - canaryReleaseName: canaryReleaseChannelName, - }), - }); - handleDismiss(); - } catch (error) { - showCanaryJoinErrorModal(error, canaryTestersCommunityName); - } finally { - setIsSubmitting(false); - } - }; - return ( - - - {i18n._(JOIN_TESTERS_COMMUNITY_DESCRIPTOR, {testersCommunityName: canaryTestersCommunityName})} - - } - data-flx="app.app-layout.nagbars.canary-tester-cta-nagbar.nagbar-content" - /> - - ); -}); diff --git a/fluxer_app/src/features/app/config/I18nDisplayConstants.ts b/fluxer_app/src/features/app/config/I18nDisplayConstants.ts index f3feb1018..727e53be9 100644 --- a/fluxer_app/src/features/app/config/I18nDisplayConstants.ts +++ b/fluxer_app/src/features/app/config/I18nDisplayConstants.ts @@ -10,7 +10,6 @@ export const DESKTOP_PRODUCT_NAME = `${PRODUCT_NAME} Desktop`; export const PRODUCT_API_NAME = `${PRODUCT_NAME} API`; export const PRODUCT_HQ_COMMUNITY_NAME = `${PRODUCT_NAME} HQ`; export const CANARY_RELEASE_CHANNEL_NAME = `${PRODUCT_NAME} Canary`; -export const CANARY_TESTERS_COMMUNITY_NAME = `${PRODUCT_NAME} Testers`; export const VOICE_PROVIDER_NAME = 'LiveKit'; export const PAYMENT_PROVIDER_NAME = 'Stripe'; export const BLUESKY_PROVIDER_NAME = 'Bluesky'; diff --git a/fluxer_app/src/features/app/constants/Endpoints.ts b/fluxer_app/src/features/app/constants/Endpoints.ts index bf4ba58f1..3e6a27186 100644 --- a/fluxer_app/src/features/app/constants/Endpoints.ts +++ b/fluxer_app/src/features/app/constants/Endpoints.ts @@ -114,7 +114,6 @@ export const Endpoints = { GUILD_WEBHOOKS: (guildId: string) => `/guilds/${guildId}/webhooks`, GUILD_AUDIT_LOGS: (guildId: string) => `/guilds/${guildId}/audit-logs`, INVITE: (code: string) => `/invites/${code}`, - CANARY_TESTER_JOIN: '/users/@me/canary-tester/join', GIFT: (code: string) => `/gifts/${code}`, GIFT_REDEEM: (code: string) => `/gifts/${code}/redeem`, USER_GIFTS: '/users/@me/gifts', diff --git a/fluxer_app/src/features/canary_tester/commands/CanaryTesterCommands.ts b/fluxer_app/src/features/canary_tester/commands/CanaryTesterCommands.ts deleted file mode 100644 index e93bf2444..000000000 --- a/fluxer_app/src/features/canary_tester/commands/CanaryTesterCommands.ts +++ /dev/null @@ -1,32 +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'; - -const logger = new Logger('CanaryTester'); - -interface CanaryTesterJoinResponse { - success: true; -} - -const JOIN_REQUEST_BODY = {}; - -async function submitCanaryTesterJoin(): Promise { - await http.post(Endpoints.CANARY_TESTER_JOIN, { - body: JOIN_REQUEST_BODY, - }); -} - -function rethrowJoinFailure(error: unknown): never { - logger.error('Failed to join Fluxer Testers guild:', error); - throw error; -} - -export async function joinCanaryTesters(): Promise { - try { - await submitCanaryTesterJoin(); - } catch (error) { - rethrowJoinFailure(error); - } -} diff --git a/fluxer_app/src/features/canary_tester/hooks/useCanaryTesterDmInvite.ts b/fluxer_app/src/features/canary_tester/hooks/useCanaryTesterDmInvite.ts deleted file mode 100644 index 3d2a889e8..000000000 --- a/fluxer_app/src/features/canary_tester/hooks/useCanaryTesterDmInvite.ts +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -import {isCanaryTesterCtaDismissed} from '@app/features/app/components/layout/app_layout/CanaryTesterDismissal'; -import Config from '@app/features/app/config/Config'; -import RuntimeConfig from '@app/features/app/state/RuntimeConfig'; -import GuildMembers from '@app/features/member/state/GuildMembers'; -import Nagbar from '@app/features/ui/state/Nagbar'; -import Users from '@app/features/user/state/Users'; -import {CANARY_TESTER_MIN_ACCOUNT_AGE_MS, CANARY_TESTERS_GUILD_ID} from '@fluxer/constants/src/AppConstants'; -import * as SnowflakeUtils from '@fluxer/snowflake/src/SnowflakeUtils'; - -export function useCanaryTesterDmInviteVisible(): boolean { - const user = Users.currentUser; - void Nagbar.canaryTesterCtaDismissalVersion; - if (Nagbar.forceHideCanaryTesterCta) return false; - if (Nagbar.forceCanaryTesterCta) return true; - if (!user) return false; - if (RuntimeConfig.isSelfHosted()) return false; - if (Config.PUBLIC_RELEASE_CHANNEL !== 'canary') return false; - if (user.bot) return false; - if (!user.email || !user.verified) return false; - if (user.requiredActions && user.requiredActions.length > 0) return false; - if (SnowflakeUtils.age(user.id) < CANARY_TESTER_MIN_ACCOUNT_AGE_MS) return false; - if (GuildMembers.getMember(CANARY_TESTERS_GUILD_ID, user.id)) return false; - return isCanaryTesterCtaDismissed(); -} diff --git a/fluxer_app/src/features/channel/components/direct_message/CanaryTesterDmItem.tsx b/fluxer_app/src/features/channel/components/direct_message/CanaryTesterDmItem.tsx deleted file mode 100644 index 3f363405e..000000000 --- a/fluxer_app/src/features/channel/components/direct_message/CanaryTesterDmItem.tsx +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -import {ConfirmModal} from '@app/features/app/components/dialogs/ConfirmModal'; -import {LongPressable} from '@app/features/app/components/LongPressable'; -import {dismissCanaryTesterCtaNagbar} from '@app/features/app/components/layout/app_layout/CanaryTesterDismissal'; -import { - CANARY_RELEASE_CHANNEL_NAME, - CANARY_TESTERS_COMMUNITY_NAME, -} from '@app/features/app/config/I18nDisplayConstants'; -import * as CanaryTesterCommands from '@app/features/canary_tester/commands/CanaryTesterCommands'; -import {useCanaryTesterDmInviteVisible} from '@app/features/canary_tester/hooks/useCanaryTesterDmInvite'; -import {showChannelErrorModal} from '@app/features/channel/components/alerts/ChannelErrorModalUtils'; -import styles from '@app/features/channel/components/direct_message/DirectMessageList.module.css'; -import {isKeyboardActivationKey} from '@app/features/input/utils/KeyboardUtils'; -import {HttpError} from '@app/features/platform/types/EndpointError'; -import {failureMessage} 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 FocusRing from '@app/features/ui/focus_ring/FocusRing'; -import NagbarState from '@app/features/ui/state/Nagbar'; -import {msg} from '@lingui/core/macro'; -import {Trans, useLingui} from '@lingui/react/macro'; -import {FlaskIcon} from '@phosphor-icons/react'; -import {observer} from 'mobx-react-lite'; -import {useCallback} from 'react'; - -const JOIN_TESTERS_DESCRIPTOR = msg({ - message: 'Join {testersCommunityName}', - comment: - 'Button or menu action label in the channel and chat canary tester dm item. Keep it concise. Preserve {testersCommunityName}; it is inserted by code.', -}); -const TESTERS_IS_A_COMMUNITY_FOR_CANARY_DESCRIPTOR = msg({ - message: - '{testersCommunityName} is a community for {canaryReleaseName} users to share bug reports and feedback that helps shape upcoming releases. Join to chat with the team and other testers.', - comment: - 'Label in the channel and chat canary tester dm item. Preserve {testersCommunityName} and {canaryReleaseName}; they are inserted by code.', -}); -const COULD_NOT_JOIN_TESTERS_PLEASE_TRY_AGAIN_DESCRIPTOR = msg({ - message: 'Could not join {testersCommunityName}. Try again later.', - comment: - 'Description text in the channel and chat canary tester dm item. Preserve {testersCommunityName}; it is inserted by code.', -}); -const COULD_NOT_JOIN_TESTERS_TITLE_DESCRIPTOR = msg({ - message: 'Could not join {testersCommunityName}', - comment: - 'Title of the error modal shown when joining the canary testers community fails. Preserve {testersCommunityName}; it is inserted by code.', -}); -const JoinConfirmModal = observer(() => { - const {i18n} = useLingui(); - const testersCommunityName = CANARY_TESTERS_COMMUNITY_NAME; - const canaryReleaseName = CANARY_RELEASE_CHANNEL_NAME; - return ( - { - try { - await CanaryTesterCommands.joinCanaryTesters(); - dismissCanaryTesterCtaNagbar(); - NagbarState.bumpCanaryTesterCtaDismissed(); - ToastCommands.createToast({ - type: 'success', - children: ( - - You joined {testersCommunityName}. Thanks for testing {canaryReleaseName}! - - ), - }); - } catch (error) { - const message = error instanceof HttpError ? failureMessage(error) : undefined; - showChannelErrorModal({ - title: i18n._(COULD_NOT_JOIN_TESTERS_TITLE_DESCRIPTOR, {testersCommunityName}), - message: - message ?? - i18n._(COULD_NOT_JOIN_TESTERS_PLEASE_TRY_AGAIN_DESCRIPTOR, { - testersCommunityName, - }), - dataFlx: 'channel.direct-message.canary-tester-dm-item.join-confirm-modal.error.generic-error-modal', - }); - throw error; - } - }} - data-flx="channel.direct-message.canary-tester-dm-item.join-confirm-modal.confirm-modal" - /> - ); -}); - -function openJoinConfirm(): void { - ModalCommands.push( - modal(() => ( - - )), - ); -} - -export const CanaryTesterDmItemDesktop = observer(() => { - const visible = useCanaryTesterDmInviteVisible(); - const {i18n} = useLingui(); - const testersCommunityName = CANARY_TESTERS_COMMUNITY_NAME; - const handleClick = useCallback(() => openJoinConfirm(), []); - if (!visible) return null; - return ( - - - - ); -}); -export const CanaryTesterDmItemMobile = observer(() => { - const visible = useCanaryTesterDmInviteVisible(); - const {i18n} = useLingui(); - const testersCommunityName = CANARY_TESTERS_COMMUNITY_NAME; - const handleClick = useCallback(() => openJoinConfirm(), []); - if (!visible) return null; - return ( - - { - if (!isKeyboardActivationKey(event.key)) return; - event.preventDefault(); - handleClick(); - }} - onLongPress={handleClick} - className={styles.mobilePersonalNotesButton} - role="button" - tabIndex={0} - aria-label={i18n._(JOIN_TESTERS_DESCRIPTOR, {testersCommunityName})} - data-flx="channel.direct-message.canary-tester-dm-item.canary-tester-dm-item-mobile.mobile-personal-notes-button.click" - > -
-
- -
-
- - {i18n._(JOIN_TESTERS_DESCRIPTOR, {testersCommunityName})} - -
-
-
-
- ); -}); diff --git a/fluxer_app/src/features/channel/components/direct_message/DirectMessageList.tsx b/fluxer_app/src/features/channel/components/direct_message/DirectMessageList.tsx index a466e6556..e77ba5f09 100644 --- a/fluxer_app/src/features/channel/components/direct_message/DirectMessageList.tsx +++ b/fluxer_app/src/features/channel/components/direct_message/DirectMessageList.tsx @@ -7,10 +7,6 @@ import {PREMIUM_PRODUCT_NAME} from '@app/features/app/config/I18nDisplayConstant import {useRovingFocusList} from '@app/features/app/hooks/useRovingFocusList'; import {PersonalNotesPurgeFailedModal} from '@app/features/channel/components/alerts/PersonalNotesPurgeFailedModal'; import {CreateDMBottomSheet} from '@app/features/channel/components/bottomsheets/CreateDMBottomSheet'; -import { - CanaryTesterDmItemDesktop, - CanaryTesterDmItemMobile, -} from '@app/features/channel/components/direct_message/CanaryTesterDmItem'; import styles from '@app/features/channel/components/direct_message/DirectMessageList.module.css'; import {getDmRouteChannelId} from '@app/features/channel/components/direct_message/DMListHelpers'; import {DMListItem} from '@app/features/channel/components/direct_message/DMListItem'; @@ -384,7 +380,6 @@ export const DMList = observer(() => { )} - {showMobilePlutoniumButton && (