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

feat: integrate push notifications sdk #1233

Merged
merged 54 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
e615fe8
feat: integrate push notifications sdk
andyesp Sep 4, 2023
2de2868
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Sep 4, 2023
220b6b1
feat: get user notifications
andyesp Sep 4, 2023
cbb4910
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Sep 5, 2023
b0effeb
feat: send broadcast notification
andyesp Sep 14, 2023
3279ae1
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Sep 14, 2023
4faacac
fix: login component import
andyesp Sep 14, 2023
1d9f3cc
feat: notification service with mock functions
andyesp Sep 18, 2023
8ec5694
feat: add notifications feed component in navigation
andyesp Sep 18, 2023
29dfdc6
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Sep 19, 2023
3dc221e
fix: tests crypto not found error with pushapi import
andyesp Sep 19, 2023
e835d78
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Sep 19, 2023
8775de7
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Sep 20, 2023
1209d17
feat: add empty state, pagination and more styles
andyesp Sep 20, 2023
df21022
fix: boolean string env var function
andyesp Sep 21, 2023
9a52aed
feat: add proposal enacted notification
andyesp Sep 25, 2023
29516e1
feat: send notification on coauthor request
andyesp Sep 25, 2023
be726c0
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Sep 25, 2023
ac73fdc
chore: update env vars
andyesp Sep 25, 2023
2ad9f0a
feat: voting ended notification to authors
andyesp Sep 25, 2023
bc7eaf3
fix: try-catch all notifications send methods
andyesp Sep 25, 2023
499edbc
fix: mock notification service function
andyesp Sep 25, 2023
71a9fae
feat: move get user feed to notification service
andyesp Sep 25, 2023
6deaf05
chore: remove old grant check test in budgeting test
andyesp Sep 25, 2023
26ee240
feat: add i18n texts
andyesp Sep 26, 2023
69fd5c2
feat: add signature state
andyesp Sep 26, 2023
796522e
feat: add custom types and icons to notification item list
andyesp Sep 26, 2023
6c1db53
feat: improve notification list styles
andyesp Sep 27, 2023
6e4b8ad
feat: add notification icons
andyesp Sep 28, 2023
d4230f2
feat: send notification to voters
andyesp Sep 28, 2023
e115d8e
refactor: move types and env vars
andyesp Sep 28, 2023
c5d4eba
fix: voting power to pass constants names
andyesp Sep 29, 2023
554c2ad
feat: move notification form to debug
andyesp Sep 29, 2023
1da431c
fix: notification button position and search icon color
andyesp Sep 29, 2023
2654b9d
fix: center notifications feed states
andyesp Oct 2, 2023
9e3857d
fix: support multiple chain ids env var
andyesp Oct 2, 2023
6860187
feat: send proposal enacted notification only to grants
andyesp Oct 2, 2023
20405b0
fix: subscriptions query keys and cleanup
andyesp Oct 2, 2023
d730a2c
feat: add function to get push notifications env
andyesp Oct 2, 2023
f197a7c
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Oct 5, 2023
5471325
refactor: code cleanup
andyesp Oct 5, 2023
8c076bf
feat: add notification dot ui
andyesp Oct 5, 2023
d452436
feat: add new notification styles in feed
andyesp Oct 6, 2023
fa30bfb
remove console.logs
andyesp Oct 6, 2023
9bdf65e
feat: integrate mark as read
andyesp Oct 6, 2023
f22578a
fix: save first last notification id
andyesp Oct 10, 2023
0345f79
feat: show dot on first view
andyesp Oct 10, 2023
e01649f
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Oct 10, 2023
349124b
feat: save last notification if there is none in db and has one
andyesp Oct 10, 2023
0752976
remove notifications page
andyesp Oct 10, 2023
6396a16
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Oct 10, 2023
e0e5806
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Oct 16, 2023
b6dd1b4
fix: build error
andyesp Oct 17, 2023
0dc27ac
Merge remote-tracking branch 'origin' into feat/notifications-push
andyesp Oct 19, 2023
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
12 changes: 9 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ GATSBY_SNAPSHOT_DURATION=600
GATSBY_SNAPSHOT_DELEGATE_CONTRACT_ADDRESS=0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446
GATSBY_SNAPSHOT_ADDRESS=
SNAPSHOT_PRIVATE_KEY=
SNAPSHOT_API_KEY=

# Snapshot Status
SNAPSHOT_STATUS_ENABLED='false'
Expand All @@ -49,9 +50,6 @@ GATSBY_DISCOURSE_API=https://forum.decentraland.org
GATSBY_DISCOURSE_CONNECT_THREAD=0
DISCOURSE_API_KEY=

# Decentraland's mailchimp integration
GATSBY_DECENTRALAND_API=https://subscription.decentraland.org

# Required voting power to pass a proposal by type
GATSBY_VOTING_POWER_TO_PASS_LINKED_WEARABLES=0
GATSBY_VOTING_POWER_TO_PASS_CATALYST=0
Expand All @@ -63,6 +61,7 @@ GATSBY_VOTING_POWER_TO_PASS_GOVERNANCE=0
GATSBY_VOTING_POWER_TO_PASS_PITCH=0
GATSBY_VOTING_POWER_TO_PASS_TENDER=0
GATSBY_VOTING_POWER_TO_PASS_HIRING=0
GATSBY_VOTING_POWER_TO_PASS_GRANT=0

# Duration for governance process proposals
GATSBY_DURATION_POLL=1200
Expand All @@ -87,6 +86,7 @@ GATSBY_SUBMISSION_THRESHOLD_HIRING=100
# Single Sign On
GATSBY_SSO_URL="https://id.decentraland.zone"

# Bidding & Tendering
MINIMUM_BIDS_TO_PUBLISH=2

# Segment integration
Expand All @@ -110,3 +110,9 @@ DAO_ROLLBAR_TOKEN=""
# Newsletter
BEEHIIV_API_KEY=
BEEHIIV_PUBLICATION_ID=

# Notifications
NOTIFICATIONS_SERVICE_ENABLED=false
PUSH_API_URL=https://backend-staging.epns.io
PUSH_CHANNEL_OWNER_PK=
GATSBY_PUSH_CHANNEL_ID=
2 changes: 2 additions & 0 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
os: false,
stream: false,
util: false,
zlib: false,
url: false,
},
},
})
Expand Down
12,843 changes: 7,018 additions & 5,825 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@dcl/ui-env": "1.2.1",
"@jparnaudo/react-crypto-icons": "^1.0.5",
"@otterspace-xyz/contracts": "^2.7.3",
"@pushprotocol/restapi": "^1.4.20",
"@snapshot-labs/snapshot.js": "0.5.5",
"@tanstack/react-query": "^4.29.7",
"autoprefixer": "^10.4.4",
Expand Down
12 changes: 12 additions & 0 deletions src/back/models/UserNotificationConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Model } from 'decentraland-gatsby/dist/entities/Database/model'

export type UserNotificationConfigAttributes = {
address: string
last_notification_id: number
}

export default class UserNotificationConfigModel extends Model<UserNotificationConfigAttributes> {
static tableName = 'user_notification_config'
static withTimestamps = false
static primaryKey = 'address'
}
71 changes: 71 additions & 0 deletions src/back/routes/notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { WithAuth, auth } from 'decentraland-gatsby/dist/entities/Auth/middleware'
import RequestError from 'decentraland-gatsby/dist/entities/Route/error'
import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle'
import routes from 'decentraland-gatsby/dist/entities/Route/routes'
import { Request } from 'express'

import { NotificationCustomType } from '../../shared/types/notifications'
import { NotificationType } from '../../utils/notifications'
import UserNotificationConfigModel, { UserNotificationConfigAttributes } from '../models/UserNotificationConfig'
import { NotificationService } from '../services/notification'
import { validateDebugAddress } from '../utils/validations'

export default routes((router) => {
const withAuth = auth()
router.post('/notifications/send', withAuth, handleAPI(sendNotification))
router.get('/notifications/user/:address', handleAPI(getUserFeed))
router.get('/notifications/last-notification', withAuth, handleAPI(getUserLastNotification))
router.post('/notifications/last-notification', withAuth, handleAPI(updateUserLastNotification))
})

async function sendNotification(req: WithAuth) {
validateDebugAddress(req.auth)
const { title, body, recipient, url, type } = req.body

if (type === NotificationType.TARGET && !recipient) {
throw new RequestError('Target type needs recipient', RequestError.BadRequest)
}

if (!title || !body) {
throw new RequestError('Invalid data', RequestError.BadRequest)
}

return await NotificationService.sendNotification({
type,
title,
body,
recipient,
url,
customType: NotificationCustomType.Announcement,
})
}

async function getUserFeed(req: Request) {
const address = req.params.address
if (!address) {
throw new RequestError('Missing user', RequestError.BadRequest)
}

return await NotificationService.getUserFeed(address)
}

async function getUserLastNotification(req: WithAuth) {
const config = await UserNotificationConfigModel.findOne<UserNotificationConfigAttributes>({ address: req.auth })
if (!config) {
throw new RequestError('User notification not found', RequestError.NotFound)
}

return config.last_notification_id
}

async function updateUserLastNotification(req: WithAuth) {
const last_notification_id = req.body.last_notification_id
if (!last_notification_id) {
throw new RequestError('Missing Notification ID', RequestError.BadRequest)
}

return await UserNotificationConfigModel.upsert({
address: req.auth,
last_notification_id,
})
}
10 changes: 8 additions & 2 deletions src/back/routes/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import { ProposalInCreation, ProposalService } from '../../services/ProposalServ
import { getProfile } from '../../utils/Catalyst'
import Time from '../../utils/date/Time'
import { ErrorCategory } from '../../utils/errorCategories'
import { NotificationService } from '../services/notification'
import { validateAddress, validateProposalId } from '../utils/validations'

export default routes((route) => {
Expand Down Expand Up @@ -521,10 +522,12 @@ export async function updateProposalStatus(req: WithAuth<Request<{ proposal: str
updated_at: new Date(),
}

if (update.status === ProposalStatus.Enacted) {
const isEnactedStatus = update.status === ProposalStatus.Enacted
const isGrantProposal = proposal.type === ProposalType.Grant
if (isEnactedStatus) {
update.enacted = true
update.enacted_by = user
if (proposal.type == ProposalType.Grant) {
if (isGrantProposal) {
const { vesting_addresses } = configuration
if (!vesting_addresses || vesting_addresses.length === 0) {
throw new RequestError('Vesting addresses are required for grant proposals', RequestError.BadRequest)
Expand Down Expand Up @@ -552,6 +555,9 @@ export async function updateProposalStatus(req: WithAuth<Request<{ proposal: str
}

await ProposalModel.update<ProposalAttributes>(update, { id })
if (isEnactedStatus && isGrantProposal) {
NotificationService.grantProposalEnacted(proposal)
}

ProposalService.commentProposalUpdateInDiscourse(id)

Expand Down
2 changes: 1 addition & 1 deletion src/back/routes/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default routes((router) => {
router.get('/snapshot/proposal-scores/:proposalSnapshotId', handleAPI(getProposalScores))
})

async function getStatus(req: Request) {
async function getStatus() {
return await SnapshotStatusService.getStatus()
}

Expand Down
7 changes: 6 additions & 1 deletion src/back/routes/votes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import RequestError from 'decentraland-gatsby/dist/entities/Route/error'
import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle'
import routes from 'decentraland-gatsby/dist/entities/Route/routes'
import { Request } from 'express'
Expand Down Expand Up @@ -28,7 +29,7 @@ export async function getProposalVotes(req: Request<{ proposal: string }>) {
const id = validateProposalId(req.params.proposal)

const proposal = await ProposalService.getProposal(id)
const latestVotes = await VoteService.getVotes(id)
const latestVotes = await VoteService.getVotes(proposal.id)

if (!!latestVotes.hash && Time.date(proposal.finish_at).getTime() + Time.Hour < Date.now() && !refresh) {
return latestVotes.votes
Expand Down Expand Up @@ -66,6 +67,10 @@ export async function updateSnapshotProposalVotes(proposal: ProposalAttributes,
}

export async function getCachedVotes(req: Request) {
if (!req.query.id) {
throw new RequestError('Missing proposal IDs', RequestError.BadRequest)
}

const list = toProposalIds(req.query.id as string[])
const scores = await VotesModel.findAny(list)
return scores.reduce((result, vote) => {
Expand Down
5 changes: 5 additions & 0 deletions src/back/services/coauthor.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import CoauthorModel from '../../entities/Coauthor/model'
import { CoauthorStatus } from '../../entities/Coauthor/types'
import { ProposalAttributes } from '../../entities/Proposal/types'
import { isSameAddress } from '../../entities/Snapshot/utils'

export class CoauthorService {
static async getAllFromProposalId(proposalId: ProposalAttributes['id'], status?: CoauthorStatus) {
return await CoauthorModel.findCoauthors(proposalId, status || CoauthorStatus.APPROVED)
}

static async isCoauthor(proposalId: string, address: string): Promise<boolean> {
const coauthors = await CoauthorModel.findCoauthors(proposalId, CoauthorStatus.APPROVED)
return !!coauthors.find((coauthor) => isSameAddress(coauthor.address, address))
Expand Down
Loading
Loading