import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../models/subscription_channel.dart'; import '../models/video_item.dart'; class HiddenChannelsService { HiddenChannelsService._(); static final HiddenChannelsService instance = HiddenChannelsService._(); static const String _key = 'youtube_personal.hidden_channels.v1'; final SharedPreferencesAsync _preferences = SharedPreferencesAsync(); final Map _hiddenById = {}; final Set _hiddenTitles = {}; bool _loaded = false; /// Lets Home/Channels invalidate their caches when the user hides or /// restores a channel. final ValueNotifier revision = ValueNotifier(0); Future ensureLoaded() async { if (_loaded) { return; } _loaded = true; final raw = await _preferences.getString(_key); if (raw == null || raw.isEmpty) { return; } try { final decoded = jsonDecode(raw); if (decoded is! List) { return; } for (final item in decoded) { if (item is! Map) { continue; } final id = item['id']; final title = item['title']; if (id is String && id.trim().isNotEmpty) { _hiddenById[id.trim()] = title is String ? title : ''; } if (title is String && title.trim().isNotEmpty) { _hiddenTitles.add(_normalize(title)); } } } catch (_) { _hiddenById.clear(); _hiddenTitles.clear(); } } bool isHiddenChannel({ String? channelId, String? title, }) { final id = channelId?.trim() ?? ''; if (id.isNotEmpty && _hiddenById.containsKey(id)) { return true; } final normalized = _normalize(title ?? ''); return normalized.isNotEmpty && _hiddenTitles.contains(normalized); } bool isHiddenVideo(VideoItem video) { return isHiddenChannel( channelId: video.channelId, title: video.author, ); } Future hide(SubscriptionChannel channel) async { await ensureLoaded(); _hiddenById[channel.channelId] = channel.title; final normalized = _normalize(channel.title); if (normalized.isNotEmpty) { _hiddenTitles.add(normalized); } await _save(); revision.value++; } Future restore(SubscriptionChannel channel) async { await ensureLoaded(); _hiddenById.remove(channel.channelId); final normalized = _normalize(channel.title); if (normalized.isNotEmpty) { // Keep a title hidden only if another hidden channel still uses it. final stillUsed = _hiddenById.values.any( (value) => _normalize(value) == normalized, ); if (!stillUsed) { _hiddenTitles.remove(normalized); } } await _save(); revision.value++; } Future _save() async { final payload = >[ for (final entry in _hiddenById.entries) { 'id': entry.key, 'title': entry.value, }, ]; await _preferences.setString( _key, jsonEncode(payload), ); } String _normalize(String value) { return value.trim().toLowerCase(); } }