using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; namespace ytLive.Models; public enum SocialService { YouTube, Twitch, X, Instagram, TikTok, Facebook, Discord, Kick, Threads, Bluesky, GitHub, LinkedIn, Pinterest, Snapchat, Reddit, WhatsApp, Telegram, Link, Website } public enum SocialBarPosition { Top, Bottom } public enum SocialBarJustify { Left, Center, Right } public sealed class SocialEntry : INotifyPropertyChanged { public SocialService Service { get; init; } public string Handle { get; init; } = string.Empty; public string ProfileUrl { get; init; } = string.Empty; public string ServiceName => Service.ToString(); public string Initials => SocialServiceIcons.InitialsFor(Service); public string AccentColor => SocialServiceIcons.ColorFor(Service); public event PropertyChangedEventHandler? PropertyChanged; private void Raise([CallerMemberName] string? name = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } /// /// The app-wide social bar config — a global resource (like the webcam) whose /// per-scene presence is toggled by . The creator /// provides handles/URLs; the app validates each by looking up the social page /// before adding it. Freemium: YT + 1 other. Premium: as many as fit one line. /// public sealed class SocialsConfig : INotifyPropertyChanged { public ObservableCollection Entries { get; } = new(); private SocialBarPosition _barPosition = SocialBarPosition.Bottom; public SocialBarPosition BarPosition { get => _barPosition; set => Set(ref _barPosition, value); } private SocialBarJustify _barJustify = SocialBarJustify.Center; public SocialBarJustify BarJustify { get => _barJustify; set => Set(ref _barJustify, value); } public event PropertyChangedEventHandler? PropertyChanged; private bool Set(ref T field, T value, [CallerMemberName] string? name = null) { if (EqualityComparer.Default.Equals(field, value)) return false; field = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); return true; } } public static class SocialServiceIcons { public static string InitialsFor(SocialService service) => service switch { SocialService.YouTube => "YT", SocialService.Twitch => "TW", SocialService.X => "X", SocialService.Instagram => "IG", SocialService.TikTok => "TT", SocialService.Facebook => "FB", SocialService.Discord => "DC", SocialService.Kick => "K", SocialService.Threads => "TH", SocialService.Bluesky => "BS", SocialService.GitHub => "GH", SocialService.LinkedIn => "LI", SocialService.Pinterest => "PN", SocialService.Snapchat => "SC", SocialService.Reddit => "RD", SocialService.WhatsApp => "WA", SocialService.Telegram => "TG", SocialService.Link => "L", _ => "★", }; public static string ColorFor(SocialService service) => service switch { SocialService.YouTube => "#FF0000", SocialService.Twitch => "#9146FF", SocialService.X => "#000000", SocialService.Instagram => "#E4405F", SocialService.TikTok => "#010101", SocialService.Facebook => "#1877F2", SocialService.Discord => "#5865F2", SocialService.Kick => "#53FC18", SocialService.Threads => "#010101", SocialService.Bluesky => "#0085FF", SocialService.GitHub => "#181717", SocialService.LinkedIn => "#0A66C2", SocialService.Pinterest => "#BD081C", SocialService.Snapchat => "#FFFC00", SocialService.Reddit => "#FF4500", SocialService.WhatsApp => "#25D366", SocialService.Telegram => "#26A5E4", _ => "#e94560", }; public static string CanonicalUrlFor(SocialService service, string handle) { var h = handle.Trim().TrimStart('@'); return service switch { SocialService.YouTube => $"https://www.youtube.com/@{h}", SocialService.Twitch => $"https://www.twitch.tv/{h}", SocialService.X => $"https://x.com/{h}", SocialService.Instagram => $"https://www.instagram.com/{h}", SocialService.TikTok => $"https://www.tiktok.com/@{h}", SocialService.Facebook => $"https://www.facebook.com/{h}", SocialService.Discord => $"https://discord.gg/{h}", SocialService.Kick => $"https://kick.com/{h}", SocialService.Threads => $"https://www.threads.net/@{h}", SocialService.Bluesky => $"https://bsky.app/profile/{h}", SocialService.GitHub => $"https://github.com/{h}", SocialService.LinkedIn => $"https://www.linkedin.com/in/{h}", SocialService.Pinterest => $"https://www.pinterest.com/{h}", SocialService.Snapchat => $"https://www.snapchat.com/add/{h}", SocialService.Reddit => $"https://www.reddit.com/user/{h}", SocialService.WhatsApp => $"https://wa.me/{h}", SocialService.Telegram => $"https://t.me/{h}", SocialService.Link or SocialService.Website => h.StartsWith("http") ? h : $"https://{h}", _ => $"https://{h}", }; } /// /// Auto-detects the social service from a URL or fediverse handle. Supports /// full URLs (https://x.com/user), domain-only (x.com/user), and fediverse /// handles (@user@instance.tube). Returns Link/Website for unknown domains /// or bare handles — the caller can then ask which service it is. /// public static (SocialService service, string handle, string url) DetectService(string input) { var trimmed = input.Trim(); // Fediverse handle: @user@domain if (trimmed.StartsWith("@") && trimmed.IndexOf('@', 1) > 0) { var parts = trimmed.TrimStart('@').Split('@', 2); if (parts.Length == 2) { var (user, domain) = (parts[0], parts[1]); return (SocialService.Link, user, $"https://{domain}/@{user}"); } } // Strip protocol for domain parsing var url = trimmed; if (!trimmed.StartsWith("http", StringComparison.OrdinalIgnoreCase)) url = $"https://{trimmed}"; try { var uri = new Uri(url); var domain = uri.Host.ToLowerInvariant(); var path = uri.AbsolutePath.Trim('/'); return domain switch { "youtube.com" or "www.youtube.com" or "youtu.be" => (SocialService.YouTube, path, url), "twitch.tv" or "www.twitch.tv" => (SocialService.Twitch, path, url), "x.com" or "twitter.com" => (SocialService.X, path, url), "instagram.com" or "www.instagram.com" => (SocialService.Instagram, path, url), "tiktok.com" or "www.tiktok.com" => (SocialService.TikTok, path, url), "facebook.com" or "www.facebook.com" or "fb.com" => (SocialService.Facebook, path, url), "discord.gg" or "discord.com" => (SocialService.Discord, path, url), "kick.com" => (SocialService.Kick, path, url), "threads.net" or "www.threads.net" => (SocialService.Threads, path, url), "bsky.app" => (SocialService.Bluesky, path, url), "github.com" => (SocialService.GitHub, path, url), "linkedin.com" or "www.linkedin.com" => (SocialService.LinkedIn, path, url), "pinterest.com" or "www.pinterest.com" => (SocialService.Pinterest, path, url), "snapchat.com" => (SocialService.Snapchat, path, url), "reddit.com" or "www.reddit.com" => (SocialService.Reddit, path, url), "wa.me" or "whatsapp.com" => (SocialService.WhatsApp, path, url), "t.me" => (SocialService.Telegram, path, url), _ => (SocialService.Website, path, url), }; } catch { return (SocialService.Link, trimmed.TrimStart('@'), SocialServiceIcons.CanonicalUrlFor(SocialService.Link, trimmed)); } } }