diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index f7dcb05..de01095 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -132,15 +132,24 @@ public function show($id) return response()->json($category); } - // Public (no auth) deep link lookup by ID. Hidden (deeplink-only) categories - // are the only ones served here, since they are discovered via the deep link. + // Public (no auth) deep link lookup by ID. Serves any category — hidden + // (deeplink-only) ones included — so https://aramejan.com/category/{id} + // resolves for every shareable category. A visible category only lists its + // visible media; inside a hidden category everything is shown, since the + // deep link is the only way in. public function publicShow($id) { - $category = Category::with(['subcategories' => function ($q) { - $q->orderBy('name')->with(['media' => function ($m) { + $category = Category::findOrFail($id); + $includeHiddenMedia = (bool) $category->is_deeplink_only; + + $category->load(['subcategories' => function ($q) use ($includeHiddenMedia) { + $q->orderBy('name')->with(['media' => function ($m) use ($includeHiddenMedia) { + if (! $includeHiddenMedia) { + $m->where('is_deeplink_only', false); + } $m->with(['image', 'detailImage', 'tags'])->latest(); }]); - }])->where('id', $id)->where('is_deeplink_only', true)->firstOrFail(); + }]); return response()->json([ 'category' => [ @@ -169,7 +178,9 @@ public function grouped($id) { $category = Category::with(['subcategories' => function ($q) { $q->orderBy('name')->with(['media' => function ($m) { - $m->with(['image', 'detailImage', 'tags'])->latest(); + // Deep-link-only media stays out of the normal category screen. + $m->where('is_deeplink_only', false) + ->with(['image', 'detailImage', 'tags'])->latest(); }]); }])->findOrFail($id); diff --git a/app/Http/Controllers/MediaController.php b/app/Http/Controllers/MediaController.php index 9322928..5421211 100644 --- a/app/Http/Controllers/MediaController.php +++ b/app/Http/Controllers/MediaController.php @@ -99,6 +99,11 @@ public function index(Request $request) ->orderBy('created_at', 'desc') ->get(); + // Same external_url fallback as the normal listing below. + $hidden->each(function ($m) { + $m->external_url = $m->external_url ?: ($m->url ?? ''); + }); + return response()->json($hidden); } @@ -491,8 +496,9 @@ public function show($id) } // Public (no auth) deep link lookup by ID. Returns the same shape as show() -// minus the user-specific fields. Hidden (deeplink-only) media is included, -// since the deep link is how such media is discovered. +// minus the user-specific fields. Serves any publicly visible media plus the +// hidden (deeplink-only) ones, since the deep link is how those are discovered +// — https://aramejan.com/media/{id} must resolve for every shareable media. public function publicShow($id) { $media = Media::with([ @@ -506,7 +512,10 @@ public function publicShow($id) }, ]) ->where('id', $id) - ->where('is_deeplink_only', true) + ->where(function ($q) { + $q->where('visibility', 'public') + ->orWhere('is_deeplink_only', true); + }) ->firstOrFail(); return response()->json([ diff --git a/app/Http/Controllers/SubCategoryController.php b/app/Http/Controllers/SubCategoryController.php index 0d43bcd..5e28dac 100644 --- a/app/Http/Controllers/SubCategoryController.php +++ b/app/Http/Controllers/SubCategoryController.php @@ -9,7 +9,7 @@ class SubCategoryController extends Controller { public function index(Request $request, $categoryId = null) { - $query = SubCategory::with('category')->withCount('media'); + $query = SubCategory::with('category')->withCount(['media' => fn ($q) => $q->where('is_deeplink_only', false)]); $categoryId = $categoryId ?? $request->input('category_id'); @@ -51,7 +51,7 @@ public function store(Request $request) public function show($id) { - $subCategory = SubCategory::with('category')->withCount('media')->findOrFail($id); + $subCategory = SubCategory::with('category')->withCount(['media' => fn ($q) => $q->where('is_deeplink_only', false)])->findOrFail($id); return response()->json($subCategory); } diff --git a/config/deeplinks.php b/config/deeplinks.php index 192e7bf..d123f19 100644 --- a/config/deeplinks.php +++ b/config/deeplinks.php @@ -3,14 +3,16 @@ // Deep link / share URL configuration. The canonical domain the mobile app and // web share deep links in (e.g. https://aramejan.com), with {id} placeholders // (the ID of the media / category). +$base = rtrim(env('DEEPLINK_BASE_URL', 'https://aramejan.com'), '/'); + return [ // Canonical deep link domain (without trailing slash). - 'base_url' => env('DEEPLINK_BASE_URL', 'https://aramejan.com'), + 'base_url' => $base, // Pattern used for media deep links. - 'media_pattern' => env('DEEPLINK_MEDIA_PATTERN', 'https://aramejan.com/media/{id}'), + 'media_pattern' => env('DEEPLINK_MEDIA_PATTERN', $base . '/media/{id}'), // Pattern used for category deep links. - 'category_pattern' => env('DEEPLINK_CATEGORY_PATTERN', 'https://aramejan.com/category/{id}'), -]; \ No newline at end of file + 'category_pattern' => env('DEEPLINK_CATEGORY_PATTERN', $base . '/category/{id}'), +];