diff --git a/Unicord.Universal/Controls/EmotePicker.xaml.cs b/Unicord.Universal/Controls/EmotePicker.xaml.cs index a3c21b41..c308b51b 100644 --- a/Unicord.Universal/Controls/EmotePicker.xaml.cs +++ b/Unicord.Universal/Controls/EmotePicker.xaml.cs @@ -35,7 +35,12 @@ public void Load() { try { - Source.Source = EmojiUtilities.GetEmoji(new ChannelViewModel(Channel.Id, true), searchBox.Text); + ChannelViewModel channelVm = null; + if (Channel != null) + { + channelVm = new ChannelViewModel(Channel.Id, true); + } + Source.Source = EmojiUtilities.GetEmoji(channelVm, searchBox.Text); } catch { } } diff --git a/Unicord.Universal/Controls/Flyouts/ChannelContextFlyout.xaml b/Unicord.Universal/Controls/Flyouts/ChannelContextFlyout.xaml index 14dca06d..1825a527 100644 --- a/Unicord.Universal/Controls/Flyouts/ChannelContextFlyout.xaml +++ b/Unicord.Universal/Controls/Flyouts/ChannelContextFlyout.xaml @@ -15,6 +15,13 @@ + + diff --git a/Unicord.Universal/Controls/Flyouts/ChannelContextFlyout.xaml.cs b/Unicord.Universal/Controls/Flyouts/ChannelContextFlyout.xaml.cs index 0d655183..df1b611a 100644 --- a/Unicord.Universal/Controls/Flyouts/ChannelContextFlyout.xaml.cs +++ b/Unicord.Universal/Controls/Flyouts/ChannelContextFlyout.xaml.cs @@ -1,4 +1,8 @@ -using Windows.UI.Xaml.Controls; +using Unicord.Universal.Models.Channels; +using Unicord.Universal.Pages.Overlay; +using Unicord.Universal.Services; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; namespace Unicord.Universal.Controls.Flyouts { @@ -8,5 +12,14 @@ public ChannelContextFlyout() { InitializeComponent(); } + + private async void ProfileItem_Click(object sender, RoutedEventArgs e) + { + if (sender is FrameworkElement element && element.DataContext is ChannelViewModel channel && channel.Recipient != null) + { + await OverlayService.GetForCurrentView() + .ShowOverlayAsync(channel.Recipient); + } + } } } diff --git a/Unicord.Universal/Controls/Flyouts/DirectMessageContextFlyout.xaml b/Unicord.Universal/Controls/Flyouts/DirectMessageContextFlyout.xaml index 01d08b74..a57fd0b8 100644 --- a/Unicord.Universal/Controls/Flyouts/DirectMessageContextFlyout.xaml +++ b/Unicord.Universal/Controls/Flyouts/DirectMessageContextFlyout.xaml @@ -15,6 +15,11 @@ + diff --git a/Unicord.Universal/Controls/Flyouts/DirectMessageContextFlyout.xaml.cs b/Unicord.Universal/Controls/Flyouts/DirectMessageContextFlyout.xaml.cs index 85ba72ca..8afad572 100644 --- a/Unicord.Universal/Controls/Flyouts/DirectMessageContextFlyout.xaml.cs +++ b/Unicord.Universal/Controls/Flyouts/DirectMessageContextFlyout.xaml.cs @@ -1,4 +1,8 @@ -using Windows.UI.Xaml.Controls; +using Unicord.Universal.Models.Channels; +using Unicord.Universal.Pages.Overlay; +using Unicord.Universal.Services; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; namespace Unicord.Universal.Controls.Flyouts { @@ -8,5 +12,14 @@ public DirectMessageContextFlyout() { InitializeComponent(); } + + private async void ProfileItem_Click(object sender, RoutedEventArgs e) + { + if (sender is FrameworkElement element && element.DataContext is ChannelViewModel channel && channel.Recipient != null) + { + await OverlayService.GetForCurrentView() + .ShowOverlayAsync(channel.Recipient); + } + } } } diff --git a/Unicord.Universal/Controls/Flyouts/UserFlyout.xaml b/Unicord.Universal/Controls/Flyouts/UserFlyout.xaml index 710f88a1..1a0c2589 100644 --- a/Unicord.Universal/Controls/Flyouts/UserFlyout.xaml +++ b/Unicord.Universal/Controls/Flyouts/UserFlyout.xaml @@ -11,60 +11,294 @@ xmlns:user="using:Unicord.Universal.Controls.Users" mc:Ignorable="d"> - - - - - - - - - - - - - - - - - - - - @# - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Unicord.Universal/Dialogs/ProfileOverlay.xaml.cs b/Unicord.Universal/Dialogs/ProfileOverlay.xaml.cs index 47ff7d93..c21e9d1b 100644 --- a/Unicord.Universal/Dialogs/ProfileOverlay.xaml.cs +++ b/Unicord.Universal/Dialogs/ProfileOverlay.xaml.cs @@ -1,14 +1,22 @@ -using Unicord.Universal.Models.User; +using System.Linq; +using DSharpPlus.Entities; +using Unicord.Universal.Extensions; +using Unicord.Universal.Models.User; using Unicord.Universal.Services; +using Windows.ApplicationModel.DataTransfer; using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; +using Lib = Microsoft.UI.Xaml.Controls; namespace Unicord.Universal.Dialogs { public sealed partial class ProfileOverlay : UserControl { + private const double CompactThresholdWidth = 640; + private string _pendingNavigationTag; + public UserViewModel User { get => (UserViewModel)GetValue(UserProperty); @@ -22,11 +30,107 @@ private static void OnUserChanged(DependencyObject d, DependencyPropertyChangedE { var overlay = (ProfileOverlay)d; overlay.Bindings.Update(); + + overlay.ApplyPendingNavigationOrDefault(); } public ProfileOverlay() { InitializeComponent(); + + SizeChanged += (_, __) => UpdatePaneMode(); + + Loaded += (s, e) => + { + ApplyPendingNavigationOrDefault(); + + UpdatePaneMode(); + }; + } + + public void NavigateToSection(string tag) + { + _pendingNavigationTag = tag; + ApplyPendingNavigationOrDefault(); + } + + private void ApplyPendingNavigationOrDefault() + { + if (NavView == null) + return; + + var tag = _pendingNavigationTag; + + Lib.NavigationViewItem targetItem = tag switch + { + "mutualfriends" => MutualFriendsItem, + "mutual" => MutualServersItem, + "activities" => ActivitiesItem, + _ => null + }; + + // If we have a pending navigation target, apply it + if (targetItem != null) + { + _pendingNavigationTag = null; + NavView.SelectedItem = targetItem; + } + // Only apply default Overview if nothing is selected yet + else if (NavView.SelectedItem == null && _pendingNavigationTag == null) + { + NavView.SelectedItem = OverviewItem; + } + } + + private void UpdatePaneMode() + { + if (NavView == null) + return; + + var isCompact = ActualWidth > 0 && ActualWidth < CompactThresholdWidth; + + var desiredMode = isCompact + ? Lib.NavigationViewPaneDisplayMode.LeftCompact + : Lib.NavigationViewPaneDisplayMode.Left; + + if (NavView.PaneDisplayMode != desiredMode) + NavView.PaneDisplayMode = desiredMode; + + var desiredIsPaneOpen = !isCompact; + if (NavView.IsPaneOpen != desiredIsPaneOpen) + NavView.IsPaneOpen = desiredIsPaneOpen; + + NavView.InvalidateMeasure(); + NavView.UpdateLayout(); + } + + private void NavView_SelectionChanged(Lib.NavigationView sender, Lib.NavigationViewSelectionChangedEventArgs args) + { + if (args.SelectedItemContainer?.Tag is not string tag) + return; + + // Hide all content + OverviewContent.Visibility = Visibility.Collapsed; + ActivitiesContent.Visibility = Visibility.Collapsed; + MutualFriendsContent.Visibility = Visibility.Collapsed; + MutualServersContent.Visibility = Visibility.Collapsed; + + // Show selected content + switch (tag) + { + case "overview": + OverviewContent.Visibility = Visibility.Visible; + break; + case "activities": + ActivitiesContent.Visibility = Visibility.Visible; + break; + case "mutualfriends": + MutualFriendsContent.Visibility = Visibility.Visible; + break; + case "mutual": + MutualServersContent.Visibility = Visibility.Visible; + break; + } } private void DropShadowPanel_PreviewKeyUp(object sender, KeyRoutedEventArgs e) @@ -37,5 +141,61 @@ private void DropShadowPanel_PreviewKeyUp(object sender, KeyRoutedEventArgs e) .CloseOverlay(); } } + + private async void MutualServersList_ItemClick(object sender, ItemClickEventArgs e) + { + if (e.ClickedItem is Unicord.Universal.Models.Guild.GuildViewModel guild) + { + OverlayService.GetForCurrentView().CloseOverlay(); + + if (App.LocalSettings.Read(Constants.ENABLE_GUILD_BROWSING, Constants.ENABLE_GUILD_BROWSING_DEFAULT)) + { + // Navigate to guild browsing view + var discordPage = Window.Current.Content.FindChild(); + if (discordPage != null) + { + discordPage.LeftSidebarFrame.Navigate(typeof(Pages.Subpages.GuildChannelListPage), guild.Guild); + } + } + else + { + // Navigate to previously selected channel or first accessible text channel + var channelId = App.RoamingSettings.Read($"GuildPreviousChannels::{guild.Guild.Id}", 0UL); + if (!guild.Guild.Channels.TryGetValue(channelId, out var channel) || (!channel.IsAccessible() || !channel.IsText())) + { + channel = guild.Guild.Channels.Values + .Where(c => c.IsAccessible()) + .Where(c => c.IsText()) + .OrderBy(c => c.Position) + .FirstOrDefault(); + } + + if (channel != null) + { + await DiscordNavigationService.GetForCurrentView() + .NavigateAsync(channel); + } + } + } + } + + private async void MutualFriendsList_ItemClick(object sender, ItemClickEventArgs e) + { + if (e.ClickedItem is Unicord.Universal.Models.User.UserViewModel friendUser) + { + await OverlayService.GetForCurrentView() + .ReplaceOverlayWithAnimationAsync(friendUser); + } + } + + private void CopyUserId_Click(object sender, RoutedEventArgs e) + { + if (User == null) + return; + + var dataPackage = new DataPackage(); + dataPackage.SetText(User.Id.ToString()); + Clipboard.SetContent(dataPackage); + } } } diff --git a/Unicord.Universal/MainPage.xaml.cs b/Unicord.Universal/MainPage.xaml.cs index 00bb400e..5a2631ce 100644 --- a/Unicord.Universal/MainPage.xaml.cs +++ b/Unicord.Universal/MainPage.xaml.cs @@ -348,6 +348,24 @@ public void HideCustomOverlay() HideOverlayStoryboard.Begin(); } + public Task HideCustomOverlayAsync() + { + if (CustomOverlayGrid.Visibility == Visibility.Collapsed) + return Task.CompletedTask; + + var tcs = new TaskCompletionSource(); + + void Handler(object sender, object e) + { + HideOverlayStoryboard.Completed -= Handler; + tcs.TrySetResult(null); + } + + HideOverlayStoryboard.Completed += Handler; + HideOverlayStoryboard.Begin(); + return tcs.Task; + } + private void OverlayBackdrop_Tapped(object sender, TappedRoutedEventArgs e) { OverlayService.GetForCurrentView() diff --git a/Unicord.Universal/Models/User/UserViewModel.cs b/Unicord.Universal/Models/User/UserViewModel.cs index 68717cbd..a10eff9e 100644 --- a/Unicord.Universal/Models/User/UserViewModel.cs +++ b/Unicord.Universal/Models/User/UserViewModel.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Windows.Input; using DSharpPlus.Entities; +using DSharpPlus.Enums; using DSharpPlus.EventArgs; using CommunityToolkit.Mvvm.Messaging; using Unicord.Universal.Commands; @@ -150,6 +151,16 @@ public List MutualGuilds } } + // Mutual friends placeholder since its currently not populated in Unicord. + public List MutualFriends + => null; + + public int MutualFriendsCount + => MutualFriends?.Count ?? 0; + + public bool HasMutualInfo + => !IsCurrent && (MutualGuilds?.Count ?? 0) > 0; + public List Roles => Member != null ? _rolesCache ??= Member.Roles.Select(r => new RoleViewModel(r, this)).ToList() : null; diff --git a/Unicord.Universal/Pages/Overlay/UserInfoOverlayPage.xaml.cs b/Unicord.Universal/Pages/Overlay/UserInfoOverlayPage.xaml.cs index ba8d42e2..c4bd8303 100644 --- a/Unicord.Universal/Pages/Overlay/UserInfoOverlayPage.xaml.cs +++ b/Unicord.Universal/Pages/Overlay/UserInfoOverlayPage.xaml.cs @@ -1,4 +1,5 @@ -using Unicord.Universal.Models.User; +using System; +using Unicord.Universal.Models.User; using Unicord.Universal.Services; using Windows.Foundation; using Windows.UI.Xaml.Controls; @@ -25,7 +26,17 @@ protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); - userInfoOverlay.User = (UserViewModel)e.Parameter; + if (e.Parameter is UserViewModel user) + { + userInfoOverlay.User = user; + return; + } + + if (e.Parameter is System.ValueTuple tuple) + { + userInfoOverlay.User = tuple.Item1; + userInfoOverlay.NavigateToSection(tuple.Item2); + } } } } diff --git a/Unicord.Universal/Resources/en-GB/Dialogs.resw b/Unicord.Universal/Resources/en-GB/Dialogs.resw index 4e10ba51..37e989ec 100644 --- a/Unicord.Universal/Resources/en-GB/Dialogs.resw +++ b/Unicord.Universal/Resources/en-GB/Dialogs.resw @@ -210,15 +210,21 @@ Pin this message? - + Connections Joined Server + + Mutual Servers + Mutual Servers + + Mutual Servers + Roles diff --git a/Unicord.Universal/Resources/en-US/Dialogs.resw b/Unicord.Universal/Resources/en-US/Dialogs.resw index 31767602..f88396b3 100644 --- a/Unicord.Universal/Resources/en-US/Dialogs.resw +++ b/Unicord.Universal/Resources/en-US/Dialogs.resw @@ -210,15 +210,21 @@ Pin this message? - + Connections Joined Server + + Mutual Servers + Mutual Servers + + Mutual Servers + Roles diff --git a/Unicord.Universal/Resources/fr/Dialogs.resw b/Unicord.Universal/Resources/fr/Dialogs.resw index b1757b5e..a1718d94 100644 --- a/Unicord.Universal/Resources/fr/Dialogs.resw +++ b/Unicord.Universal/Resources/fr/Dialogs.resw @@ -201,15 +201,21 @@ Épingler ce message? - + Profils connectés Serveurs rejoints + + Serveurs en commun + Serveurs en commun + + Serveurs en commun + Rôles diff --git a/Unicord.Universal/Resources/ja-JP/Dialogs.resw b/Unicord.Universal/Resources/ja-JP/Dialogs.resw index 42243df5..c88914e8 100644 --- a/Unicord.Universal/Resources/ja-JP/Dialogs.resw +++ b/Unicord.Universal/Resources/ja-JP/Dialogs.resw @@ -210,15 +210,21 @@ このメッセージをピン止めしますか? - + 連携 サーバー参加日 + + 共通のサーバー + 共通のサーバー + + 共通のサーバー + ロール diff --git a/Unicord.Universal/Resources/ru-RU/Dialogs.resw b/Unicord.Universal/Resources/ru-RU/Dialogs.resw index a5d27c98..1162048a 100644 --- a/Unicord.Universal/Resources/ru-RU/Dialogs.resw +++ b/Unicord.Universal/Resources/ru-RU/Dialogs.resw @@ -210,15 +210,21 @@ Закрепить это сообщение? - + Связи Присоединился к серверу + + Общие серверы + Общие серверы + + Общие серверы + Роли diff --git a/Unicord.Universal/Services/OverlayService.cs b/Unicord.Universal/Services/OverlayService.cs index 55c9645c..6d7efc24 100644 --- a/Unicord.Universal/Services/OverlayService.cs +++ b/Unicord.Universal/Services/OverlayService.cs @@ -69,6 +69,30 @@ await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => } } + public async Task ReplaceOverlayWithAnimationAsync(object model = null) where T : Page, IOverlay, new() + { + if (App.LocalSettings.Read("WindowedOverlays", false)) + { + // In a separate window overlay, just navigate within the window to get a page transition. + if (Window.Current.Content is Frame frame) + { + frame.Navigate(typeof(T), model, new EntranceNavigationTransitionInfo()); + } + + return; + } + + if (_overlayVisible) + { + _systemNavigationManager.BackRequested -= OnBackRequested; + await _mainPage.HideCustomOverlayAsync(); + _overlayVisible = false; + } + + // Re-open (plays the overlay open storyboard) + await ShowOverlayAsync(model); + } + internal void CloseOverlay() { if (App.LocalSettings.Read("WindowedOverlays", false)) diff --git a/Unicord.Universal/Themes/Controls/Messages.xaml b/Unicord.Universal/Themes/Controls/Messages.xaml index f979ad39..a5dc5761 100644 --- a/Unicord.Universal/Themes/Controls/Messages.xaml +++ b/Unicord.Universal/Themes/Controls/Messages.xaml @@ -881,7 +881,8 @@ Width="16" Height="16" Margin="0,0,4,0" - Grid.Column="1"> + Grid.Column="1" + Tapped="Avatar_Tapped"> @@ -894,7 +895,12 @@ - + @@ -943,7 +950,11 @@ - + (user, element); + } + } + + private void Avatar_Tapped(object sender, TappedRoutedEventArgs e) + { + if (sender is FrameworkElement element && element.DataContext is MessageViewModel message && message.Author != null) + { + Unicord.Universal.Utilities.AdaptiveFlyoutUtilities.ShowAdaptiveFlyout(message.Author, element, FlyoutPlacementMode.Right); + } + } } } diff --git a/Unicord.Universal/Themes/Styles/Fluent.xaml b/Unicord.Universal/Themes/Styles/Fluent.xaml index 60e947c5..cfbed71e 100644 --- a/Unicord.Universal/Themes/Styles/Fluent.xaml +++ b/Unicord.Universal/Themes/Styles/Fluent.xaml @@ -28,7 +28,10 @@ 0,0,0,0 0 + 0 + 0 0 + 0 diff --git a/Unicord.Universal/Themes/Styles/Performance.xaml b/Unicord.Universal/Themes/Styles/Performance.xaml index 3b856e51..a0d18412 100644 --- a/Unicord.Universal/Themes/Styles/Performance.xaml +++ b/Unicord.Universal/Themes/Styles/Performance.xaml @@ -34,6 +34,9 @@ 0 0 + 0 + 0 + 0 diff --git a/Unicord.Universal/Themes/Styles/SunValley.xaml b/Unicord.Universal/Themes/Styles/SunValley.xaml index 6ad3846d..08d646ee 100644 --- a/Unicord.Universal/Themes/Styles/SunValley.xaml +++ b/Unicord.Universal/Themes/Styles/SunValley.xaml @@ -21,6 +21,9 @@ 8 8,8,0,0 + 8 + 8,8,0,0 + 4