Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/giant-files-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@audius/sdk": patch
---

Artist remix contest ended notification
8 changes: 8 additions & 0 deletions packages/common/src/adapters/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,14 @@ export const notificationFromSDK = (
...formatBaseNotification(notification)
}
}
case 'artist_remix_contest_ended': {
const data = notification.actions[0].data
return {
type: NotificationType.ArtistRemixContestEnded,
entityId: HashId.parse(data.entityId),
...formatBaseNotification(notification)
}
}
case 'remix_contest_ending_soon': {
const data = notification.actions[0].data
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const useNotificationValidTypes = () => {
ValidTypes.CommentReaction,
ValidTypes.ClaimableReward,
ValidTypes.ListenStreakReminder,
ValidTypes.ArtistRemixContestEnded,
ValidTypes.RemixContestStarted,
ValidTypes.RemixContestEnded,
ValidTypes.RemixContestEndingSoon
Expand Down
9 changes: 8 additions & 1 deletion packages/common/src/store/notifications/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export enum NotificationType {
CommentThread = 'CommentThread',
CommentMention = 'CommentMention',
CommentReaction = 'CommentReaction',
ListenStreakReminder = 'ListenStreakReminder'
ListenStreakReminder = 'ListenStreakReminder',
ArtistRemixContestEnded = 'ArtistRemixContestEnded'
}

export enum PushNotificationType {
Expand Down Expand Up @@ -716,6 +717,11 @@ export type RemixContestEndingSoonNotification = BaseNotification & {
entityUserId: ID
}

export type ArtistRemixContestEndedNotification = BaseNotification & {
type: NotificationType.ArtistRemixContestEnded
entityId: ID
}

export type Notification =
| AnnouncementNotification
| UserSubscriptionNotification
Expand Down Expand Up @@ -754,6 +760,7 @@ export type Notification =
| CommentMentionNotification
| CommentReactionNotification
| ListenStreakReminderNotification
| ArtistRemixContestEndedNotification

export type IdentityNotification = Omit<Notification, 'timestamp'> & {
timestamp: string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import logging
from datetime import datetime, timedelta

from integration_tests.utils import populate_mock_db
from src.models.notifications.notification import Notification
from src.tasks.remix_contest_notifications.artist_remix_contest_ended import (
create_artist_remix_contest_ended_notifications,
)
from src.utils.db_session import get_db

logger = logging.getLogger(__name__)

TEST_EVENT_CREATOR_ID = 1
TEST_TRACK_ID = 100


def test_artist_remix_contest_ended_notification(app):
"""Test that artist remix contest ended notification is created for the contest creator when the contest ends"""
with app.app_context():
db = get_db()

now = datetime.now()
end_date = now - timedelta(hours=1)

entities = {
"users": [
{
"user_id": TEST_EVENT_CREATOR_ID,
"is_current": True,
"created_at": now,
"updated_at": now,
}
],
"tracks": [
{
"track_id": TEST_TRACK_ID,
"owner_id": TEST_EVENT_CREATOR_ID,
"is_current": True,
"is_delete": False,
"created_at": now,
"updated_at": now,
}
],
"events": [
{
"event_id": 1,
"event_type": "remix_contest",
"user_id": TEST_EVENT_CREATOR_ID,
"entity_id": TEST_TRACK_ID,
"entity_type": "track",
"is_deleted": False,
"created_at": now,
"updated_at": now,
"end_date": end_date,
}
],
}
populate_mock_db(db, entities)

with db.scoped_session() as session:
create_artist_remix_contest_ended_notifications(session, now=now)
notifications = (
session.query(Notification)
.filter(Notification.type == "artist_remix_contest_ended")
.all()
)
assert len(notifications) == 1
notification = notifications[0]
assert notification.user_ids == [TEST_EVENT_CREATOR_ID]
assert notification.data["entity_id"] == TEST_TRACK_ID
29 changes: 29 additions & 0 deletions packages/discovery-provider/src/api/v1/models/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,34 @@ def format(self, value):
},
)

artist_remix_contest_ended_notification_action_data = ns.model(
"artist_remix_contest_ended_notification_action_data",
{
"entity_id": fields.String(required=True),
},
)
artist_remix_contest_ended_notification_action = ns.clone(
"artist_remix_contest_ended_notification_action",
notification_action_base,
{
"data": fields.Nested(
artist_remix_contest_ended_notification_action_data, required=True
)
},
)
artist_remix_contest_ended_notification = ns.clone(
"artist_remix_contest_ended_notification",
notification_base,
{
"actions": fields.List(
fields.Nested(
artist_remix_contest_ended_notification_action, required=True
),
required=True,
)
},
)

cosign_notification_action_data = ns.model(
"cosign_notification_action_data",
{
Expand Down Expand Up @@ -1056,6 +1084,7 @@ def format(self, value):
"remix_contest_started": remix_contest_started_notification,
"remix_contest_ended": remix_contest_ended_notification,
"remix_contest_ending_soon": remix_contest_ending_soon_notification,
"artist_remix_contest_ended": artist_remix_contest_ended_notification,
},
discriminator="type",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,22 @@ def extend_remix_contest_ending_soon(action: NotificationAction):
}


def extend_artist_remix_contest_ended(action: NotificationAction):
data = action["data"] # type: ignore
return {
"specifier": encode_int_id(int(action["specifier"])),
"type": action["type"],
"timestamp": (
datetime.timestamp(action["timestamp"])
if action["timestamp"]
else action["timestamp"]
),
"data": {
"entity_id": encode_int_id(data["entity_id"]),
},
}


notification_action_handler = {
"follow": extend_follow,
"repost": extend_repost,
Expand Down Expand Up @@ -851,4 +867,5 @@ def extend_remix_contest_ending_soon(action: NotificationAction):
"remix_contest_started": extend_remix_contest_started,
"remix_contest_ended": extend_remix_contest_ended,
"remix_contest_ending_soon": extend_remix_contest_ending_soon,
"artist_remix_contest_ended": extend_artist_remix_contest_ended,
}
6 changes: 6 additions & 0 deletions packages/discovery-provider/src/queries/get_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class NotificationType(str, Enum):
REMIX_CONTEST_STARTED = "remix_contest_started"
REMIX_CONTEST_ENDED = "remix_contest_ended"
REMIX_CONTEST_ENDING_SOON = "remix_contest_ending_soon"
ARTIST_REMIX_CONTEST_ENDED = "artist_remix_contest_ended"

def __str__(self) -> str:
return str.__str__(self)
Expand Down Expand Up @@ -500,6 +501,10 @@ class RemixContestEndingSoonNotification(TypedDict):
entity_id: int


class ArtistRemixContestEndedNotification(TypedDict):
entity_id: int


NotificationData = Union[
AnnouncementNotification,
FollowNotification,
Expand Down Expand Up @@ -535,6 +540,7 @@ class RemixContestEndingSoonNotification(TypedDict):
RemixContestStartedNotification,
RemixContestEndedNotification,
RemixContestEndingSoonNotification,
ArtistRemixContestEndedNotification,
]


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from datetime import datetime, timedelta

from src.models.events.event import Event, EventType
from src.models.notifications.notification import Notification
from src.utils.structured_logger import StructuredLogger

ARTIST_REMIX_CONTEST_ENDED = "artist_remix_contest_ended"
REMIX_CONTEST_ENDED_WINDOW_HOURS = 24

logger = StructuredLogger(__name__)


def get_artist_remix_contest_ended_group_id(event_id):
return f"{ARTIST_REMIX_CONTEST_ENDED}:{event_id}"


def create_artist_remix_contest_ended_notifications(session, now=None):
now = now or datetime.now()
window_start = now - timedelta(hours=REMIX_CONTEST_ENDED_WINDOW_HOURS)
window_end = now

ended_contests = (
session.query(Event)
.filter(
Event.event_type == EventType.remix_contest,
Event.is_deleted == False,
Event.end_date != None,
Event.end_date.between(window_start, window_end),
)
.all()
)

new_notifications = []
for event in ended_contests:
group_id = get_artist_remix_contest_ended_group_id(event.event_id)
exists = (
session.query(Notification)
.filter(
Notification.group_id == group_id,
Notification.type == ARTIST_REMIX_CONTEST_ENDED,
Notification.user_ids.any(event.user_id),
)
.first()
)
if not exists:
new_notification = Notification(
specifier=str(event.user_id),
group_id=group_id,
blocknumber=None,
user_ids=[event.user_id],
type=ARTIST_REMIX_CONTEST_ENDED,
data={
"entity_id": event.entity_id,
},
timestamp=now,
)
new_notifications.append(new_notification)
logger.info(
f"Inserting {len(new_notifications)} artist remix contest ended notifications"
)
session.add_all(new_notifications)
session.commit()
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
CommentMentionNotification,
CommentReactionNotification
} from './Notifications'
import { ArtistRemixContestEndedNotification } from './Notifications/ArtistRemixContestEndedNotification'
import { ListenStreakReminderNotification } from './Notifications/ListenStreakReminderNotification'
import { RemixContestEndedNotification } from './Notifications/RemixContestEndedNotification'
import { RemixContestEndingSoonNotification } from './Notifications/RemixContestEndingSoonNotification'
Expand Down Expand Up @@ -135,6 +136,10 @@ export const NotificationListItem = (props: NotificationListItemProps) => {
return <RemixContestStartedNotification notification={notification} />
case NotificationType.RemixContestEnded:
return <RemixContestEndedNotification notification={notification} />
case NotificationType.ArtistRemixContestEnded:
return (
<ArtistRemixContestEndedNotification notification={notification} />
)
case NotificationType.RemixContestEndingSoon:
return (
<RemixContestEndingSoonNotification notification={notification} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useCallback } from 'react'

import { useTrack } from '@audius/common/api'
import type { ArtistRemixContestEndedNotification as ArtistRemixContestEndedNotificationType } from '@audius/common/store'

import { IconTrophy } from '@audius/harmony-native'
import { useNotificationNavigation } from 'app/hooks/useNotificationNavigation'

import {
NotificationHeader,
NotificationText,
NotificationTile,
NotificationTitle
} from '../Notification'

const messages = {
title: 'Your Remix Contest Ended',
description:
"Your remix contest has ended. Don't forget to contact your winners!"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

are we using contact here under the 'pick your winners' milestone is in?

}

type ArtistRemixContestEndedNotificationProps = {
notification: ArtistRemixContestEndedNotificationType
}

export const ArtistRemixContestEndedNotification = (
props: ArtistRemixContestEndedNotificationProps
) => {
const { notification } = props
const { entityId } = notification

const navigation = useNotificationNavigation()
const { data: track } = useTrack(entityId)

const handlePress = useCallback(() => {
if (track) {
navigation.navigate(notification)
}
}, [track, navigation, notification])

if (!track) return null

return (
<NotificationTile notification={notification} onPress={handlePress}>
<NotificationHeader icon={IconTrophy}>
<NotificationTitle>{messages.title}</NotificationTitle>
</NotificationHeader>
<NotificationText>{messages.description}</NotificationText>
</NotificationTile>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ export * from './CommentNotification'
export * from './CommentThreadNotification'
export * from './CommentMentionNotification'
export * from './CommentReactionNotification'
export * from './RemixContestEndedNotification'
export * from './RemixContestEndingSoonNotification'
export * from './ArtistRemixContestEndedNotification'
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ models/AnnouncementNotificationActionData.ts
models/ApproveManagerRequestNotification.ts
models/ApproveManagerRequestNotificationAction.ts
models/ApproveManagerRequestNotificationActionData.ts
models/ArtistRemixContestEndedNotification.ts
models/ArtistRemixContestEndedNotificationAction.ts
models/ArtistRemixContestEndedNotificationActionData.ts
models/Attestation.ts
models/AttestationReponse.ts
models/ChallengeRewardNotification.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export const GetNotificationsValidTypesEnum = {
ListenStreakReminder: 'listen_streak_reminder',
RemixContestStarted: 'remix_contest_started',
RemixContestEnded: 'remix_contest_ended',
RemixContestEndingSoon: 'remix_contest_ending_soon'
RemixContestEndingSoon: 'remix_contest_ending_soon',
ArtistRemixContestEnded: 'artist_remix_contest_ended'
} as const;
export type GetNotificationsValidTypesEnum = typeof GetNotificationsValidTypesEnum[keyof typeof GetNotificationsValidTypesEnum];
Loading