feat(nagbar): add invite CTA to Fluxer HQ (#68)
This commit is contained in:
@@ -28,9 +28,17 @@ interface NagbarButtonProps {
|
||||
isMobile: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
submitting?: boolean;
|
||||
}
|
||||
|
||||
export const NagbarButton = ({children, onClick, isMobile, className, disabled = false}: NagbarButtonProps) => {
|
||||
export const NagbarButton = ({
|
||||
children,
|
||||
onClick,
|
||||
isMobile,
|
||||
className,
|
||||
disabled = false,
|
||||
submitting,
|
||||
}: NagbarButtonProps) => {
|
||||
return (
|
||||
<Button
|
||||
variant="inverted-outline"
|
||||
@@ -40,6 +48,7 @@ export const NagbarButton = ({children, onClick, isMobile, className, disabled =
|
||||
className={clsx(styles.button, className)}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
submitting={submitting}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
|
||||
@@ -25,6 +25,7 @@ import {DesktopDownloadNagbar} from './nagbars/DesktopDownloadNagbar';
|
||||
import {DesktopNotificationNagbar} from './nagbars/DesktopNotificationNagbar';
|
||||
import {EmailVerificationNagbar} from './nagbars/EmailVerificationNagbar';
|
||||
import {GiftInventoryNagbar} from './nagbars/GiftInventoryNagbar';
|
||||
import {GuildMembershipCtaNagbar} from './nagbars/GuildMembershipCtaNagbar';
|
||||
import {MobileDownloadNagbar} from './nagbars/MobileDownloadNagbar';
|
||||
import {PendingBulkDeletionNagbar} from './nagbars/PendingBulkDeletionNagbar';
|
||||
import {PremiumExpiredNagbar} from './nagbars/PremiumExpiredNagbar';
|
||||
@@ -66,6 +67,8 @@ export const NagbarContainer: React.FC<NagbarContainerProps> = observer(({nagbar
|
||||
return <DesktopDownloadNagbar key={nagbar.type} isMobile={mobileLayout.enabled} />;
|
||||
case NagbarType.MOBILE_DOWNLOAD:
|
||||
return <MobileDownloadNagbar key={nagbar.type} isMobile={mobileLayout.enabled} />;
|
||||
case NagbarType.GUILD_MEMBERSHIP_CTA:
|
||||
return <GuildMembershipCtaNagbar key={nagbar.type} isMobile={mobileLayout.enabled} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -162,6 +162,13 @@ export const useNagbarConditions = (): NagbarConditions => {
|
||||
pendingBulkDeletion && !nagbarState.hasPendingBulkDeletionDismissed(pendingBulkDeletionKey),
|
||||
);
|
||||
|
||||
const canShowGuildMembershipCta = (() => {
|
||||
if (nagbarState.forceHideGuildMembershipCta) return false;
|
||||
if (nagbarState.forceGuildMembershipCta) return true;
|
||||
if (!user) return false;
|
||||
return !nagbarState.guildMembershipCtaDismissed;
|
||||
})();
|
||||
|
||||
return {
|
||||
userIsUnclaimed: nagbarState.forceHideUnclaimedAccount
|
||||
? false
|
||||
@@ -185,6 +192,7 @@ export const useNagbarConditions = (): NagbarConditions => {
|
||||
canShowDesktopDownload,
|
||||
canShowMobileDownload,
|
||||
hasPendingBulkMessageDeletion,
|
||||
canShowGuildMembershipCta,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -208,23 +216,28 @@ export const useActiveNagbars = (conditions: NagbarConditions): Array<NagbarStat
|
||||
visible: conditions.userIsUnclaimed,
|
||||
},
|
||||
{
|
||||
type: NagbarType.EMAIL_VERIFICATION,
|
||||
type: NagbarType.GUILD_MEMBERSHIP_CTA,
|
||||
priority: 2,
|
||||
visible: conditions.canShowGuildMembershipCta,
|
||||
},
|
||||
{
|
||||
type: NagbarType.EMAIL_VERIFICATION,
|
||||
priority: 3,
|
||||
visible: conditions.userNeedsVerification,
|
||||
},
|
||||
{
|
||||
type: NagbarType.PREMIUM_GRACE_PERIOD,
|
||||
priority: 3,
|
||||
priority: 4,
|
||||
visible: conditions.canShowPremiumGracePeriod,
|
||||
},
|
||||
{
|
||||
type: NagbarType.PREMIUM_EXPIRED,
|
||||
priority: 4,
|
||||
priority: 5,
|
||||
visible: conditions.canShowPremiumExpired,
|
||||
},
|
||||
{
|
||||
type: NagbarType.PREMIUM_ONBOARDING,
|
||||
priority: 5,
|
||||
priority: 6,
|
||||
visible: conditions.canShowPremiumOnboarding,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Trans} from '@lingui/react/macro';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import React from 'react';
|
||||
import * as ModalActionCreators from '~/actions/ModalActionCreators';
|
||||
import {modal} from '~/actions/ModalActionCreators';
|
||||
import {Nagbar} from '~/components/layout/Nagbar';
|
||||
import {NagbarButton} from '~/components/layout/NagbarButton';
|
||||
import {NagbarContent} from '~/components/layout/NagbarContent';
|
||||
import {InviteAcceptModal} from '~/components/modals/InviteAcceptModal';
|
||||
import AuthenticationStore from '~/stores/AuthenticationStore';
|
||||
import GuildMemberStore from '~/stores/GuildMemberStore';
|
||||
import InviteStore from '~/stores/InviteStore';
|
||||
import NagbarStore from '~/stores/NagbarStore';
|
||||
import {isGuildInvite} from '~/types/InviteTypes';
|
||||
|
||||
const FLUXER_HQ_INVITE_CODE = 'fluxer-hq';
|
||||
|
||||
export const GuildMembershipCtaNagbar = observer(({isMobile}: {isMobile: boolean}) => {
|
||||
const currentUserId = AuthenticationStore.currentUserId;
|
||||
const inviteState = InviteStore.invites.get(FLUXER_HQ_INVITE_CODE);
|
||||
const invite = inviteState?.data ?? null;
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
||||
|
||||
if (!currentUserId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (invite && isGuildInvite(invite)) {
|
||||
const guildId = invite.guild.id;
|
||||
const isMember = Boolean(GuildMemberStore.getMember(guildId, currentUserId));
|
||||
if (isMember) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const handleJoinGuild = async () => {
|
||||
if (isSubmitting) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
const module = await import('~/actions/InviteActionCreators');
|
||||
await module.fetchWithCoalescing(FLUXER_HQ_INVITE_CODE);
|
||||
|
||||
const loadedInvite = InviteStore.invites.get(FLUXER_HQ_INVITE_CODE)?.data ?? null;
|
||||
if (loadedInvite && isGuildInvite(loadedInvite)) {
|
||||
const guildId = loadedInvite.guild.id;
|
||||
const isMember = Boolean(GuildMemberStore.getMember(guildId, currentUserId));
|
||||
if (isMember) {
|
||||
NagbarStore.guildMembershipCtaDismissed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
ModalActionCreators.push(modal(() => <InviteAcceptModal code={FLUXER_HQ_INVITE_CODE} />));
|
||||
}
|
||||
};
|
||||
|
||||
const handleDismiss = () => {
|
||||
NagbarStore.guildMembershipCtaDismissed = true;
|
||||
};
|
||||
|
||||
return (
|
||||
<Nagbar
|
||||
isMobile={isMobile}
|
||||
backgroundColor="var(--brand-primary)"
|
||||
textColor="var(--text-on-brand-primary)"
|
||||
onDismiss={handleDismiss}
|
||||
dismissible={true}
|
||||
>
|
||||
<NagbarContent
|
||||
isMobile={isMobile}
|
||||
message={<Trans>Join Fluxer HQ to chat with the team and stay up to date on the latest!</Trans>}
|
||||
actions={
|
||||
<NagbarButton isMobile={isMobile} onClick={handleJoinGuild} submitting={isSubmitting} disabled={isSubmitting}>
|
||||
<Trans>Join Fluxer HQ</Trans>
|
||||
</NagbarButton>
|
||||
}
|
||||
/>
|
||||
</Nagbar>
|
||||
);
|
||||
});
|
||||
@@ -28,6 +28,7 @@ export const NagbarType = {
|
||||
BULK_DELETE_PENDING: 'bulk-delete-pending',
|
||||
DESKTOP_DOWNLOAD: 'desktop-download',
|
||||
MOBILE_DOWNLOAD: 'mobile-download',
|
||||
GUILD_MEMBERSHIP_CTA: 'guild-membership-cta',
|
||||
} as const;
|
||||
|
||||
export type NagbarType = (typeof NagbarType)[keyof typeof NagbarType];
|
||||
@@ -53,6 +54,7 @@ export interface NagbarConditions {
|
||||
canShowDesktopDownload: boolean;
|
||||
canShowMobileDownload: boolean;
|
||||
hasPendingBulkMessageDeletion: boolean;
|
||||
canShowGuildMembershipCta: boolean;
|
||||
}
|
||||
|
||||
export const UPDATE_DISMISS_KEY = 'fluxer_update_dismissed_until';
|
||||
|
||||
Reference in New Issue
Block a user