messages implemented
This commit is contained in:
@@ -7,14 +7,19 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use App\Models\Conversation;
|
||||
use App\Models\ConversationParticipant;
|
||||
use App\Models\Message;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Laravel\Scout\Searchable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable, SoftDeletes;
|
||||
use HasFactory, Notifiable, SoftDeletes, Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@@ -34,6 +39,7 @@ class User extends Authenticatable
|
||||
'needs_password_reset',
|
||||
'password',
|
||||
'role',
|
||||
'allow_messages_from',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -61,6 +67,7 @@ class User extends Authenticatable
|
||||
'username_changed_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'allow_messages_from' => 'string',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -106,11 +113,93 @@ class User extends Authenticatable
|
||||
return $this->hasMany(ProfileComment::class, 'profile_user_id');
|
||||
}
|
||||
|
||||
// ── Messaging ────────────────────────────────────────────────────────────
|
||||
|
||||
public function conversations(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Conversation::class, 'conversation_participants')
|
||||
->withPivot(['role', 'last_read_at', 'is_muted', 'is_archived', 'is_pinned', 'pinned_at', 'joined_at', 'left_at'])
|
||||
->wherePivotNull('left_at')
|
||||
->orderByPivot('joined_at', 'desc');
|
||||
}
|
||||
|
||||
public function conversationParticipants(): HasMany
|
||||
{
|
||||
return $this->hasMany(ConversationParticipant::class);
|
||||
}
|
||||
|
||||
public function sentMessages(): HasMany
|
||||
{
|
||||
return $this->hasMany(Message::class, 'sender_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this user allows receiving messages from the given user.
|
||||
*/
|
||||
public function allowsMessagesFrom(User $sender): bool
|
||||
{
|
||||
$pref = $this->allow_messages_from ?? 'everyone';
|
||||
|
||||
return match ($pref) {
|
||||
'everyone' => true,
|
||||
'followers' => $this->followers()->where('follower_id', $sender->id)->exists(),
|
||||
'mutual_followers' => $this->followers()->where('follower_id', $sender->id)->exists()
|
||||
&& $this->following()->where('user_id', $sender->id)->exists(),
|
||||
'nobody' => false,
|
||||
default => true,
|
||||
};
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Artworks this user has added to their favourites. */
|
||||
public function favouriteArtworks(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Artwork::class, 'artwork_favourites', 'user_id', 'artwork_id')
|
||||
->withPivot('legacy_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function hasRole(string $role): bool
|
||||
{
|
||||
return strtolower((string) ($this->role ?? '')) === strtolower($role);
|
||||
}
|
||||
|
||||
// ─── Follow helpers ───────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Whether $viewerId is following this user.
|
||||
* Uses a single indexed lookup – safe to call on every profile render.
|
||||
*/
|
||||
public function isFollowedBy(int $viewerId): bool
|
||||
{
|
||||
if ($viewerId === $this->id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DB::table('user_followers')
|
||||
->where('user_id', $this->id)
|
||||
->where('follower_id', $viewerId)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached follower count from user_statistics.
|
||||
* Returns 0 if the statistics row does not exist yet.
|
||||
*/
|
||||
public function getFollowersCountAttribute(): int
|
||||
{
|
||||
return (int) ($this->statistics?->followers_count ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached following count from user_statistics.
|
||||
*/
|
||||
public function getFollowingCountAttribute(): int
|
||||
{
|
||||
return (int) ($this->statistics?->following_count ?? 0);
|
||||
}
|
||||
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return $this->hasRole('admin');
|
||||
@@ -120,4 +209,42 @@ class User extends Authenticatable
|
||||
{
|
||||
return $this->hasRole('moderator');
|
||||
}
|
||||
|
||||
// ─── Meilisearch ──────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Only index active users (not soft-deleted, is_active = true).
|
||||
*/
|
||||
public function shouldBeSearchable(): bool
|
||||
{
|
||||
return (bool) $this->is_active && ! $this->trashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data indexed in Meilisearch.
|
||||
* Includes all v2 stat counters for top-creator sorting.
|
||||
*/
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
$stats = $this->statistics;
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'username' => strtolower((string) ($this->username ?? '')),
|
||||
'name' => $this->name,
|
||||
// Upload activity
|
||||
'uploads_count' => (int) ($stats?->uploads_count ?? 0),
|
||||
// Creator-received metrics
|
||||
'downloads_received_count' => (int) ($stats?->downloads_received_count ?? 0),
|
||||
'artwork_views_received_count' => (int) ($stats?->artwork_views_received_count ?? 0),
|
||||
'awards_received_count' => (int) ($stats?->awards_received_count ?? 0),
|
||||
'favorites_received_count' => (int) ($stats?->favorites_received_count ?? 0),
|
||||
'comments_received_count' => (int) ($stats?->comments_received_count ?? 0),
|
||||
'reactions_received_count' => (int) ($stats?->reactions_received_count ?? 0),
|
||||
// Social
|
||||
'followers_count' => (int) ($stats?->followers_count ?? 0),
|
||||
'following_count' => (int) ($stats?->following_count ?? 0),
|
||||
'created_at' => $this->created_at?->toISOString(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user