1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Models\Chat;
7
8use App\Libraries\Notification\BatchIdentities;
9use App\Models\Traits\WithDbCursorHelper;
10use App\Models\User;
11use App\Models\UserNotification;
12use DB;
13use Illuminate\Database\Query\Expression;
14
15/**
16 * @property Channel $channel
17 * @property int $channel_id
18 * @property int|null $last_read_id
19 * @property User $user
20 * @property int $user_id
21 */
22class UserChannel extends Model
23{
24 use WithDbCursorHelper;
25
26 const DEFAULT_SORT = 'user_id_asc';
27
28 const SORTS = [
29 'user_id_asc' => [['column' => 'user_id', 'order' => 'ASC']],
30 ];
31
32 public $incrementing = false;
33 public $timestamps = false;
34
35 protected $primaryKey = ':composite';
36 protected $primaryKeys = ['user_id', 'channel_id'];
37
38 private ?int $lastReadIdToSet;
39
40 public function user()
41 {
42 return $this->belongsTo(User::class, 'user_id');
43 }
44
45 public function channel()
46 {
47 return $this->belongsTo(Channel::class, 'channel_id');
48 }
49
50 public function getAttribute($key)
51 {
52 return match ($key) {
53 'channel_id',
54 'user_id' => $this->getRawAttribute($key),
55
56 'last_read_id' => $this->getLastReadId(),
57
58 'channel',
59 'user' => $this->getRelationValue($key),
60 };
61 }
62
63 // Laravel has own hidden property
64 // TODO: https://github.com/ppy/osu-web/pull/9486#discussion_r1017831112
65 public function isHidden()
66 {
67 return (bool) $this->getRawAttribute('hidden');
68 }
69
70 public function markAsRead($messageId = null)
71 {
72 $this->lastReadIdToSet = get_int($messageId ?? Message::where('channel_id', $this->channel_id)->max('message_id'));
73
74 if ($this->lastReadIdToSet === null) {
75 return;
76 }
77
78 // this prevents the read marker from going backwards
79 $this->update(['last_read_id' => DB::raw("GREATEST(COALESCE(last_read_id, 0), $this->lastReadIdToSet)")]);
80
81 UserNotification::batchMarkAsRead($this->user, BatchIdentities::fromParams([
82 'identities' => [
83 [
84 'category' => 'channel',
85 'object_type' => 'channel',
86 'object_id' => $this->channel_id,
87 ],
88 ],
89 ]));
90 }
91
92 private function getLastReadId(): ?int
93 {
94 $value = $this->getRawAttribute('last_read_id');
95
96 // return the value we tried to set it to, not the query expression.
97 return $value instanceof Expression ? $this->lastReadIdToSet : $value;
98 }
99}