Commit f20e5558 authored by HankG's avatar HankG

Add PeopleListview, Timeline into separate view, toggling between timelines

parent 4674bb73
Pipeline #98791901 failed with stage
in 60 minutes
......@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<PackageVersion>1.0.0</PackageVersion>
<PackageVersion>1.0.0-alpha2</PackageVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<NoWarn>1701;1702;1303</NoWarn>
......
using System;
using System.Collections.Generic;
using System.IO;
using Avalonia.Media.Imaging;
using MySocialPortalDesktop.Factory;
using MySocialPortalDesktop.ViewModels;
using MySocialPortalLib.Model;
using MySocialPortalLib.Service;
namespace MySocialPortalDesktop.Services
......@@ -26,5 +30,11 @@ namespace MySocialPortalDesktop.Services
}
}
public static PostTimelineViewModel GetDefaultHomePostTimelineViewModel(int maxPosts)
{
var posts = RepositoryFactory.Instance.AllPostsRepository.GetPosts(maxPosts);
return new PostTimelineViewModel(posts);
}
}
}
\ No newline at end of file
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Media.Imaging;
using JetBrains.Annotations;
......@@ -34,16 +35,22 @@ namespace MySocialPortalDesktop.Util
return DefaultValueService.DefaultProfileImage;
}
var profileImageUrl = person.SocialMediaAccounts.First().Value.ProfilePhotoPath;
try
{
var profileImageUrl = person.SocialMediaAccounts.First().Value.ProfilePhotoPath;
if (!string.IsNullOrWhiteSpace(profileImageUrl))
{
var (found, imageFileInfo) = await ServiceFactory.Instance.ProfileImageService
.GetRemoteImage(new Uri(profileImageUrl)).ConfigureAwait(false);
if (found)
var cancelToken = new CancellationTokenSource(2000);
var task = ServiceFactory.Instance.ProfileImageService
.GetRemoteImage(new Uri(profileImageUrl));
task.Wait(100, cancelToken.Token);
if (task.IsCompletedSuccessfully)
{
return new Bitmap(imageFileInfo.FullName);
var (found, imageFileInfo) = task.Result;
if (found)
{
return new Bitmap(imageFileInfo.FullName);
}
}
}
}
......
......@@ -58,11 +58,7 @@ namespace MySocialPortalDesktop.ViewModels
if (item != null && item.UrlFound)
{
vm.Title = item.Title;
if (item.Description != null)
{
vm.Description = item.Description.Length > MaxDescriptionLenth ? item.Description.Substring(0, MaxDescriptionLenth) + "..." : item.Description;
}
vm.Description = item.Description ?? "";
vm.HasPreview = true;
vm.Url = item.RequestUrl;
if (!item.HasPreviewImage)
......
......@@ -11,6 +11,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using DynamicData;
using LiteDB;
using MySocialPortalDesktop.Factory;
using MySocialPortalDesktop.Services;
using MySocialPortalLib.Model;
using MySocialPortalLib.Service.SocialMediaConnectors;
using MySocialPortalLib.Util;
......@@ -20,21 +21,17 @@ namespace MySocialPortalDesktop.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
private const int MaxPostsQuery = 25;
private const int MaxPostsFromRepo = 200;
public ObservableCollection<PostViewModel> PostViewModels { get; }
private const int DefaultMaxPosts = 50;
public PeopleListViewModel PeopleListViewModel { get; }
public PostTimelineViewModel PostTimelineViewModel { get; }
public MainWindowViewModel()
{
PostViewModels = new ObservableCollection<PostViewModel>();
TwitterConnector = SocialMediaConnectorFactory.GetNewTwitterConnector();
ImportedPosts = new List<Post>();
PeopleListViewModel = new PeopleListViewModel();
LoadInitialPosts();
PostTimelineViewModel = DefaultValueService.GetDefaultHomePostTimelineViewModel(DefaultMaxPosts);
PeopleListViewModel = new PeopleListViewModel(PostTimelineViewModel);
}
public void DoExit()
......@@ -43,23 +40,6 @@ namespace MySocialPortalDesktop.ViewModels
app?.Shutdown();
}
public void LoadNewer()
{
var posts = TwitterConnector.GetNewerHomeTimeline(MaxPostsQuery).ToList();
ProcessPosts(posts, true);
}
public void LoadOlder()
{
if (ImportedPosts.Count > 0)
{
ProcessImported();
return;
}
var posts = TwitterConnector.GetOlderHomeTimeline(MaxPostsQuery).ToList();
ProcessPosts(posts, false);
}
public async void ImportPosts()
{
var app = Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
......@@ -79,104 +59,13 @@ namespace MySocialPortalDesktop.ViewModels
}
}
};
var result = await fileDialog.ShowAsync(window).ConfigureAwait(false);
if (result != null)
{
await ImportPostsDatabase(result.First()).ConfigureAwait(false);
}
await fileDialog.ShowAsync(window).ConfigureAwait(false);
Console.WriteLine("Will import posts when get better post processor");
}
private bool ProcessImported()
{
foreach (var post in ImportedPosts)
{
try
{
PostViewModels.Add(new PostViewModel(post));
}
catch (Exception e1)
{
Console.WriteLine($"Error processing post {post?.Id}: {e1}");
}
}
return true;
}
private List<Post> ImportedPosts { get; }
private TwitterConnector TwitterConnector { get; }
private async Task<bool> ImportPostsDatabase(string jsonFile)
{
Console.WriteLine("Importing Posts");
try
{
var postsToImport =
System.Text.Json.JsonSerializer.Deserialize<List<Post>>(System.IO.File.ReadAllText(jsonFile));
ImportedPosts.AddRange(postsToImport);
}
catch (Exception e2)
{
Console.WriteLine($"Error reading file '{jsonFile}': {e2}");
}
Console.WriteLine("Import Complete");
return true;
}
private void LoadInitialPosts()
{
var posts = RepositoryFactory.Instance.AllPostsRepository.GetPosts(MaxPostsFromRepo);
if (posts == null)
{
return;
}
var postsList = posts.ToList();
postsList.Sort(new PostComparisonDescending());
postsList.ForEach(p => PostViewModels.Add(new PostViewModel(p)));
}
private void ProcessPosts(List<Post> posts, bool insertTop)
{
if (posts.Count == 0)
{
Console.WriteLine("No posts returned");
return;
}
RepositoryFactory.Instance.AllPostsRepository.AddPosts(posts);
if (insertTop)
{
posts.Sort(new PostComparisonAscending());
posts.ForEach(p => PostViewModels.Insert(0, new PostViewModel(p)));
}
else
{
posts.Sort(new PostComparisonDescending());
var currentGuessIndex = 0;
foreach (var post in posts)
{
var postDate = post.PostDateTime.LocalDateTime;
var guessDate = PostViewModels[currentGuessIndex].Date;
if (postDate >= guessDate)
{
PostViewModels.Insert(currentGuessIndex, new PostViewModel(post));
continue;
}
if (currentGuessIndex == PostViewModels.Count - 1)
{
PostViewModels.Insert(currentGuessIndex, new PostViewModel(post));
continue;
}
currentGuessIndex++;
}
PostViewModels.AddRange(posts.Select(p => new PostViewModel(p)));
}
}
}
}
......@@ -24,10 +24,15 @@ namespace MySocialPortalDesktop.ViewModels
public ObservableCollection<PersonViewModel> People { get; }
public PeopleListViewModel()
public PeopleListViewModel(PostTimelineViewModel relatedTimelineViewModel)
{
var allPeople = RepositoryFactory.Instance.MainPeopleRepository.ListAll();
var peopleVms = allPeople?.Select(p => new PersonViewModel(p)).ToList() ?? new List<PersonViewModel>();
if (relatedTimelineViewModel != null)
{
peopleVms.ForEach(p => p.TimelineToDrive = relatedTimelineViewModel);
}
People = new ObservableCollection<PersonViewModel>(peopleVms);
FullPeopleList = new List<PersonViewModel>(peopleVms);
CurrentViewFilter = FilterText = "";
......@@ -56,7 +61,6 @@ namespace MySocialPortalDesktop.ViewModels
UpdateSortedView();
}
private IComparer<PersonViewModel> CurrentComparer { get; set; }
......
using System;
using System.Linq;
using Avalonia.Media.Imaging;
using MySocialPortalDesktop.Services;
......@@ -13,6 +14,13 @@ namespace MySocialPortalDesktop.ViewModels
private string _displayUsername;
private Bitmap _displayIcon;
private Person _person;
private PostTimelineViewModel _timelineToDrive;
public PostTimelineViewModel TimelineToDrive
{
get => _timelineToDrive;
set => this.RaiseAndSetIfChanged(ref _timelineToDrive, value);
}
public string DisplayName
{
......@@ -47,6 +55,12 @@ namespace MySocialPortalDesktop.ViewModels
Person = person;
}
public void ViewTimeline()
{
Console.WriteLine($"Change timeline to be for user {Person.Name}");
TimelineToDrive?.ConfigureSources(Person);
}
private void UpdatePersonData()
{
DisplayName = string.IsNullOrWhiteSpace(Person.Name) ? "" : Person.Name;
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using DynamicData;
using MySocialPortalDesktop.Factory;
using MySocialPortalLib.Model;
using MySocialPortalLib.Service.SocialMediaConnectors;
using MySocialPortalLib.Util;
namespace MySocialPortalDesktop.ViewModels
{
public class PostTimelineViewModel : ViewModelBase
{
private const int MaxPostsQuery = 5;
private const int MaxPostHistory = 50;
public ObservableCollection<PostViewModel> PostViewModels { get; }
public PostTimelineViewModel(IEnumerable<Post> initialPosts)
{
PostViewModels = new ObservableCollection<PostViewModel>();
TwitterConnector = SocialMediaConnectorFactory.GetNewTwitterConnector();
LoadPosts(initialPosts);
}
public PostTimelineViewModel():this(null)
{
}
public void ConfigureSources(Person person = null, string network = "")
{
IEnumerable<Post> posts;
CurrentPerson = person;
if (person == null)
{
Console.WriteLine("Getting 'Home' view");
posts = RepositoryFactory.Instance.AllPostsRepository.GetPosts(MaxPostHistory);
}
else
{
Console.WriteLine($"Configuring timeline for User '{CurrentPerson.Name}' and network '{network}'");
posts = RepositoryFactory.Instance.AllPostsRepository.GetPosts(MaxPostHistory, CurrentPerson.Id, network);
}
PostViewModels.Clear();
LoadPosts(posts);
}
public void GoHome()
{
ConfigureSources(null);
}
public void LoadNewer()
{
try
{
var posts = CurrentPerson == null ?
TwitterConnector.GetNewerHomeTimeline(MaxPostsQuery).ToList() :
TwitterConnector.GetNewerUserTimeline(CurrentPerson, MaxPostsQuery).ToList();
ProcessNewPosts(posts, true);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public void LoadOlder()
{
try
{
var posts = CurrentPerson == null ?
TwitterConnector.GetOlderHomeTimeline(MaxPostsQuery).ToList() :
TwitterConnector.GetOlderUserTimeline(CurrentPerson, MaxPostsQuery).ToList();
ProcessNewPosts(posts, false);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private Person CurrentPerson { get; set; }
private TwitterConnector TwitterConnector { get; }
private void LoadPosts(IEnumerable<Post> posts)
{
if (posts == null)
{
return;
}
var postsList = posts.ToList();
postsList.Sort(new PostComparisonDescending());
postsList.ForEach(p => PostViewModels.Add(new PostViewModel(p)));
}
private void ProcessNewPosts(List<Post> posts, bool insertTop)
{
if (posts.Count == 0)
{
Console.WriteLine("No posts returned");
return;
}
var filteredPosts = posts.FindAll(p =>
RepositoryFactory.Instance.AllPostsRepository.GetByOriginalNetworkId(p.OriginalLinkUrlOrId) == null);
RepositoryFactory.Instance.AllPostsRepository.AddPosts(filteredPosts);
if (insertTop)
{
posts.Sort(new PostComparisonAscending());
posts.ForEach(p => PostViewModels.Insert(0, new PostViewModel(p)));
}
else
{
posts.Sort(new PostComparisonDescending());
var currentGuessIndex = 0;
foreach (var post in posts)
{
var postDate = post.PostDateTime.LocalDateTime;
var guessDate = PostViewModels[currentGuessIndex].Date;
if (postDate >= guessDate)
{
PostViewModels.Insert(currentGuessIndex, new PostViewModel(post));
continue;
}
if (currentGuessIndex == PostViewModels.Count - 1)
{
PostViewModels.Insert(currentGuessIndex, new PostViewModel(post));
continue;
}
currentGuessIndex++;
}
PostViewModels.AddRange(posts.Select(p => new PostViewModel(p)));
}
}
}
}
\ No newline at end of file
......@@ -33,18 +33,7 @@
</Grid.ColumnDefinitions>
<views:PeopleListView Grid.Row="0" Grid.Column="0" DataContext="{Binding PeopleListViewModel}"/>
<GridSplitter Grid.Row="0" Grid.Column="1"/>
<Grid Grid.Row="0" Grid.Column="2" Name="TimelineGrid" RowDefinitions="Auto,*,Auto" ColumnDefinitions="*">
<Button Grid.Row="0" Grid.Column="0" Width="100" Content="Load Newer" Command="{Binding LoadNewer}"/>
<ListBox Grid.Row="1" Grid.Column="0" Items="{Binding PostViewModels}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<views:PostView Name="PostsView" DataContext="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="2" Grid.Column="0" Width="100" Content="Load Older" Command="{Binding LoadOlder}"/>
</Grid>
<views:PostTimelineView Grid.Row="0" Grid.Column="2" DataContext="{Binding PostTimelineViewModel}"/>
</Grid>
</DockPanel>
</Window>
......@@ -31,6 +31,11 @@
<ListBox.DataTemplates>
<DataTemplate DataType="vm:PersonViewModel">
<Grid RowDefinitions="Auto, Auto" ColumnDefinitions="Auto, Auto">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="View Timeline" Command="{Binding ViewTimeline}"/>
</ContextMenu>
</Grid.ContextMenu>
<Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Margin="5" Source="{Binding DisplayIcon}" Width="50" Height="50"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding DisplayName}" Classes="RealName"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding DisplayUsername}" Classes="Username"/>
......
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:MySocialPortalDesktop.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MySocialPortalDesktop.Views.PostTimelineView">
<Grid Grid.Row="0" Grid.Column="0" Name="TimelineGrid" RowDefinitions="Auto,*,Auto" ColumnDefinitions="*">
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Width="100" Content="Load Newer" Command="{Binding LoadNewer}"/>
<Button Width="100" Content="Home" Command="{Binding GoHome}"/>
</StackPanel>
<ListBox Grid.Row="1" Grid.Column="0" Items="{Binding PostViewModels}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<views:PostView Name="PostsView" DataContext="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="2" Grid.Column="0" Width="100" Content="Load Older" Command="{Binding LoadOlder}"/>
</Grid>
</UserControl>
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace MySocialPortalDesktop.Views
{
public class PostTimelineView : UserControl
{
public PostTimelineView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment