Skip to content

Commit

Permalink
Support facets in posts
Browse files Browse the repository at this point in the history
  • Loading branch information
WamWooWam committed Dec 3, 2024
1 parent c4e4de1 commit 8b86507
Show file tree
Hide file tree
Showing 16 changed files with 484 additions and 84 deletions.
1 change: 1 addition & 0 deletions UniSky/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<ResourceDictionary Source="/Templates/NavigationViewStyles.xaml"/>
<ResourceDictionary Source="/Templates/ButtonStyles.xaml"/>
<ResourceDictionary Source="/Templates/TextBoxStyles.xaml"/>
<ResourceDictionary Source="/Templates/RichTextBlockStyles.xaml"/>
<ResourceDictionary Source="/Templates/MediaTransportControlsStyles.xaml"/>

<!-- Templates -->
Expand Down
26 changes: 25 additions & 1 deletion UniSky/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.Extensions.Logging;
using UniSky.Extensions;
using UniSky.Helpers.Localisation;
using UniSky.Pages;
using UniSky.Services;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
Expand Down Expand Up @@ -64,7 +65,15 @@ private void ConfigureServices()

Ioc.Default.ConfigureServices(collection.BuildServiceProvider());
Configurator.Formatters.Register("en", (locale) => new ShortTimespanFormatter("en"));
}
}

protected override void OnActivated(IActivatedEventArgs args)
{
if (args is ProtocolActivatedEventArgs e)
{
this.OnProtocolActivated(e);
}
}

/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
Expand Down Expand Up @@ -108,6 +117,21 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
}
}

private void OnProtocolActivated(ProtocolActivatedEventArgs e)
{
Hairline.Initialize();
if (Window.Current.Content is not Frame rootFrame)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
rootFrame.Navigate(typeof(RootPage));
Window.Current.Content = rootFrame;
}

// Ensure the current window is active
Window.Current.Activate();
}

/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
Expand Down
156 changes: 156 additions & 0 deletions UniSky/Controls/RichTextBlock/RichTextBlock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Toolkit.Uwp.UI.Extensions;
using UniSky.Pages;
using UniSky.Services;
using UniSky.ViewModels.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;

// The Templated Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234235

namespace UniSky.Controls
{
[TemplatePart(Name = "PART_TextBlock", Type = typeof(TextBlock))]
public sealed class RichTextBlock : Control
{
private static readonly DependencyProperty HyperlinkUrlProperty =
DependencyProperty.RegisterAttached("HyperlinkUrl", typeof(Uri), typeof(RichTextBlock), new PropertyMetadata(null));

public TextWrapping TextWrapping
{
get { return (TextWrapping)GetValue(TextWrappingProperty); }
set { SetValue(TextWrappingProperty, value); }
}

public static readonly DependencyProperty TextWrappingProperty =
DependencyProperty.Register("TextWrapping", typeof(TextWrapping), typeof(RichTextBlock), new PropertyMetadata(TextWrapping.Wrap));

public bool IsTextSelectionEnabled
{
get { return (bool)GetValue(IsTextSelectionEnabledProperty); }
set { SetValue(IsTextSelectionEnabledProperty, value); }
}

public static readonly DependencyProperty IsTextSelectionEnabledProperty =
DependencyProperty.Register("IsTextSelectionEnabled", typeof(bool), typeof(RichTextBlock), new PropertyMetadata(true));

public IList<FacetInline> Inlines
{
get { return (IList<FacetInline>)GetValue(InlinesProperty); }
set { SetValue(InlinesProperty, value); }
}

public static readonly DependencyProperty InlinesProperty =
DependencyProperty.Register("Inlines", typeof(IList<FacetInline>), typeof(RichTextBlock), new PropertyMetadata(null, OnInlinesChanged));

private static void OnInlinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not RichTextBlock text)
return;

text.UpdateInlines();
}

public RichTextBlock()
{
this.DefaultStyleKey = typeof(RichTextBlock);
}

protected override void OnApplyTemplate()
{
base.OnApplyTemplate();

UpdateInlines();
}

private void UpdateInlines()
{
this.ApplyTemplate();

if (this.FindDescendantByName("PART_TextBlock") is not TextBlock text)
return;

text.Inlines.Clear();

// TODO: this could be cleaner
foreach (var inline in Inlines)
{
var mention = inline.Properties.OfType<MentionProperty>()
.FirstOrDefault();
if (mention != null)
{
var hyperlink = new Hyperlink()
{
Inlines = { new Run() { Text = inline.Text } }
};

hyperlink.Click += Hyperlink_Click;
hyperlink.SetValue(HyperlinkUrlProperty, new Uri("unisky:///profile/" + mention.Did.ToString()));

text.Inlines.Add(hyperlink);
continue;
}

var tag = inline.Properties.OfType<TagProperty>()
.FirstOrDefault();
if (tag != null)
{
var hyperlink = new Hyperlink()
{
Inlines = { new Run() { Text = inline.Text } }
};

hyperlink.Click += Hyperlink_Click;
hyperlink.SetValue(HyperlinkUrlProperty, new Uri("unisky:///tag/" + tag.Tag));

text.Inlines.Add(hyperlink);
continue;
}

var link = inline.Properties.OfType<LinkProperty>()
.FirstOrDefault();
if (link != null && Uri.TryCreate(link.Url, UriKind.Absolute, out var uri))
{
var hyperlink = new Hyperlink()
{
NavigateUri = uri,
Inlines = { new Run() { Text = inline.Text } }
};

text.Inlines.Add(hyperlink);
continue;
}

text.Inlines.Add(new Run() { Text = inline.Text });
}
}

// TODO: move this somewhere better
private void Hyperlink_Click(Hyperlink sender, HyperlinkClickEventArgs args)
{
if (sender.GetValue(HyperlinkUrlProperty) is not Uri { Scheme: "unisky" } uri)
return;

var service = Ioc.Default.GetRequiredService<INavigationServiceLocator>()
.GetNavigationService("Home");

var path = uri.PathAndQuery.Split('/', StringSplitOptions.RemoveEmptyEntries);
switch (path.FirstOrDefault()?.ToLowerInvariant())
{
case "profile":
service.Navigate<ProfilePage>(uri);
break;
case "tag":
break;
}
}
}
}
34 changes: 18 additions & 16 deletions UniSky/DataTemplates/FeedTemplates.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
xmlns:not1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract, 7)"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:controls="using:UniSky.Controls"
xmlns:datatemplates="using:UniSky.DataTemplates"
xmlns:converters="using:UniSky.Converters"
xmlns:extensions="using:UniSky.Extensions"
xmlns:feeds="using:UniSky.ViewModels.Feeds"
xmlns:posts="using:UniSky.ViewModels.Posts"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:toolkit="using:Microsoft.Toolkit.Uwp.UI.Controls"
x:DefaultBindMode="OneWay">

<!-- TODO: un-steal these colours -->
Expand Down Expand Up @@ -134,7 +135,7 @@
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<controls:ConstrainedBox AspectRatio="{x:Bind AspectRatio}">
<toolkit:ConstrainedBox AspectRatio="{Binding AspectRatio}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
Expand All @@ -151,7 +152,7 @@
Padding="0"
Style="{ThemeResource CleanButtonStyle}">
<Image x:Name="PART_Image1A" Stretch="Uniform"
Source="{x:Bind Image1.ThumbnailUrl}"
Source="{Binding Image1.ThumbnailUrl}"
VerticalAlignment="Center"
HorizontalAlignment="Left"/>
</Button>
Expand All @@ -162,7 +163,7 @@
Padding="0"
Style="{ThemeResource CleanButtonStyle}">
<Image Stretch="UniformToFill"
Source="{x:Bind Image2.ThumbnailUrl}"
Source="{Binding Image2.ThumbnailUrl}"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Button>
Expand All @@ -173,7 +174,7 @@
Padding="0"
Style="{ThemeResource CleanButtonStyle}">
<Image Stretch="UniformToFill"
Source="{x:Bind Image3.ThumbnailUrl}"
Source="{Binding Image3.ThumbnailUrl}"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Button>
Expand All @@ -184,18 +185,18 @@
Padding="0"
Style="{ThemeResource CleanButtonStyle}">
<Image Stretch="UniformToFill"
Source="{x:Bind Image4.ThumbnailUrl}"
Source="{Binding Image4.ThumbnailUrl}"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Button>
</Grid>
</controls:ConstrainedBox>
</toolkit:ConstrainedBox>
</Grid>
</UserControl>
</DataTemplate>

<DataTemplate x:Key="VideoEmbedContentTemplate" x:DataType="posts:PostEmbedVideoViewModel">
<controls:ConstrainedBox AspectRatio="{Binding Ratio}">
<toolkit:ConstrainedBox AspectRatio="{Binding Ratio}">
<MediaPlayerElement PosterSource="{Binding ThumbnailUrl}"
Source="{Binding Source}"
AreTransportControlsEnabled="True"
Expand All @@ -206,7 +207,7 @@
<MediaTransportControls Style="{StaticResource MediaTransportControlsStyle}"/>
</MediaPlayerElement.TransportControls>
</MediaPlayerElement>
</controls:ConstrainedBox>
</toolkit:ConstrainedBox>
</DataTemplate>

<datatemplates:PostEmbedTemplateSelector x:Key="PostEmbedTemplateSelector"
Expand Down Expand Up @@ -326,13 +327,14 @@
Typography.NumeralAlignment="Tabular"
Text="{x:Bind Date}"/>
</Grid>
<TextBlock x:Name="MainContent"
x:Load="{x:Bind converters:Static.NotNullOrWhiteSpace(Text)}"
Text="{x:Bind Text}"
TextWrapping="Wrap"
Margin="0,0,0,4"
IsTextSelectionEnabled="True"
VerticalAlignment="Top"/>

<controls:RichTextBlock x:Name="MainContent"
x:Load="{x:Bind converters:Static.NotNullOrWhiteSpace(Text)}"
Inlines="{x:Bind RichText.Facets}"
TextWrapping="Wrap"
Margin="0,0,0,4"
IsTextSelectionEnabled="True"
VerticalAlignment="Top"/>

<Grid x:Name="EmbedContentContainer"
x:Load="{x:Bind converters:Static.NotNull(Embed)}"
Expand Down
8 changes: 8 additions & 0 deletions UniSky/Package.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@
</uap:DefaultTile >
<uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#0085ff" />
</uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="unisky">
<uap:DisplayName>Unisky</uap:DisplayName>
<uap:Logo>Assets\Square44x44Logo.png</uap:Logo>
</uap:Protocol>
</uap:Extension>
</Extensions>
</Application>
</Applications>

Expand Down
20 changes: 18 additions & 2 deletions UniSky/Pages/ProfilePage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using CommunityToolkit.Mvvm.DependencyInjection;
using FishyFlip.Lexicon;
using FishyFlip.Lexicon.App.Bsky.Actor;
Expand Down Expand Up @@ -56,10 +58,12 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);

if (e.Parameter is not (ProfileViewBasic or ProfileViewDetailed))
if (e.Parameter is not (ProfileViewBasic or ProfileViewDetailed or Uri))
return;

if (e.Parameter is ATObject basic)
if (e.Parameter is Uri { Scheme: "unisky" } uri)
HandleUniskyProtocol(uri);
else if (e.Parameter is ATObject basic)
this.DataContext = ActivatorUtilities.CreateInstance<ProfilePageViewModel>(Ioc.Default, basic);

var safeAreaService = Ioc.Default.GetRequiredService<ISafeAreaService>();
Expand All @@ -72,6 +76,18 @@ protected override void OnNavigatedFrom(NavigationEventArgs e)
safeAreaService.SafeAreaUpdated -= OnSafeAreaUpdated;
}

private void HandleUniskyProtocol(Uri uri)
{
var path = uri.PathAndQuery.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (path.Length < 2 || !string.Equals(path[0], "profile", StringComparison.InvariantCultureIgnoreCase))
{
this.Frame.Navigate(typeof(FeedsPage));
}

if (ATDid.TryCreate(path[1], out var did))
this.DataContext = ActivatorUtilities.CreateInstance<ProfilePageViewModel>(Ioc.Default, did);
}

private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Retrieve the ScrollViewer that the GridView is using internally
Expand Down
3 changes: 2 additions & 1 deletion UniSky/RootPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
xmlns:viewmodels="using:UniSky.ViewModels"
xmlns:pages="using:UniSky.Pages"
xmlns:sheet="using:UniSky.Controls.Sheet"
mc:Ignorable="d">
mc:Ignorable="d"
NavigationCacheMode="Required">
<Page.DataContext>
<viewmodels:RootViewModel/>
</Page.DataContext>
Expand Down
Loading

0 comments on commit 8b86507

Please sign in to comment.