-
Notifications
You must be signed in to change notification settings - Fork 49
feat(web): notification-system #1210
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
Merged
Merged
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
6f9a5db
feat(web): notification-system
nhestrompia 0927267
refactor: netlify function
nhestrompia 653793e
fix(web): make sure you are connected before settings notifications c…
kemuru 3650b3d
feat(web): add nonce to message
kemuru ee63a36
chore(web): yarn lock problem
kemuru 7cc3dd4
fix(web): change update settings to viem
kemuru bcbf73b
fix(web): payload json stringified
kemuru 2b6cfe4
fix(web): wrong netlify folder path
kemuru ef94986
chore(web): remove new lines on message
kemuru bdc9403
chore(web): remove logs and add settings type
kemuru b799786
refactor(web): account abstraction for verifymessage
kemuru b0ee92d
refactor(web): function name change
kemuru d1e49c6
fix: added error message
jaybuidl d4eb2da
feat: added telegram contact field, fixed netlify function sig verifi…
jaybuidl 889cef7
feat: use eip712 typed structured data signing
jaybuidl 2126efc
refactor: notification form filenames
jaybuidl 699e699
feat: close the settings popup if saved successfully
jaybuidl f72f819
chore: added types generation for the supabase db client
jaybuidl 1365a27
feat: hardened input validation
jaybuidl 657bb22
fix: user message
jaybuidl d1bd9d1
Merge branch 'dev' into feat(web)/notification-system
jaybuidl 967e78f
fix: interface changes after merge
jaybuidl 788a48b
chore: upgraded to the latest ui-components which decreases the opaci…
jaybuidl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { Handler } from "@netlify/functions"; | ||
import { verifyTypedData } from "viem"; | ||
import { createClient } from "@supabase/supabase-js"; | ||
import messages from "../../src/consts/eip712-messages"; | ||
|
||
const SUPABASE_KEY = process.env.SUPABASE_CLIENT_API_KEY; | ||
const SUPABASE_URL = process.env.SUPABASE_URL; | ||
const supabase = createClient(SUPABASE_URL!, SUPABASE_KEY!); | ||
|
||
export const handler: Handler = async (event) => { | ||
try { | ||
if (!event.body) { | ||
throw new Error("No body provided"); | ||
} | ||
// TODO: sanitize event.body | ||
const { email, telegram, nonce, address, signature } = JSON.parse(event.body); | ||
const lowerCaseAddress = address.toLowerCase() as `0x${string}`; | ||
// Note: this does NOT work for smart contract wallets, but viem's publicClient.verifyMessage() fails to verify atm. | ||
// https://viem.sh/docs/utilities/verifyTypedData.html | ||
const isValid = await verifyTypedData({ | ||
...messages.contactDetails(address, nonce, telegram, email), | ||
signature, | ||
}); | ||
if (!isValid) { | ||
// If the recovered address does not match the provided address, return an error | ||
throw new Error("Signature verification failed"); | ||
} | ||
// TODO: use typed supabase client | ||
// If the message is empty, delete the user record | ||
if (email === "" && telegram === "") { | ||
const { error } = await supabase.from("users").delete().match({ address: lowerCaseAddress }); | ||
if (error) throw error; | ||
return { statusCode: 200, body: JSON.stringify({ message: "Record deleted successfully." }) }; | ||
} | ||
// For a user matching this address, upsert the user record | ||
const { error } = await supabase | ||
.from("user-settings") | ||
.upsert({ address: lowerCaseAddress, email: email, telegram: telegram }) | ||
.match({ address: lowerCaseAddress }); | ||
if (error) throw error; | ||
return { statusCode: 200, body: JSON.stringify({ message: "Record updated successfully." }) }; | ||
} catch (err) { | ||
return { statusCode: 500, body: JSON.stringify({ message: `Error: ${err}` }) }; | ||
jaybuidl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
export default { | ||
contactDetails: (address: `0x${string}`, nonce, telegram = "", email = "") => | ||
({ | ||
address: address.toLowerCase() as `0x${string}`, | ||
domain: { | ||
name: "Kleros v2", | ||
version: "1", | ||
chainId: 421_613, | ||
}, | ||
types: { | ||
ContactDetails: [ | ||
{ name: "email", type: "string" }, | ||
{ name: "telegram", type: "string" }, | ||
{ name: "nonce", type: "string" }, | ||
], | ||
}, | ||
primaryType: "ContactDetails", | ||
message: { | ||
email, | ||
telegram, | ||
nonce, | ||
}, | ||
} as const), | ||
}; |
68 changes: 68 additions & 0 deletions
68
web/src/layout/Header/navbar/Menu/Settings/Notifications/FormContactDetails/FormContact.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import React, { Dispatch, SetStateAction, useMemo, useEffect } from "react"; | ||
import styled from "styled-components"; | ||
|
||
import { Field } from "@kleros/ui-components-library"; | ||
|
||
const StyledLabel = styled.label` | ||
display: flex; | ||
justify-content: space-between; | ||
margin-bottom: 10px; | ||
`; | ||
|
||
const StyledField = styled(Field)` | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
width: 100%; | ||
// TODO: make the placeholder text color lighter or ~80% opaque | ||
`; | ||
|
||
interface IForm { | ||
contactLabel: string; | ||
contactPlaceholder: string; | ||
contactInput: string; | ||
contactIsValid: boolean; | ||
setContactInput: Dispatch<SetStateAction<string>>; | ||
setContactIsValid: Dispatch<SetStateAction<boolean>>; | ||
validator: RegExp; | ||
} | ||
|
||
const FormContact: React.FC<IForm> = ({ | ||
contactLabel, | ||
contactPlaceholder, | ||
contactInput, | ||
contactIsValid, | ||
setContactInput, | ||
setContactIsValid, | ||
validator, | ||
}) => { | ||
useEffect(() => { | ||
setContactIsValid(validator.test(contactInput)); | ||
}, [contactInput, setContactIsValid, validator]); | ||
|
||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
event.preventDefault(); | ||
setContactInput(event.target.value); | ||
}; | ||
|
||
const fieldVariant = useMemo(() => { | ||
if (contactInput === "") { | ||
return undefined; | ||
} | ||
return contactIsValid ? "success" : "error"; | ||
}, [contactInput, contactIsValid]); | ||
|
||
return ( | ||
<> | ||
<StyledLabel>{contactLabel}</StyledLabel> | ||
<StyledField | ||
variant={fieldVariant} | ||
value={contactInput} | ||
onChange={handleInputChange} | ||
placeholder={contactPlaceholder} | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
export default FormContact; |
95 changes: 95 additions & 0 deletions
95
web/src/layout/Header/navbar/Menu/Settings/Notifications/FormContactDetails/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import React, { useState } from "react"; | ||
import styled from "styled-components"; | ||
import { useWalletClient, useAccount } from "wagmi"; | ||
import { Button } from "@kleros/ui-components-library"; | ||
import { uploadSettingsToSupabase } from "utils/uploadSettingsToSupabase"; | ||
import FormContact from "./FormContact"; | ||
import messages from "../../../../../../../consts/eip712-messages"; | ||
import { ISettings } from "../../types"; | ||
|
||
const FormContainer = styled.form` | ||
position: relative; | ||
display: flex; | ||
flex-direction: column; | ||
padding: 0 calc(12px + (32 - 12) * ((100vw - 300px) / (1250 - 300))); | ||
padding-bottom: 16px; | ||
`; | ||
|
||
const ButtonContainer = styled.div` | ||
display: flex; | ||
justify-content: end; | ||
`; | ||
|
||
const FormContactContainer = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
margin-bottom: 24px; | ||
`; | ||
|
||
const FormContactDetails: React.FC<ISettings> = ({ setIsSettingsOpen }) => { | ||
const [telegramInput, setTelegramInput] = useState<string>(""); | ||
const [emailInput, setEmailInput] = useState<string>(""); | ||
const [telegramIsValid, setTelegramIsValid] = useState<boolean>(false); | ||
const [emailIsValid, setEmailIsValid] = useState<boolean>(false); | ||
const { data: walletClient } = useWalletClient(); | ||
const { address } = useAccount(); | ||
|
||
// TODO: after the user is authenticated, retrieve the current email/telegram from the database and populate the form | ||
jaybuidl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
if (!address) { | ||
throw new Error("Missing address"); | ||
} | ||
const nonce = new Date().getTime().toString(); | ||
const signature = await walletClient?.signTypedData( | ||
messages.contactDetails(address, nonce, telegramInput, emailInput) | ||
); | ||
if (!signature) { | ||
throw new Error("Missing signature"); | ||
} | ||
const data = { | ||
email: emailInput, | ||
telegram: telegramInput, | ||
nonce, | ||
address, | ||
signature, | ||
}; | ||
const response = await uploadSettingsToSupabase(data); | ||
if (response.ok) { | ||
setIsSettingsOpen(false); | ||
} | ||
}; | ||
return ( | ||
<FormContainer onSubmit={handleSubmit}> | ||
<FormContactContainer> | ||
<FormContact | ||
contactLabel="Telegram" | ||
contactPlaceholder="@my_handle" | ||
contactInput={telegramInput} | ||
contactIsValid={telegramIsValid} | ||
setContactInput={setTelegramInput} | ||
setContactIsValid={setTelegramIsValid} | ||
validator={/^@[a-zA-Z0-9_]{5,32}$/} | ||
/> | ||
</FormContactContainer> | ||
<FormContactContainer> | ||
<FormContact | ||
contactLabel="Email" | ||
contactPlaceholder="[email protected]" | ||
contactInput={emailInput} | ||
contactIsValid={emailIsValid} | ||
setContactInput={setEmailInput} | ||
setContactIsValid={setEmailIsValid} | ||
validator={/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/} | ||
/> | ||
</FormContactContainer> | ||
|
||
<ButtonContainer> | ||
<Button text="Save" disabled={!emailIsValid && !telegramIsValid} /> | ||
</ButtonContainer> | ||
</FormContainer> | ||
); | ||
}; | ||
|
||
export default FormContactDetails; |
49 changes: 49 additions & 0 deletions
49
web/src/layout/Header/navbar/Menu/Settings/Notifications/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from "react"; | ||
import styled from "styled-components"; | ||
import { ISettings } from "../types"; | ||
|
||
import FormContactDetails from "./FormContactDetails"; | ||
import { EnsureChain } from "components/EnsureChain"; | ||
|
||
const Container = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
width: 100%; | ||
height: 100%; | ||
`; | ||
|
||
const HeaderContainer = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
font-size: 16px; | ||
font-weight: 600; | ||
color: ${({ theme }) => theme.primaryText}; | ||
margin-top: 16px; | ||
margin-bottom: 12px; | ||
`; | ||
|
||
const HeaderNotifs: React.FC = () => { | ||
return <HeaderContainer>Contact Details</HeaderContainer>; | ||
}; | ||
|
||
const EnsureChainContainer = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
padding-top: 16px; | ||
padding-bottom: 16px; | ||
`; | ||
|
||
const NotificationSettings: React.FC<ISettings> = ({ setIsSettingsOpen }) => { | ||
return ( | ||
<EnsureChainContainer> | ||
<EnsureChain> | ||
<Container> | ||
<HeaderNotifs /> | ||
<FormContactDetails setIsSettingsOpen={setIsSettingsOpen} /> | ||
</Container> | ||
</EnsureChain> | ||
</EnsureChainContainer> | ||
); | ||
}; | ||
|
||
export default NotificationSettings; |
45 changes: 0 additions & 45 deletions
45
web/src/layout/Header/navbar/Menu/Settings/SendMeNotifications/FormNotifs/FormEmail.tsx
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.