-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v1.3 #6
v1.3 #6
Changes from 35 commits
91c72c6
a8d5799
951a0fc
9f23718
1fcb9a7
ae9fcc4
a5e7a39
9e50293
de41d7c
1472183
5cd9d3c
d9881d7
daadbbe
34012cc
4311024
4a3265f
fad5086
5d47dc2
52ca12a
0567963
7644b69
eefeb8f
9905625
97bc11a
6b40596
44bf59a
f95989d
435ff7d
5adb813
698feb3
dfd8dd4
6e34588
f2a8557
8e25fb5
70f3d6b
e7e2d9a
2509796
d07875f
42d7cbd
f731566
0cdd2b6
89fff3c
d80cb5f
c27c695
bc0ab50
5603964
3e8f1aa
a02c617
8127bf8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Text.RegularExpressions; | ||
using System.Threading.Tasks; | ||
using Avalonia; | ||
using Avalonia.Controls; | ||
using Avalonia.Controls.Primitives; | ||
using Avalonia.Input; | ||
using Avalonia.Interactivity; | ||
using Avalonia.Layout; | ||
using Avalonia.Markup.Xaml.MarkupExtensions; | ||
using Avalonia.Media; | ||
using Avalonia.Media.Imaging; | ||
using Blast.API.Core.Processes; | ||
using Blast.API.Graphics; | ||
using Blast.API.Processes; | ||
using Blast.Core.Interfaces; | ||
using TextCopy; | ||
using static WikiPreview.Fluent.Plugin.WikiPreviewSearchResult; | ||
using static System.Environment; | ||
using static WikiPreview.Fluent.Plugin.WikiPreviewSearchApp; | ||
using static WikiPreview.Fluent.Plugin.ResultGenerator; | ||
|
||
namespace WikiPreview.Fluent.Plugin | ||
{ | ||
public class CustomPreview : IResultPreviewControlBuilder | ||
{ | ||
private const string GoogleStr = "Search Google"; | ||
private const string WikipediaStr = "Wikipedia"; | ||
private const string CopyStr = "Copy Text"; | ||
|
||
public CustomPreview() | ||
{ | ||
PreviewBuilderDescriptor = new PreviewBuilderDescriptor | ||
{ | ||
Name = "Wikipedia Preview", | ||
Description = "Displays the Wikipedia Article Information within Fluent Search.", | ||
ShowPreviewAutomatically = true | ||
}; | ||
} | ||
|
||
public bool CanBuildPreviewForResult(ISearchResult searchResult) | ||
{ | ||
if (string.IsNullOrWhiteSpace(searchResult.SearchApp)) return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition is redundant since you do |
||
|
||
if (searchResult.SearchApp.Equals(SearchAppName, | ||
StringComparison.OrdinalIgnoreCase)) return true; | ||
|
||
string host = searchResult.Context; | ||
if (string.IsNullOrWhiteSpace(host)) return false; | ||
|
||
bool result = Uri.TryCreate(host, UriKind.Absolute, out Uri uri) | ||
&& uri.Scheme == Uri.UriSchemeHttps; | ||
|
||
if (!result) return false; | ||
|
||
host = uri.Host[3..]; | ||
return host.StartsWith("wikipedia.org") && uri.Segments.Length == 3 && uri.Fragment.Length == 0; | ||
} | ||
|
||
public ValueTask<Control> CreatePreviewControl(ISearchResult searchResult) | ||
{ | ||
if (searchResult is WikiPreviewSearchResult result) | ||
{ | ||
Control control = GeneratePreview(result); | ||
return new ValueTask<Control>(control); | ||
} | ||
|
||
string pageName = searchResult.Context.Split('/').Last(); | ||
return GenerateElement(pageName); | ||
} | ||
|
||
public PreviewBuilderDescriptor PreviewBuilderDescriptor { get; } | ||
|
||
private static async ValueTask<Control> GenerateElement(string pageName) | ||
{ | ||
WikiPreviewSearchResult searchResult = await Instance.GenerateOnDemand(pageName, true, false); | ||
|
||
if (searchResult == null || string.IsNullOrWhiteSpace(searchResult.ResultName)) return default; | ||
|
||
Control control = GeneratePreview(searchResult); | ||
return control; | ||
} | ||
|
||
private static Control GeneratePreview(WikiPreviewSearchResult searchResult) | ||
{ | ||
string text = searchResult.ResultName; | ||
|
||
// double the new lines for better reading. | ||
text = Regex.Replace(text, @"\r\n?|\n", NewLine + NewLine); | ||
|
||
// StackPanel to store image and text. | ||
var wikiDetails = new DockPanel(); | ||
|
||
// creates image control. | ||
if (searchResult.PreviewImage is { IsEmpty: false }) | ||
{ | ||
Bitmap bitmap = searchResult.PreviewImage.ConvertToAvaloniaBitmap(); | ||
|
||
if (!bitmap.Dpi.IsDefault & !bitmap.Size.IsDefault) | ||
{ | ||
var imageControl = new Border | ||
{ | ||
Background = new ImageBrush(bitmap) | ||
{ | ||
Stretch = Stretch.UniformToFill | ||
}, | ||
CornerRadius = new CornerRadius(5.0), | ||
BorderThickness = new Thickness(5.0), | ||
Height = bitmap.Size.Height, | ||
Width = bitmap.Size.Width, | ||
MaxHeight = Instance.GetImageSize(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you use static import then the property need to have better name, this is really not clear There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've changed the methods to |
||
MaxWidth = Instance.GetImageSize() | ||
}; | ||
|
||
wikiDetails.Children.Add(imageControl); | ||
imageControl.SetValue(DockPanel.DockProperty, Dock.Top); | ||
} | ||
} | ||
|
||
// creates article content. | ||
var wikiDescription = new TextBlock | ||
{ | ||
Text = text, Padding = new Thickness(5, 10, 5, 0), TextWrapping = TextWrapping.Wrap, | ||
TextTrimming = TextTrimming.WordEllipsis | ||
}; | ||
|
||
wikiDetails.Children.Add(wikiDescription); | ||
|
||
var scrollViewer = new ScrollViewer | ||
{ | ||
Content = wikiDetails, | ||
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden, | ||
Margin = new Thickness(0, 0, 0, 5) | ||
}; | ||
|
||
scrollViewer.PointerEnter += ScrollViewerOnPointerEnter; | ||
scrollViewer.PointerLeave += ScrollViewerOnPointerLeave; | ||
|
||
// Create Parent Grid. | ||
var grid = new Grid | ||
{ | ||
Margin = new Thickness(10.0, 0, 10, 10) | ||
}; | ||
|
||
grid.RowDefinitions.Add(new RowDefinition()); | ||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); | ||
grid.Children.Add(scrollViewer); | ||
|
||
Button openWiki = CreateButton(WikipediaStr, searchResult.Url); | ||
Button searchGoogle = CreateButton(GoogleStr, searchResult.DisplayedName); | ||
Button copyContents = CreateButton(CopyStr, text); | ||
|
||
var uniformGrid = new UniformGrid | ||
{ | ||
Columns = 3, | ||
Rows = 0 | ||
}; | ||
|
||
uniformGrid.Children.Add(openWiki); | ||
uniformGrid.Children.Add(searchGoogle); | ||
uniformGrid.Children.Add(copyContents); | ||
|
||
// add buttons grid to the bottom row. | ||
Grid.SetRow(uniformGrid, 1); | ||
grid.Children.Add(uniformGrid); | ||
|
||
return grid; | ||
} | ||
|
||
private static void ScrollViewerOnPointerLeave(object sender, PointerEventArgs e) | ||
{ | ||
if (sender is ScrollViewer scrollViewer) | ||
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden; | ||
} | ||
|
||
private static void ScrollViewerOnPointerEnter(object sender, PointerEventArgs e) | ||
{ | ||
if (sender is ScrollViewer scrollViewer) | ||
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; | ||
} | ||
|
||
private static Button CreateButton(string text, string tag) | ||
{ | ||
var button = new Button | ||
{ | ||
Content = text, | ||
Margin = new Thickness(0, 0, 5, 0), | ||
HorizontalAlignment = HorizontalAlignment.Stretch, | ||
VerticalAlignment = VerticalAlignment.Stretch, | ||
Tag = tag, | ||
[!TemplatedControl.BackgroundProperty] = new DynamicResourceExtension("TextControlBackground") | ||
}; | ||
|
||
button.Click += ButtonOnClick; | ||
return button; | ||
} | ||
|
||
private static void ButtonOnClick(object sender, RoutedEventArgs e) | ||
{ | ||
var button = sender as Button; | ||
string buttonContent = button?.Content.ToString(); | ||
|
||
if (string.IsNullOrWhiteSpace(buttonContent)) return; | ||
|
||
string buttonTag = button.Tag?.ToString(); | ||
|
||
if (string.IsNullOrWhiteSpace(buttonTag)) return; | ||
|
||
IProcessManager managerInstance = ProcessUtils.GetManagerInstance(); | ||
if (buttonContent.Contains(WikipediaStr)) | ||
managerInstance.StartNewProcess(WikiRootUrl + buttonTag); | ||
else if (buttonContent.Contains(GoogleStr)) | ||
managerInstance.StartNewProcess(GoogleSearchUrl + buttonTag); | ||
else if (buttonContent.Contains(CopyStr)) Clipboard.SetText(buttonTag); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Drawing; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Net.Http.Json; | ||
using System.Reflection; | ||
using System.Threading.Tasks; | ||
using Blast.API.Search; | ||
using Blast.Core.Results; | ||
using static WikiPreview.Fluent.Plugin.WikiPreviewSearchApp; | ||
using static WikiPreview.Fluent.Plugin.WikiResult; | ||
|
||
namespace WikiPreview.Fluent.Plugin | ||
{ | ||
public sealed class ResultGenerator | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What this class is doing, why it's holding the image and keep it's size, nothing is clear here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's holding the placeholder image of Wiki logo, as it will be used for search results that doesn't have any images. Since I don't want the instance of image to be created every time, I'm using static class in the new code. Coming to image size, I've given users an option to change the image size through FS Plugin Settings. Since this image size preferences required across multiple classes, I'm storing it inside this static class. |
||
{ | ||
private static readonly Lazy<ResultGenerator> LazySingleton = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you using lazy to not create the instance of the image below? Are there any cases you search with Wiki and not load it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it was mainly for loading the instance of the image and also store the image size preferences, also making it accessible across all classes. Earlier I thought lazy implementation can be more efficient. In the new code, I've achieved the same behavior that I wanted in a static way. |
||
new(() => new ResultGenerator()); | ||
|
||
private readonly BitmapImageResult _bitmapLogo; | ||
|
||
private ResultGenerator() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need to load the image every time this class is created? Make it a static property There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was a Singleton before where only same instance will be provided. Now I've made it into |
||
{ | ||
var assembly = Assembly.GetExecutingAssembly(); | ||
const string resourceName = "WikiPreview.Fluent.Plugin.Wikipedia-logo.png"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to declare There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For some reasons, IDE would keep on suggesting me to declare |
||
_bitmapLogo = new BitmapImageResult(assembly.GetManifestResourceStream(resourceName)); | ||
} | ||
|
||
private int ImageSize { get; set; } | ||
|
||
public static ResultGenerator Instance => LazySingleton.Value; | ||
|
||
public int GetImageSize() | ||
{ | ||
return ImageSize; | ||
} | ||
|
||
public void SetImageSize(int size) | ||
{ | ||
ImageSize = size; | ||
} | ||
|
||
public async ValueTask<WikiPreviewSearchResult> GenerateSearchResult(PageView value, | ||
string searchedText, bool loadImage = true) | ||
{ | ||
string resultName = value.Extract; | ||
string displayedName = value.Title; | ||
double score = displayedName.SearchDistanceScore(searchedText); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better use |
||
string pageId = value.PageId.ToString(); | ||
string wikiUrl = displayedName.Replace(' ', '_'); | ||
BitmapImageResult bitmapImageResult; | ||
|
||
if (value.Thumbnail != null && loadImage) | ||
{ | ||
string imgUrl = value.Thumbnail.Source; | ||
using var imageClient = new HttpClient(); | ||
imageClient.DefaultRequestHeaders.UserAgent.TryParseAdd(UserAgentString); | ||
Stream stream = await imageClient.GetStreamAsync(imgUrl); | ||
var bitmap = new Bitmap(stream); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you creating There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, |
||
bitmapImageResult = new BitmapImageResult(bitmap); | ||
} | ||
else | ||
{ | ||
bitmapImageResult = _bitmapLogo; | ||
} | ||
|
||
return new WikiPreviewSearchResult(resultName) | ||
{ | ||
Url = wikiUrl, | ||
PreviewImage = bitmapImageResult, | ||
DisplayedName = displayedName, | ||
SearchedText = searchedText, | ||
Score = score, | ||
SearchObjectId = pageId, | ||
PinUniqueId = pageId | ||
}; | ||
} | ||
|
||
public async ValueTask<WikiPreviewSearchResult> GenerateOnDemand(string searchId, bool isCustomPreview = false, | ||
bool loadImage = true) | ||
{ | ||
if (string.IsNullOrWhiteSpace(searchId)) | ||
return default; | ||
|
||
string searchType = isCustomPreview ? "titles=" : "pageids="; | ||
|
||
string url = "https://en.wikipedia.org/w/api.php?action=query&prop=extracts|pageimages&" + searchType + | ||
searchId + | ||
"&explaintext&exintro&pilicense=any&pithumbsize=100&format=json"; | ||
|
||
using var httpClient = new HttpClient(); | ||
httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd(UserAgentString); | ||
var wiki = await httpClient.GetFromJsonAsync<Wiki>(url, SerializerOptions); | ||
if (wiki == null) return default; | ||
|
||
Dictionary<string, PageView>.ValueCollection pages = wiki.Query.Pages.Values; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check for null |
||
if (pages is { Count: 0 }) return default; | ||
|
||
PageView pageView = pages.First(); | ||
return await GenerateSearchResult(pageView, pageView?.Title, loadImage); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,20 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net5.0-windows10.0.19041</TargetFramework> | ||
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> | ||
None | ||
</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> | ||
<Platforms>AnyCPU;x64</Platforms> | ||
<AssemblyVersion>1.2.0.0</AssemblyVersion> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<Platforms>AnyCPU;x64</Platforms> | ||
<AssemblyVersion>1.3.0.0</AssemblyVersion> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AsyncEnumerator" Version="4.0.2" /> | ||
<PackageReference Include="Blast.API" Version="0.9.76.5-beta" /> | ||
<PackageReference Include="TextCopy" Version="1.5.1" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="AsyncEnumerator" Version="4.0.2"/> | ||
<PackageReference Include="Blast.API" Version="0.9.89.1-beta"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update NuGets |
||
<PackageReference Include="TextCopy" Version="1.5.1"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is redundant |
||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Remove="Wikipedia-logo.png"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is redundant |
||
<EmbeddedResource Include="Wikipedia-logo.png"/> | ||
</ItemGroup> | ||
|
||
</Project> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to follow the interface name
WikiResultPreviewControlBuilder