Skip to content

Commit

Permalink
Merge pull request #193 from TogetherCrew/feat/twitter
Browse files Browse the repository at this point in the history
Feat/twitter
  • Loading branch information
mehdi-torabiv authored Oct 2, 2023
2 parents 6f6f9d8 + 345aed8 commit 97392c1
Show file tree
Hide file tree
Showing 88 changed files with 2,866 additions and 70 deletions.
10 changes: 10 additions & 0 deletions src/components/layouts/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { Tooltip, Typography } from '@mui/material';
import useAppStore from '../../store/useStore';
import { StorageService } from '../../services/StorageService';
import { IUser } from '../../utils/types';
import { BsBarChartFill } from 'react-icons/bs';

const Sidebar = () => {
const { guildInfoByDiscord } = useAppStore();
Expand Down Expand Up @@ -62,6 +63,15 @@ const Sidebar = () => {
/>
),
},
{
name: 'Growth',
path: '/growth',
icon: (
<BsBarChartFill
style={{ fontSize: 20, color: 'black', margin: '0 auto' }}
/>
),
},
{
name: 'Settings',
path: '/settings',
Expand Down
10 changes: 10 additions & 0 deletions src/components/layouts/xs/SidebarXs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import useAppStore from '../../../store/useStore';
import { StorageService } from '../../../services/StorageService';
import { IUser } from '../../../utils/types';
import { conf } from '../../../configs';
import { BsBarChartFill } from 'react-icons/bs';

const Sidebar = () => {
const { guildInfoByDiscord } = useAppStore();
Expand Down Expand Up @@ -65,6 +66,15 @@ const Sidebar = () => {
/>
),
},
{
name: 'Growth',
path: '/growth',
icon: (
<BsBarChartFill
style={{ fontSize: 30, color: 'black', margin: '0 auto' }}
/>
),
},
{
name: 'Settings',
path: '/settings',
Expand Down
112 changes: 74 additions & 38 deletions src/components/pages/settings/ConnectCommunities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,38 @@ import CustomButton from '../../global/CustomButton';
import DatePeriodRange from '../../global/DatePeriodRange';
import CustomModal from '../../global/CustomModal';
import ChannelSelection from './ChannelSelection';
import { BsClockHistory } from 'react-icons/bs';
import { BsClockHistory, BsTwitter } from 'react-icons/bs';
import useAppStore from '../../../store/useStore';
import { useRouter } from 'next/router';
import moment from 'moment';
import { StorageService } from '../../../services/StorageService';
import { IUser } from '../../../utils/types';
import * as amplitude from '@amplitude/analytics-browser';
import { IDecodedToken } from '../../../utils/interfaces';
import jwt_decode from 'jwt-decode';

import {
setAmplitudeUserIdFromToken,
trackAmplitudeEvent,
} from '../../../helpers/amplitudeHelper';
import { decodeUserTokenDiscordId } from '../../../helpers/helper';

export default function ConnectCommunities() {
const router = useRouter();

const user = StorageService.readLocalStorage<IUser>('user');

const [open, setOpen] = useState<boolean>(false);
const [confirmModalOpen, setConfirmModalOpen] = useState<boolean>(false);
const [guildId, setGuildId] = useState<any>('');
const [activePeriod, setActivePeriod] = useState<number | string>(1);
const [datePeriod, setDatePeriod] = useState<string>('');
const [selectedChannels, setSelectedChannels] = useState<any[]>([]);

const { guilds, connectNewGuild, patchGuildById, getUserGuildInfo } =
useAppStore();
const {
guilds,
connectNewGuild,
patchGuildById,
getUserGuildInfo,
authorizeTwitter,
} = useAppStore();

if (typeof window !== 'undefined') {
useEffect(() => {
Expand Down Expand Up @@ -121,6 +127,15 @@ export default function ConnectCommunities() {
getUserGuildInfo(guildId);
setConfirmModalOpen(false);
};

const handleAuthorizeTwitter = () => {
authorizeTwitter(decodeUserTokenDiscordId(user));
};
const isAllTwitterPropertiesNull =
user &&
user.twitter &&
Object.values(user.twitter).every((value) => value == null);

return (
<>
<CustomModal
Expand Down Expand Up @@ -181,42 +196,63 @@ export default function ConnectCommunities() {
</div>
</CustomModal>
<div className="h-[268px] flex justify-center">
<div className="h-[200px] w-[200px]">
<div className="h-[200px] w-[500px]">
<p className="text-base whitespace-nowrap font-semibold pt-2">
Connect your communities
</p>
{guilds.length >= 1 ? (
<Tooltip
title={
<Typography fontSize={14}>
It will be possible to connect more communities soon.
</Typography>
}
arrow
placement="right"
>
<Paper className="text-center h-[200px] py-8 shadow-box rounded-xl mt-3 cursor-pointer opacity-60">
<p className="font-sm">Discord</p>
<FaDiscord size={60} className="mx-auto mt-2 mb-2" />
<div className="text-secondary text-base flex items-center justify-center">
<GoPlus size={20} className="mr-1" />
<p className="font-semibold">Connect</p>
</div>
</Paper>
</Tooltip>
) : (
<Paper
className="text-center h-[200px] py-8 shadow-box rounded-xl mt-3 cursor-pointer"
onClick={() => connectNewGuild()}
>
<p className="font-sm">Discord</p>
<FaDiscord size={60} className="mx-auto mt-2 mb-2" />
<div className="text-secondary text-base flex items-center justify-center">
<GoPlus size={20} className="mr-1" />
<p className="font-semibold">Connect</p>
<div className="p-4 flex flex-col space-y-3 md:space-y-0 md:flex-row md:space-x-4">
{isAllTwitterPropertiesNull ? (
<div className="h-[200px] w-[200px]">
<Paper
className="text-center h-[200px] py-8 shadow-box rounded-xl mt-3 cursor-pointer"
onClick={() => handleAuthorizeTwitter()}
>
<p className="font-sm">Twitter</p>
<BsTwitter size={55} className="mx-auto mt-2 mb-3" />
<div className="text-secondary text-base flex items-center justify-center">
<GoPlus size={20} className="mr-1" />
<p className="font-semibold">Connect</p>
</div>
</Paper>
</div>
</Paper>
)}
) : (
<></>
)}
<div className="h-[200px] w-[200px]">
{guilds.length >= 1 ? (
<Tooltip
title={
<Typography fontSize={14}>
It will be possible to connect more communities soon.
</Typography>
}
arrow
placement="right"
>
<Paper className="text-center h-[200px] py-8 shadow-box rounded-xl mt-3 cursor-pointer opacity-60">
<p className="font-sm">Discord</p>
<FaDiscord size={60} className="mx-auto mt-2 mb-2" />
<div className="text-secondary text-base flex items-center justify-center">
<GoPlus size={20} className="mr-1" />
<p className="font-semibold">Connect</p>
</div>
</Paper>
</Tooltip>
) : (
<Paper
className="text-center h-[200px] py-8 shadow-box rounded-xl mt-3 cursor-pointer"
onClick={() => connectNewGuild()}
>
<p className="font-sm">Discord</p>
<FaDiscord size={60} className="mx-auto mt-2 mb-2" />
<div className="text-secondary text-base flex items-center justify-center">
<GoPlus size={20} className="mr-1" />
<p className="font-semibold">Connect</p>
</div>
</Paper>
)}
</div>
</div>
</div>
</div>
</>
Expand Down
23 changes: 19 additions & 4 deletions src/components/pages/settings/ConnectedCommunitiesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import { Paper } from '@mui/material';
import useAppStore from '../../../store/useStore';
import { DISCONNECT_TYPE } from '../../../store/types/ISetting';
import { StorageService } from '../../../services/StorageService';
import { IUser } from '../../../utils/types';
import { ITwitter, IUser } from '../../../utils/types';

import {
setAmplitudeUserIdFromToken,
trackAmplitudeEvent,
} from '../../../helpers/amplitudeHelper';
import ConnectedTwitter from './ConnectedTwitter';

export default function ConnectedCommunitiesList({ guilds }: any) {
const { disconnecGuildById, getGuilds } = useAppStore();
Expand All @@ -22,6 +23,7 @@ export default function ConnectedCommunitiesList({ guilds }: any) {
const toggleModal = (e: boolean) => {
setOpen(e);
};
let user: IUser | undefined = StorageService.readLocalStorage<IUser>('user');
const notify = () => {
toast('The integration has been disconnected succesfully.', {
position: 'top-center',
Expand All @@ -42,9 +44,6 @@ export default function ConnectedCommunitiesList({ guilds }: any) {
notify();
getGuilds();

let user: IUser | undefined =
StorageService.readLocalStorage<IUser>('user');

setAmplitudeUserIdFromToken();

trackAmplitudeEvent({
Expand All @@ -61,6 +60,15 @@ export default function ConnectedCommunitiesList({ guilds }: any) {
});
};

function isAllTwitterPropertiesNull(twitter: ITwitter): boolean {
return (
twitter.twitterConnectedAt === null &&
twitter.twitterId === null &&
twitter.twitterProfileImageUrl === null &&
twitter.twitterUsername === null
);
}

return (
<>
{guilds && guilds.length > 0 ? (
Expand All @@ -82,6 +90,13 @@ export default function ConnectedCommunitiesList({ guilds }: any) {
</div>
))
: ''}
{user?.twitter && !isAllTwitterPropertiesNull(user.twitter) ? (
<div className="p-4">
<ConnectedTwitter twitter={user.twitter} />
</div>
) : (
<></>
)}
</div>
</div>
) : (
Expand Down
73 changes: 73 additions & 0 deletions src/components/pages/settings/ConnectedTwitter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Avatar, Paper } from '@mui/material';
import React from 'react';
import { ITwitter } from '../../../utils/types';
import useAppStore from '../../../store/useStore';
import { BsTwitter } from 'react-icons/bs';
import moment from 'moment';
import { StorageService } from '../../../services/StorageService';
import clsx from 'clsx';

interface IConnectedTwitter {
twitter?: ITwitter;
}

function ConnectedTwitter({ twitter }: IConnectedTwitter) {
const { disconnectTwitter, getUserInfo } = useAppStore();

const handleDisconnect = async () => {
await disconnectTwitter();
const {
twitterConnectedAt,
twitterId,
twitterProfileImageUrl,
twitterUsername,
} = await getUserInfo();

StorageService.updateLocalStorageWithObject('user', 'twitter', {
twitterConnectedAt,
twitterId,
twitterProfileImageUrl,
twitterUsername,
});
StorageService.removeLocalStorage('lastTwitterMetricsRefreshDate');
};

return (
<div>
<Paper className="text-center mx-auto h-[200px] border border-gray-300 w-[200px] py-3 shadow-box rounded-xl flex flex-col justify-between">
<div>
<div className="font-sm flex justify-center items-center text-center">
<p className="pr-1">Twitter</p>
<span
className={clsx(
'md:h-3 md:w-3 rounded-full',
twitter?.twitterIsInProgress ? 'bg-success' : 'bg-warning-500'
)}
/>
</div>
<BsTwitter size={30} className="mx-auto mt-2 mb-3" />
</div>
<div className="flex items-center space-x-1 mx-auto">
<Avatar
sx={{ height: 35, width: 35 }}
src={twitter?.twitterProfileImageUrl}
/>
<div className="text-left">
<p className="font-bold text-sm">{twitter?.twitterUsername}</p>
<p className="text-xs">{`Connected ${moment(
twitter?.twitterConnectedAt
).format('DD MMM yyyy')}`}</p>
</div>
</div>
<div
className="border-t text-secondary pt-2 font-semibold cursor-pointer"
onClick={handleDisconnect}
>
Disconnect
</div>
</Paper>
</div>
);
}

export default ConnectedTwitter;
26 changes: 26 additions & 0 deletions src/components/shared/TcAlert.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { render } from '@testing-library/react';
import TcAlert from './TcAlert'; // Replace with the correct import path

describe('TcAlert Component', () => {
it('renders the alert with custom message', () => {
const message = 'This is a test alert';

const { getByText } = render(<TcAlert severity="info">{message}</TcAlert>);

// Use the @testing-library/jest-dom assertions to check if the element is present
expect(getByText(message)).toBeInTheDocument();
});

it('renders the alert with a custom severity', () => {
const message = 'Custom severity alert';
const customSeverity = 'warning';

const { container } = render(
<TcAlert severity={customSeverity}>{message}</TcAlert>
);

// Check if the element has the correct class based on the custom severity
expect(container).toHaveTextContent(message);
});
});
20 changes: 20 additions & 0 deletions src/components/shared/TcAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Alert, AlertProps } from '@mui/material';
import React from 'react';

/**
* TcAlert Component
*
* A simple wrapper component for MUI's Alert component. It allows you to
* use MUI Alert with the flexibility to pass any valid AlertProps.
*
* @param {ITcAlertProps} props - The props for the TcAlert component.
* @returns {React.ReactElement} A React element representing the Alert component.
*/

interface ITcAlertProps extends AlertProps {}

function TcAlert({ ...rest }: ITcAlertProps) {
return <Alert {...rest} />;
}

export default TcAlert;
Loading

0 comments on commit 97392c1

Please sign in to comment.