where('callable_type', get_class($profile)) ->where('callable_id', $profile->id) ->when(!$showPrivate, fn ($q) => $q->where('is_public', true)) ->where('is_paused', false) ->where('is_suppressed', false) ->whereNull('deleted_at') ->where(fn ($q) => $q->whereNull('till')->orWhere('till', '>=', now())) ->orderBy('till') ->get(); $locale = App::getLocale(); return $calls->map(function (Call $model) use ($locale) { $translation = $model->translations->firstWhere('locale', $locale) ?? $model->translations->first(); $tag = $model->tag; $tagContext = $tag?->contexts->first(); $tagCategory = $tagContext?->category; $tagColor = $tagCategory?->relatedColor ?? 'gray'; $tagName = $tag?->translation?->name ?? $tag?->name; $locationStr = null; if ($model->location) { $loc = $model->location; $parts = []; if ($loc->city) { $cityName = optional($loc->city->translations->first())->name; if ($cityName) $parts[] = $cityName; } if ($loc->country) { if ($loc->country->code === 'XX') { $parts[] = __('Location not specified'); } elseif ($loc->country->code) { $parts[] = strtoupper($loc->country->code); } } $locationStr = $parts ? implode(', ', $parts) : null; } $tagCategories = []; if ($tagCategory) { $ancestors = $tagCategory->ancestorsAndSelf()->get()->reverse(); foreach ($ancestors as $cat) { $catName = $cat->translations->firstWhere('locale', $locale)?->name ?? $cat->translations->first()?->name ?? ''; if ($catName) { $tagCategories[] = [ 'name' => $catName, 'color' => $cat->relatedColor ?? 'gray', ]; } } } return [ 'id' => $model->id, 'model' => Call::class, 'title' => $tagName ?? '', 'excerpt' => $translation?->content ?? '', 'photo' => $model->callable?->profile_photo_url ?? '', 'location' => $locationStr, 'tag_color' => $tagColor, 'tag_categories' => $tagCategories, 'callable_name' => $model->callable?->name ?? '', 'callable_location' => self::buildCallableLocation($model->callable), 'till' => $model->till, 'expiry_badge_text' => self::buildExpiryBadgeText($model->till), 'like_count' => $model->loveReactant?->reactionCounters->firstWhere('reaction_type_id', 3)?->count ?? 0, 'score' => 0, ]; }); } public static function buildCallableLocation($callable): ?string { if (!$callable || !method_exists($callable, 'locations')) { return null; } $firstLoc = $callable->locations->first(); if (!$firstLoc) { return null; } $cCity = optional($firstLoc->city?->translations->first())->name; $cDivision = optional($firstLoc->division?->translations->first())->name; $cCountry = optional($firstLoc->country?->translations->first())->name; return $cCity ?? $cDivision ?? $cCountry ?? null; } public static function buildExpiryBadgeText($till): ?string { if (!$till) { return null; } $expiryWarningDays = timebank_config('calls.expiry_warning_days', 7); if ($expiryWarningDays === null) { return null; } $daysLeft = (int) now()->startOfDay()->diffInDays(\Carbon\Carbon::parse($till)->startOfDay(), false); if ($daysLeft > $expiryWarningDays) { return null; } if ($daysLeft <= 0) { return __('Expires today'); } elseif ($daysLeft === 1) { return __('Expires tomorrow'); } else { return __('Expires in :days days', ['days' => $daysLeft]); } } }