Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 73 additions & 28 deletions src/components/leave-tip-card.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { FC, useState } from 'react';
import { FC, useEffect, useMemo, useState } from 'react';
import { Typography, Box, TextField, Stack, Paper } from '@mui/material';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import LoadingButton from '@mui/lab/LoadingButton';
import { useDispatch } from 'react-redux';
import { openLoginModal } from '@redux/auth';
import { useAuth } from '@src/hooks/use-auth.ts';
import { useTransfer } from '@src/hooks/protocol/use-transfer.ts';
import { Post } from '@src/graphql/generated/graphql.ts';
import { notifyError, notifySuccess } from '@src/libs/notifications/internal-notifications.ts';
import { ERRORS } from '@src/libs/notifications/errors.ts';
import { SUCCESS } from '@src/libs/notifications/success.ts';
import { GetTipsByBakerForPostDocument, useCreateTipMutation } from '@src/graphql/generated/hooks.tsx';

const tipOptions = [
{ value: '10', title: '10', subtitle: 'A token of appreciation' },
Expand All @@ -12,22 +20,75 @@ const tipOptions = [
];

export const LeaveTipCard: FC<{ post: Post }> = ({ post }) => {
const dispatch = useDispatch();
const { session } = useAuth();
const { transfer, loading: transferLoading, error } = useTransfer();
const [createTip] = useCreateTipMutation();

const [selectedTip, setSelectedTip] = useState('10');
const [customTip, setCustomTip] = useState('');
const [successMessage, setSuccessMessage] = useState(false);

useEffect(() => {
if (error) {
notifyError(error as ERRORS);
}
}, [error]);

const amount = useMemo(() => {
// Si hay custom, usa custom. Sino, usa el seleccionado.
const raw = selectedTip ? selectedTip : customTip;
const parsed = Number(raw);
// Evita NaN o negativos
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
}, [selectedTip, customTip]);

const recipient = post?.author?.address ?? '';

const handleTipChange = (value: string) => {
setSelectedTip(value);
setCustomTip('');
setSuccessMessage(false);
};

const handleCustomTipChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSelectedTip('');
setCustomTip(event.target.value);
setSuccessMessage(false);
};

const handleSendTip = async () => {
if (!session?.authenticated) return dispatch(openLoginModal());
if (!recipient || amount <= 0) return;

try {
const pTransfer = transfer({ amount, recipient });

const pCreateTip = pTransfer.then(() =>
createTip({
variables: {
input: {
postId: post.id,
creator: recipient,
amount,
txHash: null,
message: 'tip',
},
},
refetchQueries: [
{ query: GetTipsByBakerForPostDocument, variables: { postId: post.id } },
],
awaitRefetchQueries: true,
})
);

await Promise.all([pTransfer, pCreateTip]);

notifySuccess(SUCCESS.TIP_CREATED_SUCCESSFULLY);
} catch (e) {
console.error('Transfer error:', e);
}
};

const isDisabled = transferLoading || amount <= 0 || !recipient;

return (
<Card sx={{ width: '100%', maxWidth: { lg: 400 }, margin: 'auto', backgroundColor: '#2B2D31' }}>
<CardContent>
Expand All @@ -37,6 +98,7 @@ export const LeaveTipCard: FC<{ post: Post }> = ({ post }) => {
<Typography variant="body2" color="textSecondary" sx={{ mb: 3 }}>
Choose an amount to leave a tip and support the content you love.
</Typography>

<Stack spacing={2}>
<Stack spacing={2} direction="row">
{tipOptions.map((option) => (
Expand All @@ -56,56 +118,39 @@ export const LeaveTipCard: FC<{ post: Post }> = ({ post }) => {
'&:hover': { opacity: 1 },
}}
>
<Typography variant="body1" fontWeight="bold" align={'center'}>
<Typography variant="body1" fontWeight="bold" align="center">
{option.title}
</Typography>
<Typography
variant="subtitle2"
align={'center'}
style={{
color: 'text.secondary',
fontSize: '0.7rem',
}}
>
<Typography variant="subtitle2" align="center" sx={{ fontSize: '0.7rem' }}>
MMC
</Typography>
</Paper>
))}
</Stack>

<Box>
<TextField
type="number"
placeholder="Enter custom tip in MMC"
fullWidth
value={customTip}
onChange={handleCustomTipChange}
InputProps={{
inputProps: { min: 1 },
}}
InputProps={{ inputProps: { min: 1 } }}
/>
</Box>
</Stack>

<Stack direction="row" justifyContent="center" sx={{ mt: 4 }}>
<LoadingButton
variant="contained"
disabled={true}
sx={{ width: '100%', py: 1.5 }}
loading={false}
loading={transferLoading}
disabled={isDisabled}
onClick={handleSendTip}
>
Leave a Tip
</LoadingButton>
</Stack>

<Typography variant="body2" color="text.secondary" align="center" sx={{ mt: 2 }}>
This feature is coming in the next release!
</Typography>

{successMessage && (
<Typography variant="body2" color="success.main" align="center" sx={{ mt: 2 }}>
Tip sent successfully! Thank you for your support.
</Typography>
)}
</CardContent>
</Card>
);
Expand Down
137 changes: 89 additions & 48 deletions src/components/nav-section/vertical/nav-item.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useRef, useState, useCallback } from 'react';
// @mui
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Tooltip from '@mui/material/Tooltip';
import Popover from '@mui/material/Popover';
import Stack from '@mui/material/Stack';
import ListItemText from '@mui/material/ListItemText';
// routes
import { RouterLink } from '@src/routes/components';
Expand All @@ -18,18 +20,26 @@ type Props = NavItemProps & {
};

export default function NavItem({
item,
open,
depth,
active,
config,
externalLink,
...other
}: Props) {
item,
open,
depth,
active,
config,
externalLink,
...other
}: Props) {
const { title, path, icon, info, children, disabled, caption, roles } = item;

const subItem = depth !== 1;

const anchorRef = useRef<HTMLSpanElement | null>(null);
const [popoverOpen, setPopoverOpen] = useState(false);

const handleOpen = useCallback(() => setPopoverOpen(true), []);
const handleClose = useCallback(() => setPopoverOpen(false), []);

const comingSoonText = caption || '✨ Coming soon';

const renderContent = (
<StyledItem
disableGutters
Expand All @@ -52,13 +62,7 @@ export default function NavItem({
{!(config.hiddenLabel && !subItem) && (
<ListItemText
primary={title}
secondary={
caption ? (
<Tooltip title={caption} placement="top-start">
<span>{caption}</span>
</Tooltip>
) : null
}
secondary={disabled ? null : undefined}
primaryTypographyProps={{
noWrap: true,
typography: 'body2',
Expand Down Expand Up @@ -95,44 +99,81 @@ export default function NavItem({
return null;
}

// External link
if (externalLink)
return (
<Link
href={path}
target="_blank"
rel="noopener"
underline="none"
color="inherit"
sx={{
...(disabled && {
cursor: 'default',
}),
}}
>
const commonLinkProps = {
underline: 'none' as const,
color: 'inherit',
'aria-disabled': disabled ? 'true' : undefined,
onClick: (e: React.MouseEvent) => {
if (disabled) {
e.preventDefault();
e.stopPropagation();
}
},
sx: {
...(disabled && {
cursor: 'not-allowed',
pointerEvents: 'auto',
}),
},
};

let clickableEl: React.ReactNode;

if (externalLink) {
clickableEl = (
<Link href={path} target="_blank" rel="noopener" {...commonLinkProps}>
{renderContent}
</Link>
);
} else if (children) {
clickableEl = renderContent;
} else {
clickableEl = (
<Link component={RouterLink} href={path ?? '/'} {...commonLinkProps}>
{renderContent}
</Link>
);
}

// Has child
if (children) {
return renderContent;
if (!disabled) {
return <>{clickableEl}</>;
}

// Default
return (
<Link
component={RouterLink}
href={path ?? '/'}
underline="none"
color="inherit"
sx={{
...(disabled && {
cursor: 'default',
}),
}}
>
{renderContent}
</Link>
<>
<Box
component="span"
ref={anchorRef}
onMouseEnter={handleOpen}
onMouseLeave={handleClose}
sx={{ display: 'inline-flex', width: '100%' }}
>
{clickableEl}
</Box>

<Popover
open={popoverOpen}
anchorEl={anchorRef.current}
anchorOrigin={{ vertical: 'center', horizontal: 'right' }}
transformOrigin={{ vertical: 'center', horizontal: 'left' }}
slotProps={{
paper: {
onMouseEnter: handleOpen,
onMouseLeave: handleClose,
sx: {
backgroundColor: 'rgba(0,0,0,0.6)',
padding: '8px 8px',
borderRadius: 1,
...(popoverOpen && { pointerEvents: 'auto' }),
},
},
}}
sx={{ pointerEvents: 'none' }}
>
<Stack spacing={1} direction="row" alignItems="center">
<small>{comingSoonText}</small>
</Stack>
</Popover>
</>
);
}
33 changes: 16 additions & 17 deletions src/components/publication-detail-main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { varFade } from '@src/components/animate';
import { LeaveTipCard } from '@src/components/leave-tip-card.tsx';
import PostCommentList from '@src/sections/publication/components/publication-comments-list.tsx';
import PublicationCommentForm from '@src/sections/publication/components/publication-details-comment-form.tsx';
import { SubscribeToUnlockCard } from '@src/components/subscribe-to-unlock-card/subscribe-to-unlock-card.tsx';
// import { SubscribeToUnlockCard } from '@src/components/subscribe-to-unlock-card/subscribe-to-unlock-card.tsx';
import Popover from '@mui/material/Popover';
import { useNotifications } from '@src/hooks/use-notifications.ts';
import { openLoginModal } from '@redux/auth';
Expand All @@ -62,11 +62,11 @@ import PublicationShare from '@src/sections/publication/components/publication-s

export default function PublicationDetailMain({
post,
handleSubscribe,
handleRefetchAccess,
loadingSubscribe,
subscribeDisabled,
hasAccess,
// handleSubscribe,
// handleRefetchAccess,
// loadingSubscribe,
// subscribeDisabled,
// hasAccess,
}: Readonly<PublicationDetailProps>) {
const [showComments, setShowComments] = useState(false);
const [openConfirmModal, setOpenConfirmModal] = useState(false);
Expand Down Expand Up @@ -320,18 +320,17 @@ export default function PublicationDetailMain({
pr: 1,
}}
>
{hasAccess && sessionData?.authenticated ? (
// @ts-expect-error No error in this context
{/*{hasAccess && sessionData?.authenticated ? (*/}
<LeaveTipCard post={post} />
) : (
<SubscribeToUnlockCard
loadingSubscribe={loadingSubscribe}
subscribeDisabled={subscribeDisabled ?? false}
handleRefetchAccess={handleRefetchAccess}
onSubscribe={handleSubscribe}
post={post}
/>
)}
{/*) : (*/}
{/* <SubscribeToUnlockCard*/}
{/* loadingSubscribe={loadingSubscribe}*/}
{/* subscribeDisabled={subscribeDisabled ?? false}*/}
{/* handleRefetchAccess={handleRefetchAccess}*/}
{/* onSubscribe={handleSubscribe}*/}
{/* post={post}*/}
{/* />*/}
{/*)}*/}
</Box>

<Box
Expand Down
Loading
Loading