2022-04-15 15:38:47 +01:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System ;
2022-05-25 12:29:04 +01:00
using System.Linq ;
2022-04-15 15:38:47 +01:00
using System.Collections.Generic ;
using osu.Framework.Allocation ;
2022-05-26 22:23:03 -07:00
using osu.Framework.Extensions.LocalisationExtensions ;
2022-04-15 15:38:47 +01:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Shapes ;
2025-02-24 09:43:46 +01:00
using osu.Framework.Graphics.Sprites ;
2024-10-22 01:33:53 +03:00
using osu.Framework.Graphics.UserInterface ;
2022-05-26 22:23:03 -07:00
using osu.Framework.Localisation ;
2024-10-22 11:48:24 +02:00
using osu.Framework.Testing ;
2022-04-15 15:38:47 +01:00
using osu.Game.Graphics ;
using osu.Game.Graphics.Containers ;
using osu.Game.Graphics.Sprites ;
2024-10-22 01:33:53 +03:00
using osu.Game.Graphics.UserInterface ;
2022-04-15 15:38:47 +01:00
using osu.Game.Online.Chat ;
2022-05-19 19:26:14 +09:00
using osu.Game.Overlays.Chat.Listing ;
2022-05-26 22:23:03 -07:00
using osu.Game.Resources.Localisation.Web ;
2024-10-22 11:27:45 +02:00
using osuTK ;
2022-04-15 15:38:47 +01:00
namespace osu.Game.Overlays.Chat.ChannelList
{
public partial class ChannelList : Container
{
2022-05-15 19:38:37 +01:00
public Action < Channel > ? OnRequestSelect ;
2022-04-15 15:38:47 +01:00
public Action < Channel > ? OnRequestLeave ;
2022-05-29 18:58:27 +02:00
public IEnumerable < Channel > Channels = > groupFlow . Children
. OfType < ChannelGroup > ( )
2022-05-29 18:56:37 +02:00
. SelectMany ( channelGroup = > channelGroup . ItemFlow )
. Select ( item = > item . Channel ) ;
2022-05-25 12:29:04 +01:00
2022-05-19 11:45:39 +01:00
public readonly ChannelListing . ChannelListingChannel ChannelListingChannel = new ChannelListing . ChannelListingChannel ( ) ;
2022-04-15 15:38:47 +01:00
2022-05-19 11:45:39 +01:00
private readonly Dictionary < Channel , ChannelListItem > channelMap = new Dictionary < Channel , ChannelListItem > ( ) ;
2022-05-15 19:38:37 +01:00
2024-11-24 23:43:31 +09:00
public ChannelGroup AnnounceChannelGroup { get ; private set ; } = null ! ;
public ChannelGroup PublicChannelGroup { get ; private set ; } = null ! ;
2025-02-24 09:39:27 +01:00
public ChannelGroup TeamChannelGroup { get ; private set ; } = null ! ;
2024-11-24 23:43:31 +09:00
public ChannelGroup PrivateChannelGroup { get ; private set ; } = null ! ;
2022-05-25 12:37:28 +01:00
private OsuScrollContainer scroll = null ! ;
2024-10-22 11:36:00 +02:00
private SearchContainer groupFlow = null ! ;
2024-11-24 23:43:31 +09:00
2022-05-15 19:38:37 +01:00
private ChannelListItem selector = null ! ;
2024-10-22 01:33:53 +03:00
private TextBox searchTextBox = null ! ;
2022-04-15 15:38:47 +01:00
[BackgroundDependencyLoader]
private void load ( OverlayColourProvider colourProvider )
{
Children = new Drawable [ ]
{
new Box
{
RelativeSizeAxes = Axes . Both ,
Colour = colourProvider . Background6 ,
} ,
2022-05-25 12:37:28 +01:00
scroll = new OsuScrollContainer
2022-04-15 15:38:47 +01:00
{
RelativeSizeAxes = Axes . Both ,
2022-04-16 19:19:18 +01:00
ScrollbarAnchor = Anchor . TopRight ,
2022-04-15 15:38:47 +01:00
ScrollDistance = 35f ,
2024-10-22 11:36:00 +02:00
Child = groupFlow = new SearchContainer
2022-04-15 15:38:47 +01:00
{
Direction = FillDirection . Vertical ,
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Children = new Drawable [ ]
{
2024-10-22 11:27:45 +02:00
new Container
2024-10-22 01:33:53 +03:00
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
2024-10-22 11:27:45 +02:00
Padding = new MarginPadding { Horizontal = 10 , Top = 8 } ,
2024-10-22 11:48:24 +02:00
Child = searchTextBox = new ChannelSearchTextBox
2024-10-22 01:33:53 +03:00
{
2024-10-22 11:27:45 +02:00
RelativeSizeAxes = Axes . X ,
2024-10-22 01:33:53 +03:00
}
} ,
2025-02-24 09:43:46 +01:00
// cross-reference for icons: https://github.com/ppy/osu-web/blob/3c9e99eaf4bd9e73d2712f60d67f5bc95f9dfe2b/resources/js/chat/conversation-list.tsx#L13-L19
AnnounceChannelGroup = new ChannelGroup ( ChatStrings . ChannelsListTitleANNOUNCE . ToUpper ( ) , FontAwesome . Solid . Bullhorn , false ) ,
PublicChannelGroup = new ChannelGroup ( ChatStrings . ChannelsListTitlePUBLIC . ToUpper ( ) , FontAwesome . Solid . Comments , false ) ,
2024-10-22 11:27:45 +02:00
selector = new ChannelListItem ( ChannelListingChannel ) ,
2025-02-24 09:43:46 +01:00
TeamChannelGroup = new ChannelGroup ( "TEAM" , FontAwesome . Solid . Users , false ) , // TODO: replace with osu-web localisable string once available
PrivateChannelGroup = new ChannelGroup ( ChatStrings . ChannelsListTitlePM . ToUpper ( ) , FontAwesome . Solid . Envelope , true ) ,
2022-04-15 15:38:47 +01:00
} ,
} ,
} ,
} ;
2022-05-15 19:38:37 +01:00
2024-10-22 11:36:00 +02:00
searchTextBox . Current . BindValueChanged ( _ = > groupFlow . SearchTerm = searchTextBox . Current . Value , true ) ;
2024-10-22 11:48:24 +02:00
searchTextBox . OnCommit + = ( _ , _ ) = >
{
if ( string . IsNullOrEmpty ( searchTextBox . Current . Value ) )
return ;
var firstMatchingItem = this . ChildrenOfType < ChannelListItem > ( ) . FirstOrDefault ( item = > item . MatchingFilter ) ;
if ( firstMatchingItem = = null )
return ;
OnRequestSelect ? . Invoke ( firstMatchingItem . Channel ) ;
} ;
2024-10-22 01:33:53 +03:00
2022-05-15 19:38:37 +01:00
selector . OnRequestSelect + = chan = > OnRequestSelect ? . Invoke ( chan ) ;
2025-02-24 15:06:02 +01:00
updateVisibility ( ) ;
2022-04-15 15:38:47 +01:00
}
public void AddChannel ( Channel channel )
{
if ( channelMap . ContainsKey ( channel ) )
return ;
2025-02-24 15:10:20 +01:00
ChannelListItem item = new ChannelListItem ( channel )
{
CanLeave = channel . Type ! = ChannelType . Team
} ;
2022-04-15 22:05:20 +01:00
item . OnRequestSelect + = chan = > OnRequestSelect ? . Invoke ( chan ) ;
2025-02-24 15:10:20 +01:00
if ( item . CanLeave )
item . OnRequestLeave + = chan = > OnRequestLeave ? . Invoke ( chan ) ;
2022-04-15 15:38:47 +01:00
2024-11-22 19:51:55 +09:00
ChannelGroup group = getGroupFromChannel ( channel ) ;
2022-04-15 15:38:47 +01:00
channelMap . Add ( channel , item ) ;
2024-11-22 19:51:55 +09:00
group . AddChannel ( item ) ;
2022-05-27 16:06:23 +01:00
updateVisibility ( ) ;
2022-04-15 15:38:47 +01:00
}
public void RemoveChannel ( Channel channel )
{
2024-11-22 19:11:14 +09:00
if ( ! channelMap . TryGetValue ( channel , out var item ) )
2022-04-15 15:38:47 +01:00
return ;
2024-11-22 19:51:55 +09:00
ChannelGroup group = getGroupFromChannel ( channel ) ;
2022-04-15 15:38:47 +01:00
channelMap . Remove ( channel ) ;
2024-11-22 19:51:55 +09:00
group . RemoveChannel ( item ) ;
2022-05-27 16:06:23 +01:00
updateVisibility ( ) ;
2022-04-15 15:38:47 +01:00
}
public ChannelListItem GetItem ( Channel channel )
{
2024-11-22 19:11:14 +09:00
if ( ! channelMap . TryGetValue ( channel , out var item ) )
2022-04-15 15:38:47 +01:00
throw new ArgumentOutOfRangeException ( ) ;
2024-11-22 19:11:14 +09:00
return item ;
2022-04-15 15:38:47 +01:00
}
2022-05-25 15:15:46 +01:00
public void ScrollChannelIntoView ( Channel channel ) = > scroll . ScrollIntoView ( GetItem ( channel ) ) ;
2022-05-25 12:37:28 +01:00
2024-11-22 19:51:55 +09:00
private ChannelGroup getGroupFromChannel ( Channel channel )
2022-04-15 15:38:47 +01:00
{
switch ( channel . Type )
{
case ChannelType . Public :
2024-11-24 23:43:31 +09:00
return PublicChannelGroup ;
2022-04-15 15:38:47 +01:00
case ChannelType . PM :
2024-11-24 23:43:31 +09:00
return PrivateChannelGroup ;
2022-04-15 15:38:47 +01:00
2022-05-27 16:06:23 +01:00
case ChannelType . Announce :
2024-11-24 23:43:31 +09:00
return AnnounceChannelGroup ;
2022-05-27 16:06:23 +01:00
2025-02-24 09:39:27 +01:00
case ChannelType . Team :
return TeamChannelGroup ;
2022-04-15 15:38:47 +01:00
default :
2024-11-24 23:43:31 +09:00
return PublicChannelGroup ;
2022-05-25 15:15:46 +01:00
}
}
2022-05-27 16:06:23 +01:00
private void updateVisibility ( )
{
2025-02-24 15:06:02 +01:00
AnnounceChannelGroup . Alpha = AnnounceChannelGroup . ItemFlow . Any ( ) ? 1 : 0 ;
TeamChannelGroup . Alpha = TeamChannelGroup . ItemFlow . Any ( ) ? 1 : 0 ;
2022-05-27 16:06:23 +01:00
}
2024-11-24 23:43:31 +09:00
public partial class ChannelGroup : FillFlowContainer
2022-05-25 15:15:46 +01:00
{
2024-11-27 15:46:55 +09:00
private readonly bool sortByRecent ;
2024-11-22 19:51:55 +09:00
public readonly ChannelListItemFlow ItemFlow ;
2022-05-25 12:29:04 +01:00
2025-02-24 09:43:46 +01:00
public ChannelGroup ( LocalisableString label , IconUsage icon , bool sortByRecent )
2022-04-15 15:38:47 +01:00
{
2024-11-27 15:46:55 +09:00
this . sortByRecent = sortByRecent ;
2022-04-15 15:38:47 +01:00
Direction = FillDirection . Vertical ;
RelativeSizeAxes = Axes . X ;
AutoSizeAxes = Axes . Y ;
2022-05-27 19:46:53 +01:00
Padding = new MarginPadding { Top = 8 } ;
Children = new Drawable [ ]
{
2025-02-24 09:43:46 +01:00
new FillFlowContainer
2022-05-27 19:46:53 +01:00
{
2025-02-24 09:43:46 +01:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Direction = FillDirection . Horizontal ,
Spacing = new Vector2 ( 5 ) ,
Children = new Drawable [ ]
{
new OsuSpriteText
{
Text = label ,
Margin = new MarginPadding { Left = 18 , Bottom = 5 } ,
Font = OsuFont . Torus . With ( size : 12 , weight : FontWeight . SemiBold ) ,
} ,
new SpriteIcon
{
Icon = icon ,
Size = new Vector2 ( 12 ) ,
} ,
}
2022-05-27 19:46:53 +01:00
} ,
2024-11-22 19:51:55 +09:00
ItemFlow = new ChannelListItemFlow ( sortByRecent )
2022-05-27 19:46:53 +01:00
{
Direction = FillDirection . Vertical ,
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
} ,
} ;
2022-04-15 15:38:47 +01:00
}
2024-11-22 19:51:55 +09:00
public partial class ChannelListItemFlow : FillFlowContainer < ChannelListItem >
{
private readonly bool sortByRecent ;
public ChannelListItemFlow ( bool sortByRecent )
{
this . sortByRecent = sortByRecent ;
}
public void Reflow ( ) = > InvalidateLayout ( ) ;
public override IEnumerable < Drawable > FlowingChildren = > sortByRecent
2024-11-24 23:43:31 +09:00
? base . FlowingChildren . OfType < ChannelListItem > ( ) . OrderByDescending ( i = > i . Channel . LastMessageId ? ? long . MinValue )
2024-11-22 19:51:55 +09:00
: base . FlowingChildren . OfType < ChannelListItem > ( ) . OrderBy ( i = > i . Channel . Name ) ;
}
public void AddChannel ( ChannelListItem item )
{
ItemFlow . Add ( item ) ;
2024-11-27 15:46:55 +09:00
if ( sortByRecent )
{
item . Channel . NewMessagesArrived + = newMessagesArrived ;
item . Channel . PendingMessageResolved + = pendingMessageResolved ;
}
2024-11-22 19:51:55 +09:00
ItemFlow . Reflow ( ) ;
}
public void RemoveChannel ( ChannelListItem item )
{
2024-11-27 15:46:55 +09:00
if ( sortByRecent )
{
item . Channel . NewMessagesArrived - = newMessagesArrived ;
item . Channel . PendingMessageResolved - = pendingMessageResolved ;
}
2024-11-22 19:51:55 +09:00
ItemFlow . Remove ( item , true ) ;
}
private void pendingMessageResolved ( LocalEchoMessage _ , Message __ ) = > ItemFlow . Reflow ( ) ;
private void newMessagesArrived ( IEnumerable < Message > _ ) = > ItemFlow . Reflow ( ) ;
2024-11-27 15:46:55 +09:00
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
foreach ( var item in ItemFlow )
{
item . Channel . NewMessagesArrived - = newMessagesArrived ;
item . Channel . PendingMessageResolved - = pendingMessageResolved ;
}
}
2022-04-15 15:38:47 +01:00
}
2024-10-22 11:48:24 +02:00
private partial class ChannelSearchTextBox : BasicSearchTextBox
{
protected override bool AllowCommit = > true ;
public ChannelSearchTextBox ( )
{
const float scale_factor = 0.8f ;
Scale = new Vector2 ( scale_factor ) ;
Width = 1 / scale_factor ;
}
}
2022-04-15 15:38:47 +01:00
}
}