Forum: - TipTap WYSIWYG editor with full toolbar - @emoji-mart/react emoji picker (consistent with tweets) - @mention autocomplete with user search API - Fix PHP 8.4 parse errors in Blade templates - Fix thread data display (paginator items) - Align forum page widths to max-w-5xl Discover: - Extract shared _nav.blade.php partial - Add missing nav links to for-you page - Add Following link for authenticated users Feed/Posts: - Post model, controllers, policies, migrations - Feed page components (PostComposer, FeedCard, etc) - Post reactions, comments, saves, reports, sharing - Scheduled publishing support - Link preview controller Profile: - Profile page components (ProfileHero, ProfileTabs) - Profile API controller Uploads: - Upload wizard enhancements - Scheduled publish picker - Studio status bar and readiness checklist
110 lines
3.9 KiB
PHP
110 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Posts;
|
|
|
|
use App\Models\Post;
|
|
use App\Models\PostTarget;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Creates achievement posts for significant milestones.
|
|
*
|
|
* Achievement types stored in post.meta.achievement_type:
|
|
* - follower_100 / follower_500 / follower_1000 / follower_5000 / follower_10000
|
|
* - artwork_100_views / artwork_1000_views / artwork_1000_favs
|
|
* - award_received
|
|
*/
|
|
class PostAchievementService
|
|
{
|
|
private const FOLLOWER_MILESTONES = [100, 500, 1_000, 5_000, 10_000, 50_000];
|
|
private const VIEW_MILESTONES = [100, 1_000, 10_000, 100_000];
|
|
private const FAV_MILESTONES = [10, 100, 500, 1_000];
|
|
|
|
/**
|
|
* Check if a follower count crosses a milestone and create an achievement post.
|
|
*/
|
|
public function maybeFollowerMilestone(User $user, int $newFollowerCount): void
|
|
{
|
|
foreach (self::FOLLOWER_MILESTONES as $milestone) {
|
|
if ($newFollowerCount === $milestone) {
|
|
$this->createAchievementPost($user, "follower_{$milestone}", [
|
|
'milestone' => $milestone,
|
|
'message' => "🎉 Just reached {$milestone} followers! Thank you all!",
|
|
]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if an artwork's view count crosses a milestone.
|
|
*/
|
|
public function maybeArtworkViewMilestone(User $user, int $artworkId, int $newViewCount): void
|
|
{
|
|
foreach (self::VIEW_MILESTONES as $milestone) {
|
|
if ($newViewCount === $milestone) {
|
|
$this->createAchievementPost($user, "artwork_{$milestone}_views", [
|
|
'artwork_id' => $artworkId,
|
|
'milestone' => $milestone,
|
|
'message' => "🎨 One of my artworks just hit {$milestone} views!",
|
|
], $artworkId);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create an achievement post for receiving an award.
|
|
*/
|
|
public function awardReceived(User $user, string $awardName, ?int $artworkId = null): void
|
|
{
|
|
$this->createAchievementPost($user, 'award_received', [
|
|
'award_name' => $awardName,
|
|
'artwork_id' => $artworkId,
|
|
'message' => "🏆 Just received the \"{$awardName}\" award!",
|
|
], $artworkId);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
private function createAchievementPost(
|
|
User $user,
|
|
string $achievementType,
|
|
array $meta,
|
|
?int $artworkId = null,
|
|
): void {
|
|
// Deduplicate: don't create the same achievement post twice
|
|
$exists = Post::where('user_id', $user->id)
|
|
->where('type', Post::TYPE_ACHIEVEMENT)
|
|
->whereJsonContains('meta->achievement_type', $achievementType)
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
return;
|
|
}
|
|
|
|
DB::transaction(function () use ($user, $achievementType, $meta, $artworkId) {
|
|
$post = Post::create([
|
|
'user_id' => $user->id,
|
|
'type' => Post::TYPE_ACHIEVEMENT,
|
|
'visibility' => Post::VISIBILITY_PUBLIC,
|
|
'body' => $meta['message'] ?? null,
|
|
'status' => Post::STATUS_PUBLISHED,
|
|
'meta' => array_merge($meta, ['achievement_type' => $achievementType]),
|
|
]);
|
|
|
|
if ($artworkId) {
|
|
PostTarget::create([
|
|
'post_id' => $post->id,
|
|
'target_type' => 'artwork',
|
|
'target_id' => $artworkId,
|
|
]);
|
|
}
|
|
});
|
|
|
|
Log::info("PostAchievementService: created '{$achievementType}' post for user #{$user->id}");
|
|
}
|
|
}
|