···11+<?php
22+33+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
44+// See the LICENCE file in the repository root for full licence text.
55+66+namespace App\Http\Controllers\InterOp;
77+88+use App\Http\Controllers\Controller;
99+use App\Jobs\Notifications\UserBeatmapsetNew;
1010+use App\Models\Beatmapset;
1111+1212+class BeatmapsetsController extends Controller
1313+{
1414+ public function broadcastNew($id)
1515+ {
1616+ $beatmapset = Beatmapset::findOrFail($id);
1717+1818+ (new UserBeatmapsetNew($beatmapset))->dispatch();
1919+2020+ return response(null, 204);
2121+ }
2222+}
+53
app/Jobs/Notifications/UserBeatmapsetNew.php
···11+<?php
22+33+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
44+// See the LICENCE file in the repository root for full licence text.
55+66+namespace App\Jobs\Notifications;
77+88+use App\Models\Beatmapset;
99+use App\Models\Follow;
1010+use App\Models\Notification;
1111+use App\Models\UserNotificationOption;
1212+1313+class UserBeatmapsetNew extends BroadcastNotificationBase
1414+{
1515+ const NOTIFICATION_OPTION_NAME = UserNotificationOption::BEATMAPSET_MODDING;
1616+1717+ protected $beatmapset;
1818+1919+ public static function getMailLink(Notification $notification): string
2020+ {
2121+ return route('beatmapsets.show', $notification->details['beatmapset_id']);
2222+ }
2323+2424+ public function __construct(Beatmapset $beatmapset)
2525+ {
2626+ parent::__construct($beatmapset->user);
2727+2828+ $this->beatmapset = $beatmapset;
2929+ }
3030+3131+ public function getDetails(): array
3232+ {
3333+ return [
3434+ 'beatmapset_id' => $this->beatmapset->getKey(),
3535+ 'title' => $this->beatmapset->title,
3636+ 'title_unicode' => $this->beatmapset->title_unicode,
3737+ 'cover_url' => $this->beatmapset->coverURL('card'),
3838+ ];
3939+ }
4040+4141+ public function getListeningUserIds(): array
4242+ {
4343+ return Follow::whereNotifiable($this->beatmapset->user)
4444+ ->where(['subtype' => 'mapping'])
4545+ ->pluck('user_id')
4646+ ->all();
4747+ }
4848+4949+ public function getNotifiable()
5050+ {
5151+ return $this->beatmapset->user;
5252+ }
5353+}
+9-6
app/Models/Follow.php
···1919{
2020 use Validatable;
21212222+ const SUBTYPES = [
2323+ 'comment' => Comment::COMMENTABLES,
2424+2525+ 'modding' => [
2626+ MorphMap::MAP[User::class],
2727+ ],
2828+ ];
2929+2230 public function scopeWhereNotifiable($query, $notifiable)
2331 {
2432 $query->where([
···6977 $this->validationErrors()->add('notifiable', 'required');
7078 }
71797272- if ($this->subtype === 'comment' && !in_array($this->notifiable_type, Comment::COMMENTABLES, true)) {
7373- $this->validationErrors()->add('notifiable_type', '.invalid');
7474- }
7575-7676- // FIXME: this should accept other types later.
7777- if ($this->subtype !== 'comment') {
8080+ if (!in_array($this->notifiable_type, static::SUBTYPES[$this->subtype] ?? [], true)) {
7881 $this->validationErrors()->add('subtype', '.invalid');
7982 }
8083
···3344import { forEach } from 'lodash';
5566-type Modifiers = (string | null | undefined)[] | Record<string, boolean | null | undefined>;
66+export type Modifiers = (string | null | undefined)[] | Record<string, boolean | null | undefined>;
7788-export function classWithModifiers(className: string, modifiers?: Modifiers) {
99- let ret = className;
88+export function classWithModifiers(className: string, modifiers?: Modifiers, modifiersOnly = false) {
99+ let ret = modifiersOnly ? '' : className;
10101111 if (modifiers != null) {
1212 if (Array.isArray(modifiers)) {
+13
resources/lang/en/follows.php
···11+<?php
22+33+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
44+// See the LICENCE file in the repository root for full licence text.
55+66+return [
77+ 'user' => [
88+ 'modding' => [
99+ 'to_0' => 'stop notifying me when this user uploads new beatmap',
1010+ 'to_1' => 'notify me when this user uploads new beatmap',
1111+ ],
1212+ ],
1313+];
+14
resources/lang/en/notifications.php
···143143 'user_achievement_unlock_group' => 'Medals unlocked!',
144144 ],
145145 ],
146146+147147+ 'user_modding' => [
148148+ 'user_beatmapset_new' => [
149149+ '_' => 'New beatmap',
150150+151151+ 'user_beatmapset_new' => 'New beatmap ":title" by :username',
152152+ 'user_beatmapset_new_compact' => 'New beatmap ":title"',
153153+ 'user_beatmapset_new_group' => 'New beatmaps by :username',
154154+ ],
155155+ ],
146156 ],
147157148158 'mail' => [
···200210 'user_achievement_unlock' => [
201211 'user_achievement_unlock' => ':username has unlocked a new medal, ":title"!',
202212 'user_achievement_unlock_self' => 'You\'ve unlocked a new medal, ":title"!',
213213+ ],
214214+215215+ 'user_modding' => [
216216+ 'user_beatmapset_new' => ':username has created new beatmaps',
203217 ],
204218 ],
205219 ],