Skip to content
Open
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
2 changes: 1 addition & 1 deletion frontend/packages/client/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ REACT_APP_BACK_END_SERVER_API=http://localhost:5001
REACT_APP_IPFS_GATEWAY=https://dappercollectives.mypinata.cloud/ipfs
REACT_APP_TX_OPTIONS_ADDRS="0xc590d541b72f0ac1,0x72d401812f579e3e"
REACT_APP_HOTJAR_SITE_ID=0
REACT_APP_SENTRY_URL=REPLACE_SENTRY_URL
REACT_APP_SENTRY_URL=REPLACE_SENTRY_URL
12 changes: 6 additions & 6 deletions frontend/packages/client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ function App() {
<Router>
{/* using resetCSS to avoid conficts */}
<ChakraProvider theme={theme} resetCSS={false}>
<NotificationServiceProvider>
<NotificationModalProvider>
<ErrorHandler>
<NotificationModalProvider>
<ErrorHandler>
<NotificationServiceProvider>
<AppPages />
</ErrorHandler>
</NotificationModalProvider>
</NotificationServiceProvider>
</NotificationServiceProvider>
</ErrorHandler>
</NotificationModalProvider>
</ChakraProvider>
</Router>
</Web3Provider>
Expand Down
82 changes: 44 additions & 38 deletions frontend/packages/client/src/api/notificationService.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,50 @@ import {
LEANPLUM_EXPORT_KEY,
LEANPLUM_PROD_KEY,
} from 'api/constants';
import { subscribeNotificationIntentions } from 'const';
import Leanplum from 'leanplum-sdk';

const COMMUNITY_UPDATES_CATEGORY_ID = 1;
const options = {
method: 'GET',
headers: { accept: 'application/json' },
};

export const startLeanplum = () => {
/* @param: communitySubIntentions : [{communityId:"1", subscribeIntention:"subscribe"},{communityId:"2",subscribeIntention:"unsubscribe"}]
* @return: {communityId1:'True', communityId2:'False'}
*/
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

const getDesiredAttributes = (communitySubIntentions) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to convert the array in line 18 into a format of {community1:'True', community2:'False'}

return communitySubIntentions
.map(({ communityId, subscribeIntention }) => ({
key: `community${communityId}`,
value:
subscribeIntention === subscribeNotificationIntentions.subscribe
? 'True'
: 'False',
}))
.reduce((acc, curr) => {
const { key, value } = curr;
acc[key] = value;
return acc;
}, {});
};
export const startLeanplumForUser = (walletId) => {
const IS_LOCAL_DEV = process.env.REACT_APP_APP_ENV === 'development';

if (IS_LOCAL_DEV) {
Leanplum.setAppIdForDevelopmentMode(LEANPLUM_APP_ID, LEANPLUM_DEV_KEY);
} else {
Leanplum.setAppIdForProductionMode(LEANPLUM_APP_ID, LEANPLUM_PROD_KEY);
}

Leanplum.start();
};

export const setUserId = async (walletId) => {
try {
Leanplum.setUserId(walletId);
} catch (e) {
throw new Error(e);
}
return new Promise((resolve, reject) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning a promise is necessary for ensuring the sequence of execution, we want start leanplum first then get user settings

Leanplum.start((success) => {
Leanplum.setUserId(walletId);
if (success) {
resolve('leanplum user set');
} else {
reject('leanplum user not set');
}
});
});
};

export const getUserSettings = async (walletId) => {
Expand All @@ -47,61 +65,49 @@ export const getUserSettings = async (walletId) => {
if (!data.userAttributes) {
throw new Error('User Not Found');
}

const communitySubscriptions = [];
const res = {
email: data.userAttributes.email,
};
let isSubscribedToCommunityUpdates = true;

let isSubscribedFromCommunityUpdates = true;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming correction to align the names with the notion document

for (const property in data.userAttributes) {
if (property.includes('community')) {
if (
property.includes('community') &&
typeof data.userAttributes[property] == 'string'
) {
const communityId = property.split('community')[1];
communitySubscriptions.push({
communityId,
subscribed: data.userAttributes[property] === 'True',
});
}
}

res.communitySubscription = communitySubscriptions;

if (data.unsubscribeCategories) {
data.unsubscribeCategories.forEach((category) => {
if (parseInt(category.id) === COMMUNITY_UPDATES_CATEGORY_ID) {
isSubscribedToCommunityUpdates = false;
isSubscribedFromCommunityUpdates = false;
}
});
}

res.isSubscribedToCommunityUpdates = isSubscribedToCommunityUpdates;
res.isSubscribedFromCommunityUpdates = isSubscribedFromCommunityUpdates;
return res;
} catch (e) {
throw new Error(e);
}
// setTimeout(() => {
// throw new Error('get user setting error');
// }, 500);
};

export const setUserEmail = async (email) => {
try {
await Leanplum.setUserAttributes({ email });
return true;
} catch (e) {
throw new Error(e);
}
};

export const unsubscribeCommunity = async (communityId) => {
try {
Leanplum.setUserAttributes({ [`community${communityId}`]: 'False' });
return true;
} catch (e) {
throw new Error(e);
}
Leanplum.setUserAttributes({ email });
};

export const subscribeCommunity = async (communityId) => {
export const updateCommunitySubscription = async (communitySubIntentions) => {
const desiredAttributes = getDesiredAttributes(communitySubIntentions);
try {
Leanplum.setUserAttributes({ [`community${communityId}`]: 'True' });
Leanplum.setUserAttributes(desiredAttributes);
return true;
} catch (e) {
throw new Error(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,81 @@ export default function SubscribeCommunityButton({
const { openModal, closeModal } = useModalContext();
const { notificationSettings, updateCommunitySubscription } =
useNotificationServiceContext();
const subscribedToCommunity =
notificationSettings?.communitySubscription.some(
(c) => c.communityId === communityId && c.subscribed
);
const subscribedToEmails =
notificationSettings?.isSubscribedFromCommunityUpdates;
const { communitySubscription, isSubscribedFromCommunityUpdates, email } =
notificationSettings;
const subscribedToCommunity = communitySubscription.some(
(c) => c.communityId === communityId?.toString() && c.subscribed
);

const subscribedToEmails = isSubscribedFromCommunityUpdates;
const isSubscribed = subscribedToCommunity && subscribedToEmails;

const { popToast } = useToast();
const { user } = useWebContext();
const history = useHistory();
const openWalletErrorModal = () => {
openModal(
<ErrorModal
message="In order to subscribe to a community, you must first connect your Flow wallet."
title="Connect Wallet"
footerComponent={
<WalletConnect closeModal={closeModal} expandToContainer />
}
onClose={closeModal}
/>,
{ isErrorModal: true }
);
};
const showUpdateSuccessToast = (subscribeIntention) => {
const emailNotificationsState =
subscribeIntention === subscribeNotificationIntentions.subscribe
? 'on'
: 'off';

const onOpenModal = () => {
if (!user?.addr) {
openModal(
<ErrorModal
message="In order to subscribe to a community, you must first connect your Flow wallet."
title="Connect Wallet"
footerComponent={
<WalletConnect closeModal={closeModal} expandToContainer />
}
onClose={closeModal}
/>,
{ isErrorModal: true }
);
} else if (isSubscribed) {
updateCommunitySubscription(
popToast({
message: `Email notifications are turned ${emailNotificationsState}`,
messageType: 'success',
actionFn: () => history.push('/settings'),
actionText: 'Manage Settings',
});
};
const handleUpdateSubscription = async () => {
const subscribeIntention = isSubscribed
? subscribeNotificationIntentions.unsubscribe
: subscribeNotificationIntentions.subscribe;
await updateCommunitySubscription([
{
communityId,
subscribeNotificationIntentions.unsubscribe
);
const emailNotificationsState = subscribedToEmails ? 'on' : 'off';
popToast({
message: `Email notifications are turned ${emailNotificationsState}`,
messageType: 'info',
actionFn: () => history.push('/settings'),
actionText: 'Manage Settings',
});
subscribeIntention,
},
]);
showUpdateSuccessToast(subscribeIntention);
};
const handleSignUp = () => {
openModal(
<NotificationsModal
communityId={communityId}
onClose={closeModal}
onSuccess={showUpdateSuccessToast}
/>,
{
classNameModalContent: 'rounded modal-content-sm',
showCloseButton: false,
}
);
};
const handleBellButtonClick = () => {
//if user is not connect to wallet open error modal
if (!user?.addr) {
openWalletErrorModal();
return;
}
//if leanplum has user email handle the subscribe/unsubscribe and show toast
//if leanplumn doesn't have user email, show subscribe modal
if (email?.length > 0) {
handleUpdateSubscription();
} else {
openModal(
<NotificationsModal communityId={communityId} onClose={closeModal} />,
{
classNameModalContent: 'rounded modal-content-sm',
showCloseButton: false,
}
);
handleSignUp();
}
};

Expand Down Expand Up @@ -88,7 +119,7 @@ export default function SubscribeCommunityButton({
className={`column p-0 is-narrow-tablet is-full-mobile ${className}`}
style={containerStyles}
>
<button className={buttonClasses} onClick={onOpenModal}>
<button className={buttonClasses} onClick={handleBellButtonClick}>
<Svg name={buttonIcon} style={{ minWidth: 24 }} />
{size === 'full' && <span className="ml-2">{subscribeText}</span>}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function CommunitiesList({
const subscribeIntention = subscribed
? subscribeNotificationIntentions.unsubscribe
: subscribeNotificationIntentions.subscribe;
updateCommunitySubscription(communityId, subscribeIntention);
updateCommunitySubscription([{ communityId, subscribeIntention }]);
};
return (
<div className="py-5">
Expand Down Expand Up @@ -38,9 +38,13 @@ function CommunityListItem({
subscribed,
handleUpdateCommunitySubscription,
}) {
const { data: community, isLoading } = useCommunityDetails(communityId);
const {
data: community,
isLoading,
error,
} = useCommunityDetails(communityId);
const { name, logo, slug } = community ?? {};
if (isLoading) return null;
if (isLoading || error) return null;

return (
<li className="my-1 is-flex is-flex-direction-row has-background-light-grey rounded p-2 ">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { EMAIL_REGEX } from 'const';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

export default function EmailAddressInput({ email, setUserEmail }) {
const { register, handleSubmit, formState } = useForm({
export default function EmailAddressInput({ defaultEmail, setUserEmail }) {
const { register, handleSubmit, formState, setValue } = useForm({
defaultValues: {
email: email,
email: defaultEmail,
},
resolver: yupResolver(
yup.object().shape({
Expand All @@ -20,8 +20,12 @@ export default function EmailAddressInput({ email, setUserEmail }) {
})
),
});
const onSubmit = ({ email }) => {
setUserEmail(email);
const onSubmit = async ({ email }) => {
try {
await setUserEmail(email);
} catch (e) {
setValue('email', defaultEmail);
}
};
const { isSubmitting, errors, isDirty } = formState;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Fragment } from 'react';
import { useNotificationServiceContext } from 'contexts/NotificationService';
import CommunitiesList from './CommunitiesList';
import EmailAddressInput from './EmailAddressInput';
Expand Down Expand Up @@ -27,7 +26,7 @@ export default function NotificationSettingsSection() {
)}
{communitySubscription.length > 0 && (
<>
<EmailAddressInput email={email} setUserEmail={setUserEmail} />
<EmailAddressInput defaultEmail={email} setUserEmail={setUserEmail} />
<hr />
<ReceiveEmailNotificationsSwitch
isSubscribedFromCommunityUpdates={isSubscribedFromCommunityUpdates}
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/client/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export { default as TooltipMessage } from './TooltipMessage';
export { default as FadeInOut } from './FadeInOut';
export {
Error as ErrorModal,
Retry as RetryModal,
VoteConfirmation as VoteConfirmationModal,
CastingVote as CastingVoteModal,
VoteConfirmed as VoteConfirmedModal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const SignUpForm = ({ setErrorMessage, onSubscribe, onClose }) => {

const onSubmit = async (formData) => {
try {
onSubscribe(signupAll);
onSubscribe(formData.email, signupAll);
onClose();
} catch (e) {
setErrorMessage(e.message);
Expand Down Expand Up @@ -83,6 +83,7 @@ const SignUpForm = ({ setErrorMessage, onSubscribe, onClose }) => {
<b>Close</b>
</button>
<button
disabled={errors?.email}
className="button is-primary rounded-lg px-3 flex-1 ml-2"
onClick={handleSubmit(onSubmit)}
>
Expand Down
Loading