mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
fix(app): point self-hosted users at their instance admins (#2921)
This commit is contained in:
1 parent
ea9f83a443
commit
f2785941aa
41 files changed
+5023
-4010
No files matched your search
@@ -3,6 +3,7 @@
|
||||
import {ConfirmModal} from '@app/features/app/components/dialogs/ConfirmModal';
|
||||
import * as Modal from '@app/features/app/components/dialogs/Modal';
|
||||
import {PRODUCT_NAME, SUPPORT_EMAIL} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
|
||||
import * as AuthenticationCommands from '@app/features/auth/commands/AuthenticationCommands';
|
||||
import {
|
||||
type RequiredActionFlow,
|
||||
@@ -20,6 +21,7 @@ import {
|
||||
ESCAPE_SUCCESS_EMAIL_REMAINS_TOAST_DESCRIPTOR,
|
||||
ESCAPE_SUCCESS_TOAST_DESCRIPTOR,
|
||||
ESCAPE_UNAVAILABLE_DESCRIPTOR,
|
||||
ESCAPE_UNAVAILABLE_SELF_HOSTED_DESCRIPTOR,
|
||||
GET_NEW_CODE_DESCRIPTOR,
|
||||
NEXT_DESCRIPTOR,
|
||||
RESEND_EMAIL_DESCRIPTOR,
|
||||
@@ -75,7 +77,7 @@ import * as UserCommands from '@app/features/user/commands/UserCommands';
|
||||
import Users from '@app/features/user/state/Users';
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import type {PhoneGateEscapePreviewResponse} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
import type {MessageDescriptor} from '@lingui/core';
|
||||
import type {I18n, MessageDescriptor} from '@lingui/core';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import type React from 'react';
|
||||
@@ -185,6 +187,12 @@ function getIntroDescription(flow: RequiredActionFlow | null, isEmailBounced: bo
|
||||
return STEP_INTRO_GENERIC_DESCRIPTION_DESCRIPTOR;
|
||||
}
|
||||
|
||||
function escapeUnavailableMessage(i18n: I18n): string {
|
||||
return RuntimeConfig.isSelfHosted()
|
||||
? i18n._(ESCAPE_UNAVAILABLE_SELF_HOSTED_DESCRIPTOR)
|
||||
: i18n._(ESCAPE_UNAVAILABLE_DESCRIPTOR, {supportEmail: SUPPORT_EMAIL});
|
||||
}
|
||||
|
||||
const RequiredActionModal: React.FC<{mock?: boolean}> = observer(({mock = false}) => {
|
||||
const {i18n} = useLingui();
|
||||
const user = Users.currentUser;
|
||||
@@ -394,7 +402,7 @@ const RequiredActionModal: React.FC<{mock?: boolean}> = observer(({mock = false}
|
||||
const remaining = updated.required_actions;
|
||||
const phoneRemains = remaining.some((action) => action.includes('PHONE'));
|
||||
if (phoneRemains) {
|
||||
setActionError(i18n._(ESCAPE_UNAVAILABLE_DESCRIPTOR, {supportEmail: SUPPORT_EMAIL}));
|
||||
setActionError(escapeUnavailableMessage(i18n));
|
||||
return;
|
||||
}
|
||||
ToastCommands.success(
|
||||
@@ -404,7 +412,7 @@ const RequiredActionModal: React.FC<{mock?: boolean}> = observer(({mock = false}
|
||||
);
|
||||
} catch (error) {
|
||||
if (failureCode(error) === APIErrorCodes.PHONE_GATE_ESCAPE_UNAVAILABLE) {
|
||||
setActionError(i18n._(ESCAPE_UNAVAILABLE_DESCRIPTOR, {supportEmail: SUPPORT_EMAIL}));
|
||||
setActionError(escapeUnavailableMessage(i18n));
|
||||
} else if (hadGuildsToLeave) {
|
||||
setActionError(i18n._(ESCAPE_FAILED_PARTIAL_DESCRIPTOR));
|
||||
} else {
|
||||
|
||||
+21
@@ -265,6 +265,11 @@ export const SUPPORT_LINK_LABEL_DESCRIPTOR = msg({
|
||||
message: 'Contact support',
|
||||
comment: 'Link label in the required-action modal. Opens an email to Fluxer support.',
|
||||
});
|
||||
export const INSTANCE_ADMIN_CONTACT_DESCRIPTOR = msg({
|
||||
message: 'Contact the administrators of this instance for help.',
|
||||
comment:
|
||||
'Shown in the required-action modal on a self-hosted instance in place of the Contact support link. The instance is run by its own administrators, not by the app maker.',
|
||||
});
|
||||
export const ALT_ROUTES_TITLE_DESCRIPTOR = msg({
|
||||
message: "If you can't verify by SMS",
|
||||
comment:
|
||||
@@ -276,6 +281,12 @@ export const ALT_ROUTES_HUMAN_REVIEW_DESCRIPTOR = msg({
|
||||
comment:
|
||||
'Body of the human-review option in the required-action modal, rendered directly above the Contact support link. Sets the expectation that a reply takes time.',
|
||||
});
|
||||
export const ALT_ROUTES_HUMAN_REVIEW_SELF_HOSTED_DESCRIPTOR = msg({
|
||||
message:
|
||||
'Ask the administrators of this instance to review your account. This instance is run independently, and only its administrators can change your account.',
|
||||
comment:
|
||||
'Body of the human-review option in the required-action modal on a self-hosted instance. The instance is run by its own administrators, not by the app maker.',
|
||||
});
|
||||
export const ESCAPE_BUTTON_DESCRIPTOR = msg({
|
||||
message: 'Set this check aside',
|
||||
comment:
|
||||
@@ -328,6 +339,11 @@ export const ESCAPE_CONFIRM_SUPPORT_DESCRIPTOR = msg({
|
||||
comment:
|
||||
'Last line of the set-aside confirmation in the required-action modal. supportEmail is inserted by code. The self-serve route never closes the support route.',
|
||||
});
|
||||
export const ESCAPE_CONFIRM_SUPPORT_SELF_HOSTED_DESCRIPTOR = msg({
|
||||
message: 'You can still ask the administrators of this instance for a human review at any time.',
|
||||
comment:
|
||||
'Last line of the set-aside confirmation in the required-action modal on a self-hosted instance. The instance is run by its own administrators, not by the app maker.',
|
||||
});
|
||||
export const ESCAPE_CONFIRM_PRIMARY_NO_GUILDS_DESCRIPTOR = msg({
|
||||
message: 'Set this check aside',
|
||||
comment:
|
||||
@@ -353,6 +369,11 @@ export const ESCAPE_UNAVAILABLE_DESCRIPTOR = msg({
|
||||
comment:
|
||||
'Shown in the required-action modal when setting the check aside is refused, and when it reported success but the phone requirement is still in force. supportEmail is inserted by code, and the address is named in full because the support link may have scrolled out of view.',
|
||||
});
|
||||
export const ESCAPE_UNAVAILABLE_SELF_HOSTED_DESCRIPTOR = msg({
|
||||
message: 'This is not available on your account right now. Ask the administrators of this instance to review it.',
|
||||
comment:
|
||||
'Shown in the required-action modal on a self-hosted instance when setting the check aside is refused, and when it reported success but the phone requirement is still in force.',
|
||||
});
|
||||
export const ESCAPE_FAILED_NOTHING_CHANGED_DESCRIPTOR = msg({
|
||||
message: 'That did not go through. Nothing changed. Try again.',
|
||||
comment:
|
||||
|
||||
+7
-1
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {SUPPORT_EMAIL} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
|
||||
import {
|
||||
ESCAPE_CONFIRM_EMAIL_REMAINS_DESCRIPTOR,
|
||||
ESCAPE_CONFIRM_LEAVE_DESCRIPTOR,
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
ESCAPE_CONFIRM_PRIMARY_NO_GUILDS_DESCRIPTOR,
|
||||
ESCAPE_CONFIRM_PRIMARY_WITH_GUILDS_DESCRIPTOR,
|
||||
ESCAPE_CONFIRM_SUPPORT_DESCRIPTOR,
|
||||
ESCAPE_CONFIRM_SUPPORT_SELF_HOSTED_DESCRIPTOR,
|
||||
ESCAPE_CONFIRM_TITLE_NO_GUILDS_DESCRIPTOR,
|
||||
ESCAPE_CONFIRM_TITLE_WITH_GUILDS_DESCRIPTOR,
|
||||
ESCAPE_HINT_NO_GUILDS_DESCRIPTOR,
|
||||
@@ -60,7 +62,11 @@ export function buildPhoneGateEscapeConfirmCopy(i18n: I18n, plan: PhoneGateEscap
|
||||
if (plan.emailStepRemains) {
|
||||
bodyLines.push(i18n._(ESCAPE_CONFIRM_EMAIL_REMAINS_DESCRIPTOR));
|
||||
}
|
||||
bodyLines.push(i18n._(ESCAPE_CONFIRM_SUPPORT_DESCRIPTOR, {supportEmail: SUPPORT_EMAIL}));
|
||||
bodyLines.push(
|
||||
RuntimeConfig.isSelfHosted()
|
||||
? i18n._(ESCAPE_CONFIRM_SUPPORT_SELF_HOSTED_DESCRIPTOR)
|
||||
: i18n._(ESCAPE_CONFIRM_SUPPORT_DESCRIPTOR, {supportEmail: SUPPORT_EMAIL}),
|
||||
);
|
||||
return {
|
||||
title: leaving
|
||||
? i18n._(ESCAPE_CONFIRM_TITLE_WITH_GUILDS_DESCRIPTOR, {count: plan.guildNames.length})
|
||||
|
||||
+17
-4
@@ -4,10 +4,12 @@ import {showGenericErrorModal} from '@app/features/app/components/alerts/Generic
|
||||
import * as Modal from '@app/features/app/components/dialogs/Modal';
|
||||
import {ExternalLink} from '@app/features/app/components/shared/ExternalLink';
|
||||
import {EXAMPLE_EMAIL, SUPPORT_EMAIL, SUPPORT_EMAIL_MAILTO} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
|
||||
import styles from '@app/features/auth/components/modals/RequiredActionModal.module.css';
|
||||
import {
|
||||
A_NEW_VERIFICATION_CODE_HAS_BEEN_SENT_DESCRIPTOR,
|
||||
ALT_ROUTES_HUMAN_REVIEW_DESCRIPTOR,
|
||||
ALT_ROUTES_HUMAN_REVIEW_SELF_HOSTED_DESCRIPTOR,
|
||||
ALT_ROUTES_TITLE_DESCRIPTOR,
|
||||
CAPTCHA_REQUIRED_DESCRIPTOR,
|
||||
CODE_DID_NOT_WORK_DESCRIPTOR,
|
||||
@@ -20,6 +22,7 @@ import {
|
||||
ENTER_VALID_PHONE_DESCRIPTOR,
|
||||
ESCAPE_BUTTON_DESCRIPTOR,
|
||||
FAILED_TO_RESEND_VERIFICATION_CODE_PLEASE_TRY_AGAIN_DESCRIPTOR,
|
||||
INSTANCE_ADMIN_CONTACT_DESCRIPTOR,
|
||||
NEW_EMAIL_DESCRIPTOR,
|
||||
PHONE_ALREADY_USED_DESCRIPTOR,
|
||||
PHONE_CANNOT_BE_USED_DESCRIPTOR,
|
||||
@@ -290,6 +293,13 @@ export const ValueBlock: React.FC<ValueBlockProps> = ({label, value}) => (
|
||||
);
|
||||
export const SupportLinkLine: React.FC = () => {
|
||||
const {i18n} = useLingui();
|
||||
if (RuntimeConfig.isSelfHosted()) {
|
||||
return (
|
||||
<span className={styles.supportLink} data-flx="auth.required-action-modal.support-link-line.instance-admin">
|
||||
{i18n._(INSTANCE_ADMIN_CONTACT_DESCRIPTOR)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ExternalLink
|
||||
href={SUPPORT_EMAIL_MAILTO}
|
||||
@@ -311,6 +321,7 @@ interface RequiredActionAltRoutesProps {
|
||||
|
||||
export const RequiredActionAltRoutes: React.FC<RequiredActionAltRoutesProps> = ({escapeAction}) => {
|
||||
const {i18n} = useLingui();
|
||||
const selfHosted = RuntimeConfig.isSelfHosted();
|
||||
return (
|
||||
<div data-flx="auth.required-action-modal.alt-routes">
|
||||
<p
|
||||
@@ -322,11 +333,13 @@ export const RequiredActionAltRoutes: React.FC<RequiredActionAltRoutesProps> = (
|
||||
{i18n._(ALT_ROUTES_TITLE_DESCRIPTOR)}
|
||||
</p>
|
||||
<p className={styles.altRoutesBody} data-flx="auth.required-action-modal.alt-routes.body">
|
||||
{i18n._(ALT_ROUTES_HUMAN_REVIEW_DESCRIPTOR)}
|
||||
{i18n._(selfHosted ? ALT_ROUTES_HUMAN_REVIEW_SELF_HOSTED_DESCRIPTOR : ALT_ROUTES_HUMAN_REVIEW_DESCRIPTOR)}
|
||||
</p>
|
||||
<div className={styles.supportBlock} data-flx="auth.required-action-modal.alt-routes.support-block">
|
||||
<SupportLinkLine data-flx="auth.required-action.required-action-shared.required-action-alt-routes.support-link-line" />
|
||||
</div>
|
||||
{selfHosted ? null : (
|
||||
<div className={styles.supportBlock} data-flx="auth.required-action-modal.alt-routes.support-block">
|
||||
<SupportLinkLine data-flx="auth.required-action.required-action-shared.required-action-alt-routes.support-link-line" />
|
||||
</div>
|
||||
)}
|
||||
{escapeAction ? (
|
||||
<>
|
||||
<div className={styles.altRoutesDivider} data-flx="auth.required-action-modal.alt-routes.divider" />
|
||||
|
||||
Reference in new issue
Block a user