diff --git a/api/dbv1/models.go b/api/dbv1/models.go index 25cce869..5ff976b5 100644 --- a/api/dbv1/models.go +++ b/api/dbv1/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 package dbv1 diff --git a/api/swagger/swagger-v1.yaml b/api/swagger/swagger-v1.yaml index b598370d..30d14971 100644 --- a/api/swagger/swagger-v1.yaml +++ b/api/swagger/swagger-v1.yaml @@ -8201,9 +8201,12 @@ paths: - fan_remix_contest_ended - fan_remix_contest_ending_soon - fan_remix_contest_winners_selected + - fan_remix_contest_submission - artist_remix_contest_ended - artist_remix_contest_ending_soon - artist_remix_contest_submissions + - fan_club_text_post + - remix_contest_update responses: "200": description: Success @@ -16894,6 +16897,8 @@ components: - $ref: "#/components/schemas/artist_remix_contest_ending_soon_notification" - $ref: "#/components/schemas/artist_remix_contest_submissions_notification" - $ref: "#/components/schemas/fan_remix_contest_winners_selected_notification" + - $ref: "#/components/schemas/remix_contest_update_notification" + - $ref: "#/components/schemas/fan_remix_contest_submission_notification" - $ref: "#/components/schemas/fan_club_text_post_notification" discriminator: propertyName: type @@ -16939,6 +16944,8 @@ components: artist_remix_contest_ending_soon: "#/components/schemas/artist_remix_contest_ending_soon_notification" artist_remix_contest_submissions: "#/components/schemas/artist_remix_contest_submissions_notification" fan_remix_contest_winners_selected: "#/components/schemas/fan_remix_contest_winners_selected_notification" + remix_contest_update: "#/components/schemas/remix_contest_update_notification" + fan_remix_contest_submission: "#/components/schemas/fan_remix_contest_submission_notification" fan_club_text_post: "#/components/schemas/fan_club_text_post_notification" save_notification: required: @@ -17088,6 +17095,46 @@ components: type: array items: $ref: "#/components/schemas/fan_remix_contest_winners_selected_notification_action" + remix_contest_update_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: "#/components/schemas/remix_contest_update_notification_action" + fan_remix_contest_submission_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: "#/components/schemas/fan_remix_contest_submission_notification_action" tier_change_notification: required: - actions @@ -17516,6 +17563,38 @@ components: type: integer data: $ref: "#/components/schemas/fan_remix_contest_winners_selected_notification_action_data" + remix_contest_update_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer + data: + $ref: "#/components/schemas/remix_contest_update_notification_action_data" + fan_remix_contest_submission_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer + data: + $ref: "#/components/schemas/fan_remix_contest_submission_notification_action_data" send_tip_notification_action: required: - data @@ -18087,6 +18166,41 @@ components: type: string entity_id: type: string + remix_contest_update_notification_action_data: + required: + - event_id + - entity_id + - entity_user_id + - comment_id + type: object + properties: + event_id: + type: string + entity_id: + type: string + entity_user_id: + type: string + comment_id: + type: string + fan_remix_contest_submission_notification_action_data: + required: + - event_id + - entity_id + - entity_user_id + - submission_track_id + - submitter_user_id + type: object + properties: + event_id: + type: string + entity_id: + type: string + entity_user_id: + type: string + submission_track_id: + type: string + submitter_user_id: + type: string trending_notification_action: required: - data diff --git a/api/v1_notifications.go b/api/v1_notifications.go index da0d6e43..ac250322 100644 --- a/api/v1_notifications.go +++ b/api/v1_notifications.go @@ -15,7 +15,7 @@ import ( type GetNotificationsQueryParams struct { // Note that when limit is 0, we return 20 items to calculate unread count Limit int `query:"limit" default:"20" validate:"min=0,max=100"` - Types []string `query:"types" validate:"dive,oneof=announcement follow repost save remix cosign create tip_receive tip_send challenge_reward repost_of_repost save_of_repost tastemaker reaction supporter_dethroned supporter_rank_up supporting_rank_up milestone track_added_to_playlist tier_change trending trending_playlist trending_underground usdc_purchase_buyer usdc_purchase_seller track_added_to_purchased_album request_manager approve_manager_request claimable_reward comment comment_thread comment_mention comment_reaction listen_streak_reminder fan_remix_contest_started fan_remix_contest_ended fan_remix_contest_ending_soon fan_remix_contest_winners_selected artist_remix_contest_ended artist_remix_contest_ending_soon artist_remix_contest_submissions fan_club_text_post"` + Types []string `query:"types" validate:"dive,oneof=announcement follow repost save remix cosign create tip_receive tip_send challenge_reward repost_of_repost save_of_repost tastemaker reaction supporter_dethroned supporter_rank_up supporting_rank_up milestone track_added_to_playlist tier_change trending trending_playlist trending_underground usdc_purchase_buyer usdc_purchase_seller track_added_to_purchased_album request_manager approve_manager_request claimable_reward comment comment_thread comment_mention comment_reaction listen_streak_reminder fan_remix_contest_started fan_remix_contest_ended fan_remix_contest_ending_soon fan_remix_contest_winners_selected fan_remix_contest_submission artist_remix_contest_ended artist_remix_contest_ending_soon artist_remix_contest_submissions fan_club_text_post remix_contest_update"` GroupID string `query:"group_id" validate:"omitempty"` Timestamp float64 `query:"timestamp" validate:"omitempty,min=0"` } diff --git a/ddl/functions/handle_event.sql b/ddl/functions/handle_event.sql index 1bfff92a..351a0a99 100644 --- a/ddl/functions/handle_event.sql +++ b/ddl/functions/handle_event.sql @@ -14,8 +14,7 @@ begin -- Only create notifications if the track is public if track_is_public then - -- For each follower of the event creator and each user who favorited the track - -- Using UNION to ensure we don't get duplicate user_ids + -- Followers of the host, track savers, and users who follow the contest (event subscribers) for notified_user_id in select distinct user_id from ( @@ -33,6 +32,13 @@ begin and s.save_type = 'track' and s.is_current = true and s.is_delete = false + union + select sub.subscriber_id as user_id + from subscriptions sub + where sub.entity_type = 'Event' + and sub.user_id = new.event_id + and sub.is_current = true + and sub.is_delete = false ) as users_to_notify loop -- Create a notification for this user diff --git a/ddl/functions/handle_track.sql b/ddl/functions/handle_track.sql index cb2dd418..c0ec2fbe 100644 --- a/ddl/functions/handle_track.sql +++ b/ddl/functions/handle_track.sql @@ -35,8 +35,8 @@ begin count(*) from tracks t where t.is_current is true - and t.is_delete is false - and t.is_available is true + and t.is_delete = false + and t.is_available = true and t.stem_of is null and t.access_authorities is null and t.owner_id = new.owner_id @@ -101,7 +101,7 @@ begin raise warning 'An error occurred in %: %', tg_name, sqlerrm; end; - -- If new remix is a submission to an active remix contest, check for milestone notifications + -- If new remix is a submission to an active remix contest, milestones and follower alerts begin if track_should_notify(OLD, new, TG_OP) AND new.remix_of is not null THEN declare @@ -110,14 +110,15 @@ begin submission_count int; milestone int; parent_track_id int := (new.remix_of->'tracks'->0->>'parent_track_id')::int; + contest_follower int; begin - select event_id, user_id + select e.event_id, e.user_id into contest_event_id, contest_creator_id - from events - where event_type = 'remix_contest' - and is_deleted = false - and end_date > now() - and entity_id = parent_track_id + from events e + where e.event_type = 'remix_contest' + and e.is_deleted = false + and (e.end_date is null or e.end_date > now()) + and e.entity_id = parent_track_id limit 1; if contest_event_id is not null then @@ -127,6 +128,7 @@ begin join events e on e.event_type = 'remix_contest' and e.is_deleted = false and e.entity_id = parent_track_id + and e.event_id = contest_event_id where t.is_current = true and t.is_delete = false and t.remix_of is not null @@ -155,6 +157,44 @@ begin on conflict do nothing; END IF; END LOOP; + + -- Notify everyone following the contest (and the host) of a new submission, + -- excluding the submitter. The host is included even if they are not a + -- subscriber so they get a per-submission alert in addition to the + -- existing milestone-based artist_remix_contest_submissions notification. + for contest_follower in + select user_id from ( + select s.subscriber_id as user_id + from subscriptions s + where s.entity_type = 'Event' + and s.user_id = contest_event_id + and s.is_current = true + and s.is_delete = false + union + select contest_creator_id as user_id + ) recipients + where user_id != new.owner_id + loop + insert into notification + (blocknumber, user_ids, timestamp, type, specifier, group_id, data) + values + ( + new.blocknumber, + ARRAY [contest_follower], + new.updated_at, + 'fan_remix_contest_submission', + contest_follower, + 'fan_remix_contest_submission:' || contest_event_id || ':submission:' || new.track_id, + json_build_object( + 'event_id', contest_event_id, + 'entity_id', parent_track_id, + 'entity_user_id', contest_creator_id, + 'submission_track_id', new.track_id, + 'submitter_user_id', new.owner_id + ) + ) + on conflict do nothing; + end loop; end if; end; end if; @@ -165,8 +205,7 @@ begin -- If a track with an active remix contest transitions from unlisted to public, -- create fan_remix_contest_started notifications for the contest creator's - -- followers and the track's savers. Mirrors handle_event.sql for the case - -- where the contest was created while the track was still unlisted. + -- followers, the track's savers, and contest followers. begin if TG_OP = 'UPDATE' and OLD.is_unlisted = true and new.is_unlisted = false then insert into notification @@ -196,10 +235,17 @@ begin and s.save_type = 'track' and s.is_current = true and s.is_delete = false + union + select sub.subscriber_id as user_id + from subscriptions sub + where sub.entity_type = 'Event' + and sub.user_id = e.event_id + and sub.is_current = true + and sub.is_delete = false ) u on true where e.event_type = 'remix_contest' and e.is_deleted = false - and e.end_date > now() + and (e.end_date is null or e.end_date > now()) and e.entity_id = new.track_id on conflict do nothing; end if; @@ -211,9 +257,9 @@ begin return null; exception - when others then - raise warning 'An error occurred in %: %', tg_name, sqlerrm; - raise; + when others then + raise warning 'An error occurred in %: %', tg_name, sqlerrm; + raise; end; $$ language plpgsql;