Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next #453

Merged
merged 10 commits into from
Jan 21, 2025
14 changes: 11 additions & 3 deletions src/components/avatar/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import Avatar from '@mui/material/Avatar';
import {FC} from "react";
import {dicebear} from "@src/utils/dicebear.ts";
import {COLORS} from "@src/layouts/config-layout.ts";

interface AvatarProfileProps {
src: string;
Expand All @@ -11,12 +12,19 @@ interface AvatarProfileProps {
}

const AvatarProfile: FC<AvatarProfileProps> = ({ src, alt, sx, ...other }) => {

//Check if src is a valid URL starting with http or https; if not, use the dicebear API to generate a random avatar
if (!src.startsWith('http') && !src.startsWith('https')) { src = dicebear(src) }
const imageSrc = src.startsWith('http') || src.startsWith('https') ? src : dicebear(src);

// Default styles for the Avatar component
sx = {
backgroundColor: COLORS.GRAY_DARK,
fontWeight: 'bold',
...sx,
};


return (
<Avatar alt={alt ?? 'Avatar profile'} src={src} sx={sx} {...other} />
<Avatar alt={alt?.toUpperCase() ?? 'Avatar profile'} src={imageSrc} sx={sx} {...other} />
)
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/user-item/BadgeVerified.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const BadgeVerified: FC<BadgeVerifiedProps> = ({ address }) => {
// If the user is not verified, do not render the badge
if (!isVerified) return null;

return <Icon width={20} color={"#FFF"} icon={"ic:round-verified"} />;
return <Icon width={20} color={"#cca421"} icon={"ic:round-verified"} />;
};

export default BadgeVerified;
2 changes: 1 addition & 1 deletion src/config-global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const GLOBAL_CONSTANTS = {
process.env.VITE_ACCESS_MANAGER_ADDRESS || import.meta.env.VITE_ACCESS_MANAGER_ADDRESS || '',
SENTRY_AUTH_TOKEN:
process.env.VITE_SENTRY_AUTH_TOKEN || import.meta.env.VITE_SENTRY_AUTH_TOKEN || '',
SENTRY_DNS: process.env.VITE_SENTRY_DNS || import.meta.env.VITE_SENTRY_DNS || '',
SENTRY_DSN: process.env.VITE_SENTRY_DSN || import.meta.env.VITE_SENTRY_DSN || '',
PINATA_API_KEY: process.env.VITE_PINATA_API_KEY || import.meta.env.VITE_PINATA_API_KEY || '',
PINATA_SECRET_API_KEY:
process.env.VITE_PINATA_SECRET_API_KEY || import.meta.env.VITE_PINATA_SECRET_API_KEY || '',
Expand Down
45 changes: 23 additions & 22 deletions src/hooks/use-referrals.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import { useState } from 'react';
import { useSelector } from 'react-redux';
import emailjs from '@emailjs/browser';

import { GLOBAL_CONSTANTS } from '@src/config-global';
import { useSelector } from 'react-redux';
import { Invitation } from '@src/types/invitation'; // <-- Importing from separate types file
import {
fetchInvitations as fetchInvitationsAction,
checkIfMyEmailHasPendingInvite as checkIfMyEmailHasPendingInviteAction,
acceptInvitation as acceptInvitationAction,
checkIfInvitationSent as checkIfInvitationSentAction,
checkIfEmailAlreadyAccepted as checkIfEmailAlreadyAcceptedAction,
sendInvitation as sendInvitationAction,
acceptOrCreateInvitationForUser as acceptOrCreateInvitationForUserAction
acceptOrCreateInvitationForUser as acceptOrCreateInvitationForUserAction,
} from '@src/utils/supabase-actions';

export interface Invitation {
id: string;
status: 'pending' | 'accepted' | 'rejected';
sender_email: string;
destination: string;
sender_id: string;
receiver_id: string | null;
payload: any;
created_at: string;
}

/**
* The type for sending emails through EmailJS.
*/
export type EmailParams = {
to_email: string;
from_name: string;
Expand Down Expand Up @@ -94,14 +88,18 @@ const useReferrals = () => {
setLoading(true);
setError(null);

const { data, error } = await acceptInvitationAction(invitationId, sessionData?.profile?.id);
const { data, error } = await acceptInvitationAction(
invitationId,
sessionData?.profile?.id
);

if (error) {
setError(error);
setLoading(false);
return null;
}

// Update local state to reflect the accepted status
setInvitations((prev) =>
prev.map((inv) =>
inv.id === invitationId
Expand Down Expand Up @@ -168,6 +166,7 @@ const useReferrals = () => {
* @returns {Promise<void>} - Throws an error if something goes wrong.
*/
const sendInvitation = async (destination: string, payload: any): Promise<void> => {
// Insert a new invitation into Supabase
const { error } = await sendInvitationAction(destination, payload, userEmail, sessionData);

if (error) {
Expand All @@ -176,7 +175,7 @@ const useReferrals = () => {
} else {
console.log('Invitation stored successfully in Supabase.');

// Send the email using EmailJS
// Send the email via EmailJS
await sendEmail({
to_email: destination,
from_name: payload?.data?.from?.displayName ?? 'Watchit Web3xAI',
Expand All @@ -201,13 +200,17 @@ const useReferrals = () => {
}
};

/**
* Send an email with EmailJS.
*/
const sendEmail = async (data: EmailParams) => {
const { from_name, to_email } = data;

// Set the template parameters for EmailJS
const templateParams = {
to_email,
from_name,
from_email: GLOBAL_CONSTANTS.SENDER_EMAIL,
from_email: GLOBAL_CONSTANTS.SENDER_EMAIL, // <-- Enforcing the global from_email
};

try {
Expand All @@ -233,17 +236,15 @@ const useReferrals = () => {
loading,
error,

// Fetch/CRUD Methods
// CRUD/Fetch Methods
fetchInvitations,
sendInvitation,
sendEmail,

// Additional Validation/Check Methods
checkIfMyEmailHasPendingInvite,
acceptInvitation,
checkIfInvitationSent,
checkIfEmailAlreadyAccepted,
acceptOrCreateInvitationForUser
sendInvitation,
acceptOrCreateInvitationForUser,
sendEmail,
};
};

Expand Down
3 changes: 2 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ const isDevelopment = GLOBAL_CONSTANTS.ENVIRONMENT === 'development';
Sentry.init({
environment: GLOBAL_CONSTANTS.ENVIRONMENT,
enabled: !isDevelopment,
dsn: GLOBAL_CONSTANTS.SENTRY_DNS,
dsn: GLOBAL_CONSTANTS.SENTRY_DSN,
integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],
// Tracing
tracesSampleRate: 1.0, // Capture 100% of the transactions
tracePropagationTargets: ["localhost", /^https:\/\/app.watchit\.movie/],
// Session Replay
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
Expand Down
1 change: 1 addition & 0 deletions src/layouts/_common/account-popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export default function AccountPopover() {
}
alt="avatar"
sx={{
fontSize: '1.25rem',
width: 36,
height: 36,
border: (theme: any) => `solid 2px ${theme.palette.background.default}`,
Expand Down
33 changes: 30 additions & 3 deletions src/sections/finance/components/finance-invite-friends.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ERRORS } from '@notifications/errors.ts';

import useReferrals from "@src/hooks/use-referrals";
import LoadingButton from '@mui/lab/LoadingButton';
import {checkIfEmailAlreadyInvited} from "@src/utils/supabase-actions.ts";

interface Props extends BoxProps {
img?: string;
Expand All @@ -42,17 +43,25 @@ export default function FinanceInviteFriends({
} = useReferrals();
const theme = useTheme();
const sessionData = useSelector((state: any) => state.auth.session);
const userLoggedEmail = useSelector((state: any) => state.auth.email);
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(false);

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setEmail(event.target.value);
};

const handleInviteClick = async () => {
// Basic email format check
/*
* Return true if the email is valid, false otherwise.
* */
const handleValidEmail = () => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return emailRegex.test(email);
}

const handleInviteClick = async () => {

if (!handleValidEmail()) {
notifyError(ERRORS.INVITATION_EMAIL_ERROR);
return;
}
Expand All @@ -61,6 +70,23 @@ export default function FinanceInviteFriends({

// Check if there's an existing invitation from the current user to this email
const alreadySent = await checkIfInvitationSent(email);

// Check if the user has already been invited but someone else
const { invited } = await checkIfEmailAlreadyInvited(email);

if (invited) {
notifyError(ERRORS.INVITATION_USER_ALREADY_INVITED);
setLoading(false);
return;
}

// Check if the email entered is the same as the logged user's email
if (email === userLoggedEmail) {
notifyError(ERRORS.INVITATION_USER_CANT_INVITE_SELF);
setLoading(false);
return;
}

if (alreadySent) {
// You can adapt the notification message to match your requirements
notifyError(ERRORS.ALREADY_SENT_INVITATION);
Expand Down Expand Up @@ -165,6 +191,7 @@ export default function FinanceInviteFriends({
onChange={handleInputChange}
endAdornment={
<LoadingButton
disabled={!email || loading || !handleValidEmail()}
color="warning"
variant="contained"
size="small"
Expand Down
3 changes: 2 additions & 1 deletion src/sections/user/profile-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const shareLinks = [
{
icon: 'mingcute:social-x-line',
label: 'X',
url: `https://x.com/share/?url=${encodeURIComponent(urlToShare)}&text=Visit%20my%20profile%20in%20Watchit&hashtags=Watchit`,
url: `https://x.com/share/?url=${encodeURIComponent(urlToShare)}&text=Visit%20my%20profile%20on%20Watchit&hashtags=Watchit,Crypto,Blockchain`,
},
{
icon: 'mdi:facebook',
Expand Down Expand Up @@ -332,6 +332,7 @@ const ProfileHeader = ({
alt={profile?.handle?.localName ?? ''}
variant="rounded"
sx={{
fontSize: '3em',
width: { xs: 96, md: 128 },
height: { xs: 96, md: 128 },
border: `solid 2px ${theme.palette.common.white}`,
Expand Down
10 changes: 10 additions & 0 deletions src/types/invitation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface Invitation {
id: string;
status: 'pending' | 'accepted' | 'rejected';
sender_email: string;
destination: string;
sender_id: string;
receiver_id: string | null;
payload: any;
created_at: string;
}
4 changes: 4 additions & 0 deletions src/utils/notifications/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export enum ERRORS {
ALREADY_SENT_INVITATION = 'ALREADY_SENT_INVITATION',
ALREADY_ENROLLED = 'ALREADY_ENROLLED',
INVITATION_SEND_ERROR = 'INVITATION_SEND_ERROR',
INVITATION_USER_ALREADY_INVITED = 'INVITATION_USER_ALREADY_INVITED',
INVITATION_USER_CANT_INVITE_SELF = 'INVITATION_USER_CANT_INVITE_SELF',
}

/**
Expand Down Expand Up @@ -113,4 +115,6 @@ export const ERROR_MESSAGES: Record<ERRORS, string> = {
[ERRORS.ALREADY_SENT_INVITATION]: 'You have already sent an invitation to this email address!',
[ERRORS.ALREADY_ENROLLED]: 'This user is already enrolled!',
[ERRORS.INVITATION_SEND_ERROR]: 'An error occurred while sending the invitation.',
[ERRORS.INVITATION_USER_ALREADY_INVITED]: 'This user has already been invited!',
[ERRORS.INVITATION_USER_CANT_INVITE_SELF]: 'You cannot invite yourself!',
};
Loading
Loading