diff --git a/Unigram/Unigram.Native/PlaceholderImageHelper.cpp b/Unigram/Unigram.Native/PlaceholderImageHelper.cpp index 900fdc8b49..75ee3c7213 100644 --- a/Unigram/Unigram.Native/PlaceholderImageHelper.cpp +++ b/Unigram/Unigram.Native/PlaceholderImageHelper.cpp @@ -164,140 +164,88 @@ HRESULT PlaceholderImageHelper::InternalDrawSvg(String^ path, _In_ Color foregro ReturnIfFailed(result, m_d2dContext->CreateSolidColorBrush( D2D1::ColorF(foreground.R / 255.0f, foreground.G / 255.0f, foreground.B / 255.0f, foreground.A / 255.0f), &blackBrush)); - for (auto shape = image->shapes; shape != NULL; shape = shape->next) { - if (!(shape->flags & NSVG_FLAGS_VISIBLE)) { + for (auto shape = image->shapes; shape != NULL; shape = shape->next) + { + if (!(shape->flags & NSVG_FLAGS_VISIBLE) || (shape->fill.type == NSVG_PAINT_NONE && shape->stroke.type == NSVG_PAINT_NONE)) + { continue; } - if (shape->fill.type != NSVG_PAINT_NONE) { - - ComPtr geometry; - ReturnIfFailed(result, m_d2dFactory->CreatePathGeometry(&geometry)); - - ComPtr sink; - ReturnIfFailed(result, geometry->Open(&sink)); - ////CGContextSetFillColorWithColor(context, UIColorRGBA(shape->fill.color, shape->opacity).CGColor); - //CGContextSetFillColorWithColor(context, [foregroundColor colorWithAlphaComponent : shape->opacity].CGColor); - - bool isFirst = true; - bool hasStartPoint = false; - D2D1_POINT_2F startPoint; - for (NSVGpath* path = shape->paths; path != NULL; path = path->next) { - if (isFirst) { - //CGContextBeginPath(context); - sink->BeginFigure(D2D1::Point2F(path->pts[0], path->pts[1]), D2D1_FIGURE_BEGIN_FILLED); - isFirst = false; - hasStartPoint = true; - startPoint.x = path->pts[0]; - startPoint.y = path->pts[1]; - } - //CGContextMoveToPoint(context, path->pts[0], path->pts[1]); - else { - sink->AddLine(D2D1::Point2F(path->pts[0], path->pts[1])); - } - for (int i = 0; i < path->npts - 1; i += 3) { - float* p = &path->pts[i * 2]; - //CGContextAddCurveToPoint(context, p[2], p[3], p[4], p[5], p[6], p[7]); - sink->AddBezier(D2D1::BezierSegment(D2D1::Point2F(p[2], p[3]), D2D1::Point2F(p[4], p[5]), D2D1::Point2F(p[6], p[7]))); - } + blackBrush->SetOpacity(shape->opacity); - if (path->closed) { - if (hasStartPoint) { - hasStartPoint = false; - //CGContextAddLineToPoint(context, startPoint.x, startPoint.y); - sink->AddLine(startPoint); - } - } + ComPtr geometry; + ReturnIfFailed(result, m_d2dFactory->CreatePathGeometry(&geometry)); - if (path->next != NULL) { - int a = 1 + 2; - } - } - sink->EndFigure(D2D1_FIGURE_END_OPEN); - switch (shape->fillRule) { - case NSVG_FILLRULE_EVENODD: - //CGContextEOFillPath(context); - sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE); - break; - default: - //CGContextFillPath(context); - sink->SetFillMode(D2D1_FILL_MODE_WINDING); - break; - } + ComPtr sink; + ReturnIfFailed(result, geometry->Open(&sink)); - ReturnIfFailed(result, sink->Close()); - m_d2dContext->FillGeometry(geometry.Get(), m_black.Get()); - } + for (NSVGpath* path = shape->paths; path != NULL; path = path->next) + { + sink->BeginFigure({ path->pts[0], path->pts[1] }, D2D1_FIGURE_BEGIN_FILLED); - if (shape->stroke.type != NSVG_PAINT_NONE) { - ComPtr geometry; - ReturnIfFailed(result, m_d2dFactory->CreatePathGeometry(&geometry)); + for (int i = 0; i < path->npts - 1; i += 3) + { + float* p = &path->pts[i * 2]; + sink->AddBezier({ { p[2], p[3] }, { p[4], p[5] }, { p[6], p[7] }}); + } - ComPtr sink; - ReturnIfFailed(result, geometry->Open(&sink)); + sink->EndFigure(path->closed ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN); + } - ////CGContextSetStrokeColorWithColor(context, UIColorRGBA(shape->fill.color, shape->opacity).CGColor); - //CGContextSetStrokeColorWithColor(context, [foregroundColor colorWithAlphaComponent : shape->opacity].CGColor); - //CGContextSetMiterLimit(context, shape->miterLimit); + if (shape->fill.type != NSVG_PAINT_NONE) + { + switch (shape->fillRule) + { + case NSVG_FILLRULE_EVENODD: + sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE); + break; + default: + sink->SetFillMode(D2D1_FILL_MODE_WINDING); + break; + } + ReturnIfFailed(result, sink->Close()); + m_d2dContext->FillGeometry(geometry.Get(), blackBrush.Get()); + } + if (shape->stroke.type != NSVG_PAINT_NONE) + { D2D1_STROKE_STYLE_PROPERTIES1 strokeProperties{}; strokeProperties.miterLimit = shape->miterLimit; - //CGContextSetLineWidth(context, shape->strokeWidth); - switch (shape->strokeLineCap) { - case NSVG_CAP_BUTT: - //CGContextSetLineCap(context, kCGLineCapButt); - strokeProperties.startCap = strokeProperties.endCap = D2D1_CAP_STYLE_FLAT; - break; - case NSVG_CAP_ROUND: - //CGContextSetLineCap(context, kCGLineCapRound); - strokeProperties.startCap = strokeProperties.endCap = D2D1_CAP_STYLE_ROUND; - break; - case NSVG_CAP_SQUARE: - //CGContextSetLineCap(context, kCGLineCapSquare); - strokeProperties.startCap = strokeProperties.endCap = D2D1_CAP_STYLE_SQUARE; - break; - default: - break; + switch (shape->strokeLineCap) + { + case NSVG_CAP_BUTT: + strokeProperties.startCap = strokeProperties.endCap = D2D1_CAP_STYLE_FLAT; + break; + case NSVG_CAP_ROUND: + strokeProperties.startCap = strokeProperties.endCap = D2D1_CAP_STYLE_ROUND; + break; + case NSVG_CAP_SQUARE: + strokeProperties.startCap = strokeProperties.endCap = D2D1_CAP_STYLE_SQUARE; + break; + default: + break; } - switch (shape->strokeLineJoin) { - case NSVG_JOIN_BEVEL: - //CGContextSetLineJoin(context, kCGLineJoinBevel); - strokeProperties.lineJoin = D2D1_LINE_JOIN_BEVEL; - break; - case NSVG_JOIN_MITER: - //CGContextSetLineCap(context, kCGLineJoinMiter); - strokeProperties.lineJoin = D2D1_LINE_JOIN_MITER; - break; - case NSVG_JOIN_ROUND: - //CGContextSetLineCap(context, kCGLineJoinRound); - strokeProperties.lineJoin = D2D1_LINE_JOIN_ROUND; - break; - default: - break; + + switch (shape->strokeLineJoin) + { + case NSVG_JOIN_BEVEL: + strokeProperties.lineJoin = D2D1_LINE_JOIN_BEVEL; + break; + case NSVG_JOIN_MITER: + strokeProperties.lineJoin = D2D1_LINE_JOIN_MITER; + break; + case NSVG_JOIN_ROUND: + strokeProperties.lineJoin = D2D1_LINE_JOIN_ROUND; + break; + default: + break; } ComPtr strokeStyle; ReturnIfFailed(result, m_d2dFactory->CreateStrokeStyle(strokeProperties, NULL, 0, &strokeStyle)); - for (NSVGpath* path = shape->paths; path != NULL; path = path->next) { - //CGContextBeginPath(context); - //CGContextMoveToPoint(context, path->pts[0], path->pts[1]); - sink->BeginFigure(D2D1::Point2F(path->pts[0], path->pts[1]), D2D1_FIGURE_BEGIN_HOLLOW); - for (int i = 0; i < path->npts - 1; i += 3) { - float* p = &path->pts[i * 2]; - //CGContextAddCurveToPoint(context, p[2], p[3], p[4], p[5], p[6], p[7]); - sink->AddBezier(D2D1::BezierSegment(D2D1::Point2F(p[2], p[3]), D2D1::Point2F(p[4], p[5]), D2D1::Point2F(p[6], p[7]))); - } - - //if (path->closed) { - // CGContextClosePath(context); - //} - sink->EndFigure(path->closed ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN); - - //CGContextStrokePath(context); - } ReturnIfFailed(result, sink->Close()); m_d2dContext->DrawGeometry(geometry.Get(), blackBrush.Get(), shape->strokeWidth); diff --git a/Unigram/Unigram.Native/VideoAnimation.cpp b/Unigram/Unigram.Native/VideoAnimation.cpp index cf30f4173e..0e29f012a4 100644 --- a/Unigram/Unigram.Native/VideoAnimation.cpp +++ b/Unigram/Unigram.Native/VideoAnimation.cpp @@ -452,7 +452,7 @@ int VideoAnimation::RenderSync(CanvasBitmap^ bitmap, bool preview) unknown->QueryInterface(IID_PPV_ARGS(&bitmapAbi)); bitmapAbi->SetPixelBytes(pixelWidth * pixelHeight * 4, (BYTE*)pixels); - delete[] pixels; + free(pixels); info->prevFrame = timestamp; info->prevDuration = (1000 * info->frame->pkt_duration * av_q2d(info->video_stream->time_base)); diff --git a/Unigram/Unigram/App.xaml.cs b/Unigram/Unigram/App.xaml.cs index fdd4d2c80c..f282bb367e 100644 --- a/Unigram/Unigram/App.xaml.cs +++ b/Unigram/Unigram/App.xaml.cs @@ -5,10 +5,9 @@ using Telegram.Td; using Telegram.Td.Api; using Unigram.Common; -using Unigram.Controls; using Unigram.Navigation; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.Services.Updates; using Unigram.Views; using Unigram.Views.Host; @@ -389,7 +388,6 @@ public override UIElement CreateRootElement(IActivatedEventArgs e) { var navigationFrame = new Frame { FlowDirection = ApiInfo.FlowDirection }; var navigationService = NavigationServiceFactory(BackButton.Ignore, ExistingContent.Include, navigationFrame, sessionId, $"Main{sessionId}", false) as NavigationService; - navigationService.SerializationService = TLSerializationService.Current; return navigationFrame; } @@ -397,7 +395,6 @@ public override UIElement CreateRootElement(IActivatedEventArgs e) { var navigationFrame = new Frame(); var navigationService = NavigationServiceFactory(BackButton.Ignore, ExistingContent.Include, navigationFrame, sessionId, $"{sessionId}", true) as NavigationService; - navigationService.SerializationService = TLSerializationService.Current; return new RootPage(navigationService) { FlowDirection = ApiInfo.FlowDirection }; } diff --git a/Unigram/Unigram/Collections/MvxObservableCollection.cs b/Unigram/Unigram/Collections/MvxObservableCollection.cs index a433b1f56e..11ef293b1b 100644 --- a/Unigram/Unigram/Collections/MvxObservableCollection.cs +++ b/Unigram/Unigram/Collections/MvxObservableCollection.cs @@ -66,6 +66,11 @@ public bool EventsAreSuppressed get { return this._suppressEvents > 0; } } + public void Dispose() + { + _suppressEvents = int.MaxValue; + } + /// /// Raises the event with the provided event data. /// @@ -74,7 +79,7 @@ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (!EventsAreSuppressed) { - InvokeOnMainThread(() => base.OnCollectionChanged(e)); + base.OnCollectionChanged(e); } } @@ -290,17 +295,6 @@ public void Reset() OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } - protected void InvokeOnMainThread(Action action) - { - action(); - //Execute.BeginOnUIThread(action); - } - - protected override void OnPropertyChanged(PropertyChangedEventArgs e) - { - InvokeOnMainThread(() => base.OnPropertyChanged(e)); - } - public virtual bool Set(ref T storage, T value, [CallerMemberName] string propertyName = null) { if (object.Equals(storage, value)) diff --git a/Unigram/Unigram/Common/AnimatedRepeaterHandler.cs b/Unigram/Unigram/Common/AnimatedRepeaterHandler.cs index fee4f4f853..b2c805363f 100644 --- a/Unigram/Unigram/Common/AnimatedRepeaterHandler.cs +++ b/Unigram/Unigram/Common/AnimatedRepeaterHandler.cs @@ -270,7 +270,7 @@ private void Play(IEnumerable<(Button Contaner, T Sticker)> items, bool auto) var presenter = new AnimationView(); presenter.AutoPlay = true; presenter.IsLoopingEnabled = true; - presenter.Source = new Uri("file:///" + data.File.Local.Path); + presenter.Source = UriEx.GetLocal(data.File.Local.Path); data.Presenter = presenter; data.Container.Children.Insert(1, presenter); diff --git a/Unigram/Unigram/Common/Emoticon.cs b/Unigram/Unigram/Common/Emoticon.cs index 6372aa586b..a826f76ac6 100644 --- a/Unigram/Unigram/Common/Emoticon.cs +++ b/Unigram/Unigram/Common/Emoticon.cs @@ -202,7 +202,9 @@ public static class Emoticon { ":-*", "\U0001F61A" }, { "B-)", "\U0001F60E" }, { ":-D", "\U0001F603" }, + { ":D", "\U0001F600" }, { ";-)", "\U0001F609" }, + { ";)", "\U0001F609" }, { ";-P", "\U0001F61C" }, { ":-p", "\U0001F60B" }, { "3(", "\U0001F614" }, diff --git a/Unigram/Unigram/Common/Extensions.cs b/Unigram/Unigram/Common/Extensions.cs index 91d197ee39..f0389dfd58 100644 --- a/Unigram/Unigram/Common/Extensions.cs +++ b/Unigram/Unigram/Common/Extensions.cs @@ -11,8 +11,8 @@ using Unigram.Controls.Messages; using Unigram.Native; using Unigram.Navigation; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Windows.ApplicationModel; using Windows.ApplicationModel.DataTransfer; using Windows.Foundation; @@ -921,4 +921,17 @@ public static WindowContext GetWindowWrapper(this INavigationService service) public static IDispatcherWrapper GetDispatcherWrapper(this INavigationService service) => service.GetWindowWrapper()?.Dispatcher; } + + public static class UriEx + { + public static Uri GetLocal(string path) + { + return new Uri("file:///" + Uri.EscapeUriString(path.Replace('\\', '/'))); + + var directory = Path.GetDirectoryName(path); + var file = Path.GetFileName(path); + + return new Uri("file:///" + directory + "\\" + Uri.EscapeUriString(file)); + } + } } diff --git a/Unigram/Unigram/Common/MessageHelper.cs b/Unigram/Unigram/Common/MessageHelper.cs index 1c1035fa9d..783c9872d6 100644 --- a/Unigram/Unigram/Common/MessageHelper.cs +++ b/Unigram/Unigram/Common/MessageHelper.cs @@ -5,8 +5,8 @@ using Unigram.Controls; using Unigram.Converters; using Unigram.Navigation; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.ViewModels; using Unigram.Views; using Unigram.Views.Host; diff --git a/Unigram/Unigram/Common/NavigationService.cs b/Unigram/Unigram/Common/NavigationService.cs index 4ca701efa4..9f20018eb1 100644 --- a/Unigram/Unigram/Common/NavigationService.cs +++ b/Unigram/Unigram/Common/NavigationService.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Threading.Tasks; using Telegram.Td.Api; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.ViewModels; using Unigram.Views; using Unigram.Views.Payments; @@ -129,19 +129,19 @@ public static void NavigateToInstant(this INavigationService service, string url } } - public static void NavigateToChat(this INavigationService service, Chat chat, long? message = null, string accessToken = null, IDictionary state = null, bool scheduled = false) + public static void NavigateToChat(this INavigationService service, Chat chat, long? message = null, string accessToken = null, IDictionary state = null, bool scheduled = false, bool force = true) { if (service is TLNavigationService serviceEx) { - serviceEx.NavigateToChat(chat, message, accessToken, state, scheduled); + serviceEx.NavigateToChat(chat, message, accessToken, state, scheduled, force); } } - public static void NavigateToChat(this INavigationService service, long chatId, long? message = null, string accessToken = null, IDictionary state = null, bool scheduled = false) + public static void NavigateToChat(this INavigationService service, long chatId, long? message = null, string accessToken = null, IDictionary state = null, bool scheduled = false, bool force = true) { if (service is TLNavigationService serviceEx) { - serviceEx.NavigateToChat(chatId, message, accessToken, state, scheduled); + serviceEx.NavigateToChat(chatId, message, accessToken, state, scheduled, force); } } @@ -229,19 +229,6 @@ public static void RemovePeerFromStack(this INavigationService service, long tar } } - public static bool IsPeerActive(this INavigationService service, long chat) - { - if (service.CurrentPageType == typeof(ChatPage)) - { - if (TryGetPeerFromParameter(service, service.CurrentPageParam, out long chatId)) - { - return chat == chatId; - } - } - - return false; - } - public static long GetPeerFromBackStack(this INavigationService service) { if (service.CurrentPageType == typeof(ChatPage)) @@ -269,16 +256,16 @@ public static long GetPeerFromBackStack(this INavigationService service) public static bool TryGetPeerFromParameter(this INavigationService service, object parameter, out long chatId) { - if (parameter is string) - { - parameter = TLSerializationService.Current.Deserialize((string)parameter); - } - if (parameter is long) { chatId = (long)parameter; return true; } + else if (parameter is string cacheKey && service.CacheKeyToChatId.TryGetValue(cacheKey, out long value)) + { + chatId = value; + return true; + } chatId = 0; return false; diff --git a/Unigram/Unigram/Common/SoundEffects.cs b/Unigram/Unigram/Common/SoundEffects.cs index 8de4d16749..5cf6078ad5 100644 --- a/Unigram/Unigram/Common/SoundEffects.cs +++ b/Unigram/Unigram/Common/SoundEffects.cs @@ -9,33 +9,38 @@ public static class SoundEffects { public static async void Play(SoundEffect effect) { - var settings = new AudioGraphSettings(AudioRenderCategory.SoundEffects); - settings.QuantumSizeSelectionMode = QuantumSizeSelectionMode.LowestLatency; - - var result = await AudioGraph.CreateAsync(settings); - if (result.Status != AudioGraphCreationStatus.Success) - { - return; - } - - var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Audio/sent.mp3")); - - var fileInputNodeResult = await result.Graph.CreateFileInputNodeAsync(file); - if (fileInputNodeResult.Status != AudioFileNodeCreationStatus.Success) + try { - return; + // This seems to fail in some conditions. + var settings = new AudioGraphSettings(AudioRenderCategory.SoundEffects); + settings.QuantumSizeSelectionMode = QuantumSizeSelectionMode.SystemDefault; + + var result = await AudioGraph.CreateAsync(settings); + if (result.Status != AudioGraphCreationStatus.Success) + { + return; + } + + var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Audio/sent.mp3")); + + var fileInputNodeResult = await result.Graph.CreateFileInputNodeAsync(file); + if (fileInputNodeResult.Status != AudioFileNodeCreationStatus.Success) + { + return; + } + + var deviceOutputNodeResult = await result.Graph.CreateDeviceOutputNodeAsync(); + if (deviceOutputNodeResult.Status != AudioDeviceNodeCreationStatus.Success) + { + return; + } + + fileInputNodeResult.FileInputNode + .AddOutgoingConnection(deviceOutputNodeResult.DeviceOutputNode); + + result.Graph.Start(); } - - var deviceOutputNodeResult = await result.Graph.CreateDeviceOutputNodeAsync(); - if (deviceOutputNodeResult.Status != AudioDeviceNodeCreationStatus.Success) - { - return; - } - - fileInputNodeResult.FileInputNode - .AddOutgoingConnection(deviceOutputNodeResult.DeviceOutputNode); - - result.Graph.Start(); + catch { } } } diff --git a/Unigram/Unigram/Common/TLNavigationService.cs b/Unigram/Unigram/Common/TLNavigationService.cs index 408f9069db..1031bd0def 100644 --- a/Unigram/Unigram/Common/TLNavigationService.cs +++ b/Unigram/Unigram/Common/TLNavigationService.cs @@ -3,9 +3,8 @@ using System.Threading.Tasks; using Telegram.Td.Api; using Unigram.Controls; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; -using Unigram.Services.ViewService; using Unigram.Views; using Unigram.Views.Popups; using Unigram.Views.Settings; @@ -23,10 +22,7 @@ public class TLNavigationService : NavigationService private readonly IProtoService _protoService; private readonly IPasscodeService _passcodeService; - private ViewLifetimeControl _walletLifetime; - private Dictionary _instantWindows = new Dictionary(); - private AppWindow _walletWindow; public TLNavigationService(IProtoService protoService, Frame frame, int session, string id) : base(frame, session, id) @@ -72,7 +68,7 @@ public async void NavigateToInstant(string url) } } - public async void NavigateToChat(Chat chat, long? message = null, string accessToken = null, IDictionary state = null, bool scheduled = false) + public async void NavigateToChat(Chat chat, long? message = null, string accessToken = null, IDictionary state = null, bool scheduled = false, bool force = true) { if (chat == null) { @@ -175,21 +171,32 @@ public async void NavigateToChat(Chat chat, long? message = null, string accessT } else { - await NavigateAsync(scheduled ? typeof(ChatScheduledPage) : typeof(ChatPage), chat.Id, state); + if (Frame.Content is ChatPage chatPage && !scheduled && !force) + { + chatPage.ViewModel.OnNavigatingFrom(null); + + chatPage.Dispose(); + chatPage.Activate(); + chatPage.ViewModel.NavigationService = this; + chatPage.ViewModel.Dispatcher = Dispatcher; + await chatPage.ViewModel.OnNavigatedToAsync(chat.Id, Windows.UI.Xaml.Navigation.NavigationMode.New, new Dictionary()); + + FrameFacade.RaiseNavigated(chat.Id); + } + else + { + Navigate(scheduled ? typeof(ChatScheduledPage) : typeof(ChatPage), chat.Id, state); + } } } } - public async void NavigateToChat(long chatId, long? message = null, string accessToken = null, IDictionary state = null, bool scheduled = false) + public async void NavigateToChat(long chatId, long? message = null, string accessToken = null, IDictionary state = null, bool scheduled = false, bool force = true) { var chat = _protoService.GetChat(chatId); if (chat == null) { - var response = await _protoService.SendAsync(new GetChat(chatId)); - if (response is Chat result) - { - chat = result; - } + chat = await _protoService.SendAsync(new GetChat(chatId)) as Chat; } if (chat == null) @@ -197,7 +204,7 @@ public async void NavigateToChat(long chatId, long? message = null, string acces return; } - NavigateToChat(chat, message, accessToken, state, scheduled); + NavigateToChat(chat, message, accessToken, state, scheduled, force); } public async void NavigateToPasscode() diff --git a/Unigram/Unigram/Common/TLRootNavigationService.cs b/Unigram/Unigram/Common/TLRootNavigationService.cs index 7b6624a697..1bdb223d23 100644 --- a/Unigram/Unigram/Common/TLRootNavigationService.cs +++ b/Unigram/Unigram/Common/TLRootNavigationService.cs @@ -1,7 +1,7 @@ using Telegram.Td.Api; using Unigram.Controls; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.ViewModels.SignIn; using Unigram.Views; using Unigram.Views.SignIn; diff --git a/Unigram/Unigram/Common/TdExtensions.cs b/Unigram/Unigram/Common/TdExtensions.cs index 1c5270d5b4..d6bc289d8b 100644 --- a/Unigram/Unigram/Common/TdExtensions.cs +++ b/Unigram/Unigram/Common/TdExtensions.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using System.Text; +using System.Threading; using Telegram.Td.Api; using Unigram.Common; using Unigram.ViewModels; @@ -884,12 +885,12 @@ public static bool IsPhoto(this WebPage webPage) } } - return false; + return webPage.Photo != null; } public static bool IsSmallPhoto(this WebPage webPage) { - if (webPage.Photo != null) + if (webPage.Photo != null && (webPage.SiteName.Length > 0 || webPage.Title.Length > 0 || webPage.Author.Length > 0 || webPage.Description?.Text.Length > 0)) { return !webPage.IsMedia(); } @@ -947,21 +948,6 @@ public static bool IsService(this Message message) return true; // Local types: case MessageChatEvent chatEvent: - if (chatEvent.IsFull) - { - switch (chatEvent.Event.Action) - { - case ChatEventMessageDeleted messageDeleted: - return messageDeleted.Message.IsService(); - case ChatEventMessageEdited messageEdited: - return messageEdited.NewMessage.IsService(); - case ChatEventMessagePinned messagePinned: - return messagePinned.Message.IsService(); - case ChatEventPollStopped pollStopped: - return pollStopped.Message.IsService(); - } - } - return true; case MessageHeaderDate headerDate: case MessageHeaderUnread headerUnread: return true; @@ -1097,14 +1083,18 @@ public static ChatPosition GetPosition(this Chat chat, ChatList chatList) return null; } + Monitor.Enter(chat); + for (int i = 0; i < chat.Positions.Count; i++) { if (chat.Positions[i].List.ListEquals(chatList)) { + Monitor.Exit(chat); return chat.Positions[i]; } } + Monitor.Exit(chat); return null; } @@ -1115,14 +1105,18 @@ public static long GetOrder(this Chat chat, ChatList chatList) return 0; } + Monitor.Enter(chat); + for (int i = 0; i < chat.Positions.Count; i++) { if (chat.Positions[i].List.ListEquals(chatList)) { + Monitor.Exit(chat); return chat.Positions[i].Order; } } + Monitor.Exit(chat); return 0; } @@ -1607,6 +1601,16 @@ public static bool CanPromoteMembers(this Supergroup supergroup) return supergroup.Status is ChatMemberStatusCreator || supergroup.Status is ChatMemberStatusAdministrator administrator && administrator.CanPromoteMembers; } + public static bool CanPromoteMembers(this BasicGroup basicGroup) + { + if (basicGroup.Status == null) + { + return false; + } + + return basicGroup.Status is ChatMemberStatusCreator; + } + public static bool CanInviteUsers(this Supergroup supergroup) { if (supergroup.Status == null) @@ -2188,13 +2192,32 @@ public NativeObject ToUnmanaged() public class MessageChatEvent : MessageContent { - public ChatEvent Event { get; set; } - public bool IsFull { get; set; } - - public MessageChatEvent(ChatEvent chatEvent, bool isFull) - { - Event = chatEvent; - IsFull = isFull; + /// + /// Action performed by the user. + /// + public ChatEventAction Action { get; set; } + + /// + /// Identifier of the user who performed the action that triggered the event. + /// + public int UserId { get; set; } + + /// + /// Point in time (Unix timestamp) when the event happened. + /// + public int Date { get; set; } + + /// + /// Chat event identifier. + /// + public long Id { get; set; } + + public MessageChatEvent(ChatEvent chatEvent) + { + Action = chatEvent.Action; + UserId = chatEvent.UserId; + Date = chatEvent.Date; + Id = chatEvent.Id; } public NativeObject ToUnmanaged() diff --git a/Unigram/Unigram/Common/UniqueList.cs b/Unigram/Unigram/Common/UniqueList.cs index 468f867bd2..c48442ac5b 100644 --- a/Unigram/Unigram/Common/UniqueList.cs +++ b/Unigram/Unigram/Common/UniqueList.cs @@ -68,6 +68,11 @@ public bool ContainsKey(TKey key) return _inner.ContainsKey(key); } + public bool TryGetValue(TKey key, out TValue value) + { + return _inner.TryGetValue(key, out value); + } + public void CopyTo(TValue[] array, int arrayIndex) { throw new NotImplementedException(); diff --git a/Unigram/Unigram/Common/WindowContext.cs b/Unigram/Unigram/Common/WindowContext.cs index 3c35b6b91c..f228f6f628 100644 --- a/Unigram/Unigram/Common/WindowContext.cs +++ b/Unigram/Unigram/Common/WindowContext.cs @@ -4,8 +4,8 @@ using Unigram.Controls; using Unigram.Native; using Unigram.Navigation; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.Views; using Unigram.Views.Popups; using Unigram.Views.SignIn; diff --git a/Unigram/Unigram/Controls/AnimationView.cs b/Unigram/Unigram/Controls/AnimationView.cs index d1d6e2523d..140e39aa98 100644 --- a/Unigram/Unigram/Controls/AnimationView.cs +++ b/Unigram/Unigram/Controls/AnimationView.cs @@ -8,6 +8,7 @@ using Unigram.Common; using Unigram.Native; using Windows.Foundation; +using Windows.Graphics.DirectX; using Windows.Storage; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -91,10 +92,13 @@ private void OnUnloaded(object sender, RoutedEventArgs e) Dispose(); - //_animation?.Dispose(); + _device = null; + + _animation?.Dispose(); _animation = null; //_bitmap?.Dispose(); + _bitmap?.Dispose(); _bitmap = null; } @@ -115,6 +119,9 @@ private void OnInvalidate(object sender, EventArgs e) _canvas?.Invalidate(); } + private static object _reusableLock = new object(); + private static byte[] _reusableBuffer; + private void OnCreateResources(CanvasControl sender, CanvasCreateResourcesEventArgs args) { args.TrackAsyncAction(Task.Run(() => @@ -127,9 +134,15 @@ private void OnCreateResources(CanvasControl sender, CanvasCreateResourcesEventA _animation = animation; - var colors = new byte[_animation.PixelWidth * _animation.PixelHeight * 4]; + lock (_reusableLock) + { + if (_reusableBuffer == null || _reusableBuffer.Length < _animation.PixelWidth * _animation.PixelHeight * 4) + { + _reusableBuffer = new byte[_animation.PixelWidth * _animation.PixelHeight * 4]; + } + } - _bitmap = CanvasBitmap.CreateFromBytes(sender, colors, _animation.PixelWidth, _animation.PixelHeight, Windows.Graphics.DirectX.DirectXPixelFormat.R8G8B8A8UIntNormalized); + _bitmap = CanvasBitmap.CreateFromBytes(sender, _reusableBuffer, _animation.PixelWidth, _animation.PixelHeight, DirectXPixelFormat.R8G8B8A8UIntNormalized); _device = sender; }).AsAsyncAction()); diff --git a/Unigram/Unigram/Controls/Cells/ChatCell.xaml.cs b/Unigram/Unigram/Controls/Cells/ChatCell.xaml.cs index 873d8fe615..127df61b99 100644 --- a/Unigram/Unigram/Controls/Cells/ChatCell.xaml.cs +++ b/Unigram/Unigram/Controls/Cells/ChatCell.xaml.cs @@ -9,8 +9,8 @@ using Unigram.Controls.Messages; using Unigram.Converters; using Unigram.Navigation; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.ViewModels.Delegates; using Windows.UI; using Windows.UI.Composition; diff --git a/Unigram/Unigram/Controls/Cells/SharedLinkCell.xaml.cs b/Unigram/Unigram/Controls/Cells/SharedLinkCell.xaml.cs index 08ee1c64e8..89cd592be5 100644 --- a/Unigram/Unigram/Controls/Cells/SharedLinkCell.xaml.cs +++ b/Unigram/Unigram/Controls/Cells/SharedLinkCell.xaml.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using Telegram.Td.Api; using Unigram.Common; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; diff --git a/Unigram/Unigram/Controls/Chats/ChatListView.cs b/Unigram/Unigram/Controls/Chats/ChatListView.cs index 1891f4df35..fe0db45e13 100644 --- a/Unigram/Unigram/Controls/Chats/ChatListView.cs +++ b/Unigram/Unigram/Controls/Chats/ChatListView.cs @@ -21,6 +21,7 @@ public class ChatListView : PaddedListView public ItemsStackPanel ItemsStack { get; private set; } private DisposableMutex _loadMoreLock = new DisposableMutex(); + private bool _loadingMore = false; public ChatListView() { @@ -56,6 +57,8 @@ private async void OnLoaded(object sender, RoutedEventArgs e) SetScrollMode(); } + _loadingMore = true; + using (await _loadMoreLock.WaitAsync()) { if (ScrollingHost.ScrollableHeight < 200 && Items.Count > 0) @@ -68,6 +71,8 @@ private async void OnLoaded(object sender, RoutedEventArgs e) await ViewModel.LoadNextSliceAsync(false, true); } } + + _loadingMore = false; } protected override void OnApplyTemplate() @@ -80,6 +85,8 @@ protected override void OnApplyTemplate() private async void Panel_SizeChanged(object sender, SizeChangedEventArgs e) { + _loadingMore = true; + using (await _loadMoreLock.WaitAsync()) { if (ScrollingHost.ScrollableHeight < 200) @@ -95,24 +102,29 @@ private async void Panel_SizeChanged(object sender, SizeChangedEventArgs e) } } } + + _loadingMore = false; } private async void ScrollingHost_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) { - if (ScrollingHost == null || ItemsStack == null || ViewModel == null) + if (ScrollingHost == null || ItemsStack == null || ViewModel == null || _loadingMore) { return; } + _loadingMore = true; + //if (ScrollingHost.VerticalOffset < 200 && ScrollingHost.ScrollableHeight > 0 && !e.IsIntermediate) //if (ItemsStack.FirstCacheIndex == 0 && !e.IsIntermediate) using (await _loadMoreLock.WaitAsync()) { - if (ItemsStack.FirstCacheIndex == 0 && !e.IsIntermediate) + if (ItemsStack.FirstCacheIndex == 0 && ViewModel.IsLastSliceLoaded != true) { await ViewModel.LoadNextSliceAsync(true); } - else if (ScrollingHost.ScrollableHeight - ScrollingHost.VerticalOffset < 200 && ScrollingHost.ScrollableHeight > 0 && !e.IsIntermediate) + //else if (ScrollingHost.ScrollableHeight - ScrollingHost.VerticalOffset < 200 && ScrollingHost.ScrollableHeight > 0) + else if (ItemsStack.LastCacheIndex == ViewModel.Items.Count - 1) { if (ViewModel.IsFirstSliceLoaded != true) { @@ -125,6 +137,8 @@ private async void ScrollingHost_ViewChanged(object sender, ScrollViewerViewChan } } } + + _loadingMore = false; } private ItemsUpdatingScrollMode? _pendingMode; diff --git a/Unigram/Unigram/Controls/Chats/ChatSearchBar.xaml b/Unigram/Unigram/Controls/Chats/ChatSearchBar.xaml index 935bf05446..79310ee8ac 100644 --- a/Unigram/Unigram/Controls/Chats/ChatSearchBar.xaml +++ b/Unigram/Unigram/Controls/Chats/ChatSearchBar.xaml @@ -208,7 +208,7 @@ - + diff --git a/Unigram/Unigram/Controls/Chats/ChatSearchBar.xaml.cs b/Unigram/Unigram/Controls/Chats/ChatSearchBar.xaml.cs index 5355c1bf3d..edfeafa3ca 100644 --- a/Unigram/Unigram/Controls/Chats/ChatSearchBar.xaml.cs +++ b/Unigram/Unigram/Controls/Chats/ChatSearchBar.xaml.cs @@ -2,6 +2,7 @@ using System.Numerics; using Telegram.Td.Api; using Unigram.Common; +using Unigram.ViewModels; using Unigram.ViewModels.Chats; using Windows.Foundation.Metadata; using Windows.UI.Core; @@ -42,6 +43,13 @@ public void Update(ChatSearchViewModel viewModel) Field.Filter = null; Field.State = ChatSearchState.Text; + if (viewModel != null) + { + SearchPrevious.Visibility = viewModel.Dialog.Type != DialogType.Normal ? Visibility.Collapsed : Visibility.Visible; + SearchNext.Visibility = viewModel.Dialog.Type != DialogType.Normal ? Visibility.Collapsed : Visibility.Visible; + ToolsPanel.Visibility = viewModel.Dialog.Type != DialogType.Normal ? Visibility.Collapsed : Visibility.Visible; + } + ShowHide(viewModel != null); } @@ -271,7 +279,7 @@ private void SetState(ChatSearchState state, User from = null, ChatSearchMediaFi ViewModel.Autocomplete = null; break; default: - ToolsPanel.Visibility = Visibility.Visible; + ToolsPanel.Visibility = ViewModel.Dialog.Type != DialogType.Normal ? Visibility.Collapsed : Visibility.Visible; ViewModel.Autocomplete = null; break; } diff --git a/Unigram/Unigram/Controls/Chats/ChatTextBox.cs b/Unigram/Unigram/Controls/Chats/ChatTextBox.cs index d5fba73d55..220b645aeb 100644 --- a/Unigram/Unigram/Controls/Chats/ChatTextBox.cs +++ b/Unigram/Unigram/Controls/Chats/ChatTextBox.cs @@ -77,93 +77,84 @@ protected override void OnPaste() private async void OnPaste(object sender, TextControlPasteEventArgs e) { - // If the user tries to paste RTF content from any TOM control (Visual Studio, Word, Wordpad, browsers) - // we have to handle the pasting operation manually to allow plaintext only. - var package = Clipboard.GetContent(); - if (package.AvailableFormats.Contains(StandardDataFormats.Bitmap)) + try { - if (e != null) + // If the user tries to paste RTF content from any TOM control (Visual Studio, Word, Wordpad, browsers) + // we have to handle the pasting operation manually to allow plaintext only. + var package = Clipboard.GetContent(); + if (package.AvailableFormats.Contains(StandardDataFormats.Bitmap) || package.AvailableFormats.Contains(StandardDataFormats.StorageItems)) { - e.Handled = true; - } - - await ViewModel.HandlePackageAsync(package); - } - //else if (package.AvailableFormats.Contains(StandardDataFormats.WebLink)) - //{ + if (e != null) + { + e.Handled = true; + } - //} - else if (package.AvailableFormats.Contains(StandardDataFormats.StorageItems)) - { - if (e != null) - { - e.Handled = true; + await ViewModel.HandlePackageAsync(package); } - - await ViewModel.HandlePackageAsync(package); - } - else if (package.AvailableFormats.Contains(StandardDataFormats.Text) && package.AvailableFormats.Contains("application/x-tl-field-tags")) - { - if (e != null) + else if (package.AvailableFormats.Contains(StandardDataFormats.Text) && package.AvailableFormats.Contains("application/x-tl-field-tags")) { - e.Handled = true; - } - - // This is our field format - var text = await package.GetTextAsync(); - var data = await package.GetDataAsync("application/x-tl-field-tags") as IRandomAccessStream; - var reader = new DataReader(data.GetInputStreamAt(0)); - var length = await reader.LoadAsync((uint)data.Size); + if (e != null) + { + e.Handled = true; + } - var count = reader.ReadInt32(); - var entities = new List(count); + // This is our field format + var text = await package.GetTextAsync(); + var data = await package.GetDataAsync("application/x-tl-field-tags") as IRandomAccessStream; + var reader = new DataReader(data.GetInputStreamAt(0)); + var length = await reader.LoadAsync((uint)data.Size); - for (int i = 0; i < count; i++) - { - var entity = new TextEntity { Offset = reader.ReadInt32(), Length = reader.ReadInt32() }; - var type = reader.ReadByte(); + var count = reader.ReadInt32(); + var entities = new List(count); - switch (type) + for (int i = 0; i < count; i++) { - case 1: - entity.Type = new TextEntityTypeBold(); - break; - case 2: - entity.Type = new TextEntityTypeItalic(); - break; - case 3: - entity.Type = new TextEntityTypePreCode(); - break; - case 4: - entity.Type = new TextEntityTypeTextUrl { Url = reader.ReadString(reader.ReadUInt32()) }; - break; - case 5: - entity.Type = new TextEntityTypeMentionName { UserId = reader.ReadInt32() }; - break; + var entity = new TextEntity { Offset = reader.ReadInt32(), Length = reader.ReadInt32() }; + var type = reader.ReadByte(); + + switch (type) + { + case 1: + entity.Type = new TextEntityTypeBold(); + break; + case 2: + entity.Type = new TextEntityTypeItalic(); + break; + case 3: + entity.Type = new TextEntityTypePreCode(); + break; + case 4: + entity.Type = new TextEntityTypeTextUrl { Url = reader.ReadString(reader.ReadUInt32()) }; + break; + case 5: + entity.Type = new TextEntityTypeMentionName { UserId = reader.ReadInt32() }; + break; + } + + entities.Add(entity); } - entities.Add(entity); + InsertText(text, entities); } - - InsertText(text, entities); - } - else if (package.AvailableFormats.Contains(StandardDataFormats.Text) && package.AvailableFormats.Contains("application/x-td-field-tags")) - { - // This is Telegram Desktop mentions format - } - else if (package.AvailableFormats.Contains(StandardDataFormats.Text) /*&& package.Contains("Rich Text Format")*/) - { - if (e != null) + else if (package.AvailableFormats.Contains(StandardDataFormats.Text) && package.AvailableFormats.Contains("application/x-td-field-tags")) { - e.Handled = true; + // This is Telegram Desktop mentions format } + else if (package.AvailableFormats.Contains(StandardDataFormats.Text) /*&& package.Contains("Rich Text Format")*/) + { + if (e != null) + { + e.Handled = true; + } - var text = await package.GetTextAsync(); - var start = Document.Selection.StartPosition; + var text = await package.GetTextAsync(); + var start = Document.Selection.StartPosition; - Document.Selection.SetText(TextSetOptions.None, text); - Document.Selection.SetRange(start + text.Length, start + text.Length); + Document.Selection.SetText(TextSetOptions.None, text); + Document.Selection.SetRange(start + text.Length, start + text.Length); + } } + catch { } } public ListView Messages { get; set; } diff --git a/Unigram/Unigram/Controls/Gallery/GalleryView.xaml.cs b/Unigram/Unigram/Controls/Gallery/GalleryView.xaml.cs index f5fe6d531a..9167548e64 100644 --- a/Unigram/Unigram/Controls/Gallery/GalleryView.xaml.cs +++ b/Unigram/Unigram/Controls/Gallery/GalleryView.xaml.cs @@ -625,7 +625,7 @@ private void Play(Grid parent, GalleryContent item, File file) } else { - _mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("file:///" + file.Local.Path)); + _mediaPlayer.Source = MediaSource.CreateFromUri(UriEx.GetLocal(file.Local.Path)); } _mediaPlayer.IsLoopingEnabled = item.IsLoop; diff --git a/Unigram/Unigram/Controls/LottieView.cs b/Unigram/Unigram/Controls/LottieView.cs index 5f4de99a65..60bc70f15e 100644 --- a/Unigram/Unigram/Controls/LottieView.cs +++ b/Unigram/Unigram/Controls/LottieView.cs @@ -144,10 +144,21 @@ private void OnInvalidate(object sender, EventArgs e) _canvas?.Invalidate(); } + private static object _reusableLock = new object(); + private static byte[] _reusableBuffer; + private void OnCreateResources(CanvasControl sender, CanvasCreateResourcesEventArgs args) { + lock (_reusableLock) + { + if (_reusableBuffer == null) + { + _reusableBuffer = new byte[256 * 256 * 4]; + } + } + _device = sender; - _bitmap = CanvasBitmap.CreateFromBytes(sender, new byte[256 * 256 * 4], 256, 256, DirectXPixelFormat.B8G8R8A8UIntNormalized); + _bitmap = CanvasBitmap.CreateFromBytes(sender, _reusableBuffer, 256, 256, DirectXPixelFormat.B8G8R8A8UIntNormalized); if (args.Reason == CanvasCreateResourcesReason.FirstTime) { @@ -324,12 +335,6 @@ private void OnSourceChanged(string newValue, string oldValue) _index = _isCachingEnabled ? 0 : _animationTotalFrame - 1; - var update = TimeSpan.FromSeconds(_animation.Duration / _animation.TotalFrame); - if (_limitFps && _animation.FrameRate >= 60) - { - update = TimeSpan.FromSeconds(update.TotalSeconds * 2); - } - //canvas.Paused = true; //canvas.ResetElapsedTime(); //canvas.TargetElapsedTime = update > TimeSpan.Zero ? update : TimeSpan.MaxValue; diff --git a/Unigram/Unigram/Controls/MasterDetailView.cs b/Unigram/Unigram/Controls/MasterDetailView.cs index 228ec21bd4..7af2448c6f 100644 --- a/Unigram/Unigram/Controls/MasterDetailView.cs +++ b/Unigram/Unigram/Controls/MasterDetailView.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.ComponentModel; using Unigram.Navigation; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.Views; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -42,7 +42,6 @@ public void Initialize(string key, Frame parent, int session) if (service == null) { service = BootStrapper.Current.NavigationServiceFactory(BootStrapper.BackButton.Ignore, BootStrapper.ExistingContent.Exclude, session, key + session, false) as NavigationService; - service.SerializationService = TLSerializationService.Current; service.Frame.DataContext = new object(); service.FrameFacade.BackRequested += OnBackRequested; } diff --git a/Unigram/Unigram/Controls/Messages/Content/PhotoContent.xaml.cs b/Unigram/Unigram/Controls/Messages/Content/PhotoContent.xaml.cs index 025b713ff5..5a1973b197 100644 --- a/Unigram/Unigram/Controls/Messages/Content/PhotoContent.xaml.cs +++ b/Unigram/Unigram/Controls/Messages/Content/PhotoContent.xaml.cs @@ -43,7 +43,7 @@ public void UpdateMessage(MessageViewModel message) var small = photo.GetSmall(); var big = photo.GetBig(); - if (small != null && !big.Photo.Local.IsDownloadingCompleted /*&& small.Photo.Id != big.Photo.Id*/) + if (small != null && !big.Photo.Local.IsDownloadingCompleted /*&& small.Photo.Id != big.Photo.Id*/ && !message.IsSecret()) { UpdateThumbnail(message, small.Photo); } @@ -156,7 +156,20 @@ public void UpdateFile(MessageViewModel message, File file) Overlay.Opacity = 0; - Texture.Source = new BitmapImage(new Uri("file:///" + file.Local.Path)); + var width = 0; + var height = 0; + + if (width > MaxWidth || height > MaxHeight) + { + double ratioX = MaxWidth / big.Width; + double ratioY = MaxHeight / big.Height; + double ratio = Math.Max(ratioX, ratioY); + + width = (int)(big.Width * ratio); + height = (int)(big.Height * ratio); + } + + Texture.Source = new BitmapImage(new Uri("file:///" + file.Local.Path)) { DecodePixelWidth = width, DecodePixelHeight = height }; } } } diff --git a/Unigram/Unigram/Controls/Messages/MessageBubble.xaml.cs b/Unigram/Unigram/Controls/Messages/MessageBubble.xaml.cs index 96dfb6e6b1..082d8520d8 100644 --- a/Unigram/Unigram/Controls/Messages/MessageBubble.xaml.cs +++ b/Unigram/Unigram/Controls/Messages/MessageBubble.xaml.cs @@ -280,33 +280,8 @@ public void UpdateMessageReply(MessageViewModel message) } } - private void MaybeUseInner(ref MessageViewModel message) - { - if (message.Content is MessageChatEvent chatEvent) - { - if (chatEvent.Event.Action is ChatEventMessageDeleted messageDeleted) - { - message = new MessageViewModel(message.ProtoService, message.PlaybackService, message.Delegate, messageDeleted.Message) { IsFirst = true, IsLast = true, IsOutgoing = false }; - } - else if (chatEvent.Event.Action is ChatEventMessageEdited messageEdited) - { - message = new MessageViewModel(message.ProtoService, message.PlaybackService, message.Delegate, messageEdited.NewMessage) { IsFirst = true, IsLast = true, IsOutgoing = false }; - } - else if (chatEvent.Event.Action is ChatEventMessagePinned messagePinned) - { - message = new MessageViewModel(message.ProtoService, message.PlaybackService, message.Delegate, messagePinned.Message) { IsFirst = true, IsLast = true, IsOutgoing = false }; - } - else if (chatEvent.Event.Action is ChatEventPollStopped pollStopped) - { - message = new MessageViewModel(message.ProtoService, message.PlaybackService, message.Delegate, pollStopped.Message) { IsFirst = true, IsLast = true, IsOutgoing = false }; - } - } - } - public void UpdateMessageHeader(MessageViewModel message) { - MaybeUseInner(ref message); - var paragraph = HeaderLabel; var admin = AdminLabel; var parent = Header; @@ -627,8 +602,6 @@ public void UpdateMessageContentOpened(MessageViewModel message) public void UpdateMessageContent(MessageViewModel message, bool padding = false) { - MaybeUseInner(ref message); - string display = null; //if (message == null || message.Media == null || message.Media is TLMessageMediaEmpty || empty) @@ -935,10 +908,10 @@ private void UpdateMessageText(MessageViewModel message) //Footer.HorizontalAlignment = adjust ? HorizontalAlignment.Left : HorizontalAlignment.Right; _placeholderVertical = adjust; - //if (adjust) - //{ - // Span.Inlines.Add(new LineBreak()); - //} + if (adjust) + { + Span.Inlines.Add(new LineBreak()); + } } private bool GetEntities(MessageViewModel message, Span span, string text, out bool adjust) @@ -1282,9 +1255,9 @@ private void OnSizeChanged(object sender, SizeChangedEventArgs e) var rect = Message.ContentEnd.GetCharacterRect(LogicalDirection.Forward); var diff = width - rect.Right; - if (diff < footerWidth || _placeholderVertical) + if (diff < footerWidth /*|| _placeholderVertical*/) { - if (Message.ActualHeight < rect.Height * 2 && width + footerWidth < _maxWidth - ContentPanel.Padding.Left - ContentPanel.Padding.Right && !_placeholderVertical) + if (Message.ActualHeight < rect.Height * 2 && width + footerWidth < _maxWidth - ContentPanel.Padding.Left - ContentPanel.Padding.Right /*&& !_placeholderVertical*/) { Message.Margin = new Thickness(0, 0, footerWidth, 0); } diff --git a/Unigram/Unigram/Controls/Messages/MessageReference.xaml.cs b/Unigram/Unigram/Controls/Messages/MessageReference.xaml.cs index f3fb5206b2..d0e87b3214 100644 --- a/Unigram/Unigram/Controls/Messages/MessageReference.xaml.cs +++ b/Unigram/Unigram/Controls/Messages/MessageReference.xaml.cs @@ -827,19 +827,6 @@ private bool SetUnsupportedMediaTemplate(MessageViewModel message, string title) return true; } - private bool SetUnsupportedTemplate(MessageViewModel message, string title) - { - Visibility = Visibility.Collapsed; - - if (ThumbRoot != null) - ThumbRoot.Visibility = Visibility.Collapsed; - - TitleLabel.Text = string.Empty; - ServiceLabel.Text = string.Empty; - MessageLabel.Text = string.Empty; - return false; - } - #endregion private string GetFromLabel(MessageViewModel message, string title) diff --git a/Unigram/Unigram/Controls/Messages/MessageService.cs b/Unigram/Unigram/Controls/Messages/MessageService.cs index c683a1bfe1..9b7b2efeb9 100644 --- a/Unigram/Unigram/Controls/Messages/MessageService.cs +++ b/Unigram/Unigram/Controls/Messages/MessageService.cs @@ -91,7 +91,7 @@ private static (string Text, IList Entities) GetEntities(MessageView return UpdateExpiredVideo(message, expiredVideo, active); // Local types: case MessageChatEvent chatEvent: - switch (chatEvent.Event.Action) + switch (chatEvent.Action) { case ChatEventSignMessagesToggled signMessagesToggled: return UpdateSignMessagesToggled(message, signMessagesToggled, active); @@ -108,13 +108,13 @@ private static (string Text, IList Entities) GetEntities(MessageView case ChatEventMessageUnpinned messageUnpinned: return UpdateMessageUnpinned(message, messageUnpinned, active); case ChatEventMessageDeleted messageDeleted: - return chatEvent.IsFull ? GetEntities(new MessageViewModel(message.ProtoService, message.PlaybackService, message.Delegate, messageDeleted.Message), active) : UpdateMessageDeleted(message, messageDeleted, active); + return UpdateMessageDeleted(message, messageDeleted, active); case ChatEventMessageEdited messageEdited: - return chatEvent.IsFull ? GetEntities(new MessageViewModel(message.ProtoService, message.PlaybackService, message.Delegate, messageEdited.NewMessage), active) : UpdateMessageEdited(message, messageEdited, active); + return UpdateMessageEdited(message, messageEdited, active); case ChatEventDescriptionChanged descriptionChanged: return UpdateDescriptionChanged(message, descriptionChanged, active); case ChatEventMessagePinned messagePinned: - return chatEvent.IsFull ? GetEntities(new MessageViewModel(message.ProtoService, message.PlaybackService, message.Delegate, messagePinned.Message), active) : UpdateMessagePinned(message, messagePinned, active); + return UpdateMessagePinned(message, messagePinned, active); case ChatEventUsernameChanged usernameChanged: return UpdateUsernameChanged(message, usernameChanged, active); case ChatEventPollStopped pollStopped: @@ -413,7 +413,7 @@ private static (string Text, IList Entities) UpdatePollStopped(Messa var fromUser = message.GetSenderUser(); - var poll = message.Content as MessagePoll; + var poll = pollStopped.Message.Content as MessagePoll; if (poll.Poll.Type is PollTypeRegular) { content = ReplaceWithLink(Strings.Resources.EventLogStopPoll, "un1", fromUser, ref entities); diff --git a/Unigram/Unigram/Controls/OverlayPage.cs b/Unigram/Unigram/Controls/OverlayPage.cs index 6672639915..227ddb62db 100644 --- a/Unigram/Unigram/Controls/OverlayPage.cs +++ b/Unigram/Unigram/Controls/OverlayPage.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Unigram.Common; using Unigram.Navigation; -using Unigram.Services.Navigation; +using Unigram.Navigation.Services; using Unigram.Services.ViewService; using Windows.ApplicationModel.Core; using Windows.Foundation; @@ -371,6 +371,8 @@ public void GoBack(NavigationTransitionInfo infoOverride = null) public int SessionId => throw new NotImplementedException(); + public IDictionary CacheKeyToChatId => throw new NotImplementedException(); + public event TypedEventHandler AfterRestoreSavedNavigation; public void ClearCache(bool removeCachedPagesInBackStack = false) @@ -393,22 +395,7 @@ public Task LoadAsync() throw new NotImplementedException(); } - public void Navigate(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) - { - throw new NotImplementedException(); - } - - public void Navigate(T key, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) where T : struct, IConvertible - { - throw new NotImplementedException(); - } - - public Task NavigateAsync(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) - { - throw new NotImplementedException(); - } - - public Task NavigateAsync(T key, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) where T : struct, IConvertible + public bool Navigate(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) { throw new NotImplementedException(); } diff --git a/Unigram/Unigram/Controls/PlaybackHeader.xaml.cs b/Unigram/Unigram/Controls/PlaybackHeader.xaml.cs index 172de6cfc0..dbdb379008 100644 --- a/Unigram/Unigram/Controls/PlaybackHeader.xaml.cs +++ b/Unigram/Unigram/Controls/PlaybackHeader.xaml.cs @@ -4,8 +4,8 @@ using Unigram.Common; using Unigram.Controls.Cells; using Unigram.Converters; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Windows.Media.Playback; using Windows.System; using Windows.UI.Xaml; diff --git a/Unigram/Unigram/Controls/ProgressBarRing.cs b/Unigram/Unigram/Controls/ProgressBarRing.cs index 6f88da4d03..3d4a89c7cc 100644 --- a/Unigram/Unigram/Controls/ProgressBarRing.cs +++ b/Unigram/Unigram/Controls/ProgressBarRing.cs @@ -109,16 +109,8 @@ private void OnApplyLegacyTemplate() _angleStoryboard.Children.Add(angleAnimation); _angleStoryboard.Completed += OnAngleStoryboardCompleted; - Loaded += (s, args) => - { - _foreverStoryboard.RepeatBehavior = RepeatBehavior.Forever; - _foreverStoryboard.Begin(); - }; - Unloaded += (s, args) => - { - _foreverStoryboard.RepeatBehavior = new RepeatBehavior(1); - _foreverStoryboard.Stop(); - }; + Loaded += OnLoaded; + Unloaded += OnUnloaded; } else { @@ -128,6 +120,18 @@ private void OnApplyLegacyTemplate() OnValueChanged(0, Value); } + private void OnLoaded(object sender, RoutedEventArgs e) + { + _foreverStoryboard.RepeatBehavior = RepeatBehavior.Forever; + _foreverStoryboard.Begin(); + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + _foreverStoryboard.RepeatBehavior = new RepeatBehavior(1); + _foreverStoryboard.Stop(); + } + private void OnForeverStoryboardCompleted(object sender, object e) { } diff --git a/Unigram/Unigram/Logs/Logger.cs b/Unigram/Unigram/Logs/Logger.cs index 1b2b81e612..26ec99da49 100644 --- a/Unigram/Unigram/Logs/Logger.cs +++ b/Unigram/Unigram/Logs/Logger.cs @@ -57,7 +57,7 @@ public static void Info(Target tag, string message, [CallerMemberName] string me [Conditional("DEBUG")] private static void Log(Target tag, LogLevel level, Type type, string message, string member, string filePath, int line) { - Logs.Log.Write(LogHelper.CreateEntryWithoutType(DateTime.Now, level, member, line, message)); + //Logs.Log.Write(LogHelper.CreateEntryWithoutType(DateTime.Now, level, member, line, message)); System.Diagnostics.Debug.WriteLine(LogHelper.CreateEntryWithoutType(DateTime.Now, level, member, line, message)); } diff --git a/Unigram/Unigram/Navigation/BootStrapper.cs b/Unigram/Unigram/Navigation/BootStrapper.cs index 73767c3ee4..e352e7ab39 100644 --- a/Unigram/Unigram/Navigation/BootStrapper.cs +++ b/Unigram/Unigram/Navigation/BootStrapper.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; using System.Threading.Tasks; using Unigram.Common; -using Unigram.Services.Navigation; +using Unigram.Navigation.Services; using Unigram.Services.ViewService; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; @@ -59,8 +59,8 @@ protected void Set(ref T storage, T value, [CallerMemberName] String property #region Debug [Conditional("DEBUG")] - static void DebugWrite(string text = null, Services.Logging.Severities severity = Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => - Services.Logging.LoggingService.WriteLine(text, severity, caller: $"BootStrapper.{caller}"); + static void DebugWrite(string text = null, Unigram.Services.Logging.Severities severity = Unigram.Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => + Unigram.Services.Logging.LoggingService.WriteLine(text, severity, caller: $"BootStrapper.{caller}"); #endregion @@ -78,7 +78,7 @@ private void Loaded() DebugWrite(); // Hook up keyboard and mouse Back handler - var keyboard = Services.Keyboard.KeyboardService.GetForCurrentView(); + var keyboard = Unigram.Services.Keyboard.KeyboardService.GetForCurrentView(); keyboard.AfterBackGesture = (key) => { DebugWrite(caller: nameof(keyboard.AfterBackGesture)); @@ -782,22 +782,6 @@ public static AdditionalKinds DetermineStartCause(IActivatedEventArgs args) } } - private object _PageKeys; - // T must be a custom Enum - public Dictionary PageKeys() - where T : struct, IConvertible - { - if (!typeof(T).GetTypeInfo().IsEnum) - { - throw new ArgumentException("T must be an enumerated type"); - } - if (_PageKeys != null && _PageKeys is Dictionary) - { - return _PageKeys as Dictionary; - } - return (_PageKeys = new Dictionary()) as Dictionary; - } - public class LifecycleLogic { public async Task AutoRestoreAsync(ILaunchActivatedEventArgs e, INavigationService nav) diff --git a/Unigram/Unigram/Navigation/Deferral.cs b/Unigram/Unigram/Navigation/Deferral.cs deleted file mode 100644 index 9ab602b84c..0000000000 --- a/Unigram/Unigram/Navigation/Deferral.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Unigram.Navigation -{ - public sealed class DeferralManager - { - int _count = 0; - TaskCompletionSource _completed = new TaskCompletionSource(); - public Deferral GetDeferral() - { - System.Threading.Interlocked.Increment(ref _count); - return new Deferral(() => - { - var count = System.Threading.Interlocked.Decrement(ref _count); - if (count == 0) _completed.SetResult(null); - }); - } - public bool IsComplete() - { - return WaitForDeferralsAsync().IsCompleted; - } - public Task WaitForDeferralsAsync() - { - if (_count == 0) return Task.CompletedTask; - return _completed.Task; - } - } - - public sealed class Deferral - { - private Action _callback; - public Deferral(Action callback) - { - _callback = callback; - } - public void Complete() - { - _callback.Invoke(); - } - } -} diff --git a/Unigram/Unigram/Navigation/DispatcherWrapper.cs b/Unigram/Unigram/Navigation/DispatcherWrapper.cs index 62904cf1f1..1f18b0b534 100644 --- a/Unigram/Unigram/Navigation/DispatcherWrapper.cs +++ b/Unigram/Unigram/Navigation/DispatcherWrapper.cs @@ -12,8 +12,8 @@ public class DispatcherWrapper : IDispatcherWrapper #region Debug [Conditional("DEBUG")] - static void DebugWrite(string text = null, Services.Logging.Severities severity = Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => - Services.Logging.LoggingService.WriteLine(text, severity, caller: $"DispatcherWrapper.{caller}"); + static void DebugWrite(string text = null, Unigram.Services.Logging.Severities severity = Unigram.Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => + Unigram.Services.Logging.LoggingService.WriteLine(text, severity, caller: $"DispatcherWrapper.{caller}"); #endregion diff --git a/Unigram/Unigram/Services/Navigation/FrameFacade.cs b/Unigram/Unigram/Navigation/Services/FrameFacade.cs similarity index 75% rename from Unigram/Unigram/Services/Navigation/FrameFacade.cs rename to Unigram/Unigram/Navigation/Services/FrameFacade.cs index 77dbf05d47..1d24de65ad 100644 --- a/Unigram/Unigram/Services/Navigation/FrameFacade.cs +++ b/Unigram/Unigram/Navigation/Services/FrameFacade.cs @@ -3,14 +3,13 @@ using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; -using Unigram.Navigation; -using Unigram.Services.Serialization; +using Unigram.Views; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Navigation; -namespace Unigram.Services.Navigation +namespace Unigram.Navigation.Services { // DOCS: https://github.com/Windows-XAML/Template10/wiki/Docs-%7C-NavigationService public class FrameFacade @@ -18,8 +17,8 @@ public class FrameFacade #region Debug [Conditional("DEBUG")] - static void DebugWrite(string text = null, Services.Logging.Severities severity = Logging.Severities.Template10, [CallerMemberName] string caller = null) => - Logging.LoggingService.WriteLine(text, severity, caller: $"{nameof(FrameFacade)}.{caller}"); + static void DebugWrite(string text = null, Unigram.Services.Logging.Severities severity = Unigram.Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => + Unigram.Services.Logging.LoggingService.WriteLine(text, severity, caller: $"{nameof(FrameFacade)}.{caller}"); #endregion @@ -27,8 +26,8 @@ internal FrameFacade(NavigationService navigationService, Frame frame, string id { NavigationService = navigationService; Frame = frame; - frame.Navigated += (s, e) => FacadeNavigatedEventHandler(s, e); - frame.Navigating += (s, e) => FacadeNavigatingCancelEventHandler(s, e); + frame.Navigated += FacadeNavigatedEventHandler; + frame.Navigating += FacadeNavigatingCancelEventHandler; // setup animations var t = new NavigationThemeTransition @@ -41,6 +40,33 @@ internal FrameFacade(NavigationService navigationService, Frame frame, string id FrameId = id; } + public void RaiseNavigated(long chatId) + { + if (Content is ChatPage) + { + NavigationService.CacheKeyToChatId[CurrentPageCacheKey] = chatId; + CurrentPageParam = chatId; + + var args = new NavigatedEventArgs + { + NavigationMode = NavigationMode.Refresh, + SourcePageType = CurrentPageType, + Parameter = CurrentPageParam, + Content = Frame.Content as Page + }; + + foreach (var handler in _navigatedEventHandlers) + { + if (handler.Target is INavigationService) + { + continue; + } + + handler(Frame, args); + } + } + } + public event EventHandler BackRequested; public void RaiseBackRequested(HandledEventArgs args) { @@ -67,9 +93,9 @@ public void RaiseForwardRequested(HandledEventArgs args) private string GetFrameStateKey() => string.Format("{0}-PageState", FrameId); - private SettingsLegacy.ISettingsService FrameStateSettingsService() + private Unigram.Services.SettingsLegacy.ISettingsService FrameStateSettingsService() { - return SettingsLegacy.SettingsService.Create(GetFrameStateKey(), true); + return Unigram.Services.SettingsLegacy.SettingsService.Create(GetFrameStateKey(), true); } public void SetFrameState(string key, string value) @@ -97,13 +123,13 @@ private string GetPageStateKey(string frameId, Type type, int backStackDepth, ob return $"{frameId}-{type}-{backStackDepth}"; } - public SettingsLegacy.ISettingsService PageStateSettingsService(Type type, int depth = 0, object parameter = null) + public Unigram.Services.SettingsLegacy.ISettingsService PageStateSettingsService(Type type, int depth = 0, object parameter = null) { var key = GetPageStateKey(FrameId, type, BackStackDepth + depth, parameter); return FrameStateSettingsService().Open(key, true); } - public SettingsLegacy.ISettingsService PageStateSettingsService(string key) + public Unigram.Services.SettingsLegacy.ISettingsService PageStateSettingsService(string key) { return FrameStateSettingsService().Open(key, true); } @@ -139,24 +165,6 @@ public bool Navigate(Type page, object parameter, NavigationTransitionInfo infoO } } - internal ISerializationService SerializationService => NavigationService.SerializationService; - - [Obsolete("Use NavigationService.NavigationState instead")] - public void SetNavigationState(string state) - { - DebugWrite($"State {state}"); - - Frame.SetNavigationState(state); - } - - [Obsolete("Use NavigationService.NavigationState instead")] - public string GetNavigationState() - { - DebugWrite(); - - return Frame.GetNavigationState(); - } - public int BackStackDepth => Frame.BackStackDepth; public bool CanGoBack => Frame.CanGoBack; @@ -261,11 +269,7 @@ public void GoForward() public object CurrentPageParam { get; internal set; } - public object GetValue(DependencyProperty dp) => Frame.GetValue(dp); - - public void SetValue(DependencyProperty dp, object value) { Frame.SetValue(dp, value); } - - public void ClearValue(DependencyProperty dp) { Frame.ClearValue(dp); } + public string CurrentPageCacheKey { get; private set; } #endregion @@ -280,14 +284,25 @@ void FacadeNavigatedEventHandler(object sender, Windows.UI.Xaml.Navigation.Navig DebugWrite(); CurrentPageType = e.SourcePageType; - CurrentPageParam = SerializationService.Deserialize(e.Parameter?.ToString()); + CurrentPageParam = e.Parameter; + CurrentPageCacheKey = null; + + if (e.SourcePageType == typeof(ChatPage) && CurrentPageParam is string cacheKey) + { + CurrentPageParam = NavigationService.CacheKeyToChatId[cacheKey]; + CurrentPageCacheKey = cacheKey; + } + var args = new NavigatedEventArgs(e, Content as Page); + if (NavigationModeHint != NavigationMode.New) args.NavigationMode = NavigationModeHint; + NavigationModeHint = NavigationMode.New; + foreach (var handler in _navigatedEventHandlers) { - handler(this, args); + handler(Frame, args); } } @@ -297,28 +312,29 @@ void FacadeNavigatedEventHandler(object sender, Windows.UI.Xaml.Navigation.Navig add { if (!_navigatingEventHandlers.Contains(value)) _navigatingEventHandlers.Add(value); } remove { if (_navigatingEventHandlers.Contains(value)) _navigatingEventHandlers.Remove(value); } } - private async void FacadeNavigatingCancelEventHandler(object sender, NavigatingCancelEventArgs e) + private void FacadeNavigatingCancelEventHandler(object sender, NavigatingCancelEventArgs e) { DebugWrite(); - object parameter = null; - try + var parameter = e.Parameter; + if (parameter is string cacheKey && e.SourcePageType == typeof(ChatPage)) { - parameter = SerializationService.Deserialize(e.Parameter?.ToString()); + parameter = NavigationService.CacheKeyToChatId[cacheKey]; } - catch (Exception ex) - { - throw new Exception("Your parameter must be serializable. If it isn't, then use SessionState.", ex); - } - var deferral = new DeferralManager(); - var args = new NavigatingEventArgs(deferral, e, Content as Page, e.SourcePageType, parameter, e.Parameter); + + var args = new NavigatingEventArgs(e, Content as Page, e.SourcePageType, parameter, e.Parameter); + if (NavigationModeHint != NavigationMode.New) args.NavigationMode = NavigationModeHint; + NavigationModeHint = NavigationMode.New; - _navigatingEventHandlers.ForEach(x => x(this, args)); - await deferral.WaitForDeferralsAsync().ConfigureAwait(false); + + foreach (var handler in _navigatingEventHandlers) + { + handler(Frame, args); + } + e.Cancel = args.Cancel; } } - } diff --git a/Unigram/Unigram/Services/Navigation/INavigable.cs b/Unigram/Unigram/Navigation/Services/INavigable.cs similarity index 83% rename from Unigram/Unigram/Services/Navigation/INavigable.cs rename to Unigram/Unigram/Navigation/Services/INavigable.cs index 3daa78e925..553c28c14e 100644 --- a/Unigram/Unigram/Services/Navigation/INavigable.cs +++ b/Unigram/Unigram/Navigation/Services/INavigable.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Unigram.Navigation; using Windows.UI.Xaml.Navigation; -namespace Unigram.Services.Navigation +namespace Unigram.Navigation.Services { // DOCS: https://github.com/Windows-XAML/Template10/wiki/Docs-%7C-NavigationService public interface INavigable { Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary state); Task OnNavigatedFromAsync(IDictionary suspensionState, bool suspending); - Task OnNavigatingFromAsync(NavigatingEventArgs args); + void OnNavigatingFrom(NavigatingEventArgs args); INavigationService NavigationService { get; set; } IDispatcherWrapper Dispatcher { get; set; } IDictionary SessionState { get; set; } diff --git a/Unigram/Unigram/Services/Navigation/INavigationService.cs b/Unigram/Unigram/Navigation/Services/INavigationService.cs similarity index 72% rename from Unigram/Unigram/Services/Navigation/INavigationService.cs rename to Unigram/Unigram/Navigation/Services/INavigationService.cs index 43b06cd818..4ac67da527 100644 --- a/Unigram/Unigram/Services/Navigation/INavigationService.cs +++ b/Unigram/Unigram/Navigation/Services/INavigationService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Unigram.Navigation; using Unigram.Services.ViewService; using Windows.ApplicationModel.Core; using Windows.Foundation; @@ -9,7 +8,7 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation; -namespace Unigram.Services.Navigation +namespace Unigram.Navigation.Services { public interface INavigationService { @@ -18,17 +17,15 @@ public interface INavigationService object Content { get; } - void Navigate(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null); - void Navigate(T key, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) where T : struct, IConvertible; - - Task NavigateAsync(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null); - Task NavigateAsync(T key, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) where T : struct, IConvertible; + bool Navigate(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null); bool CanGoBack { get; } bool CanGoForward { get; } string NavigationState { get; set; } + IDictionary CacheKeyToChatId { get; } + void Refresh(); void Refresh(object param); diff --git a/Unigram/Unigram/Services/Navigation/JournalEntry.cs b/Unigram/Unigram/Navigation/Services/JournalEntry.cs similarity index 100% rename from Unigram/Unigram/Services/Navigation/JournalEntry.cs rename to Unigram/Unigram/Navigation/Services/JournalEntry.cs diff --git a/Unigram/Unigram/Services/Navigation/NavigatedEventArgs.cs b/Unigram/Unigram/Navigation/Services/NavigatedEventArgs.cs similarity index 73% rename from Unigram/Unigram/Services/Navigation/NavigatedEventArgs.cs rename to Unigram/Unigram/Navigation/Services/NavigatedEventArgs.cs index 87a0c1a2fd..52e728814b 100644 --- a/Unigram/Unigram/Services/Navigation/NavigatedEventArgs.cs +++ b/Unigram/Unigram/Navigation/Services/NavigatedEventArgs.cs @@ -2,7 +2,7 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; -namespace Unigram.Services.Navigation +namespace Unigram.Navigation.Services { // DOCS: https://github.com/Windows-XAML/Template10/wiki/Docs-%7C-NavigationService public class NavigatedEventArgs : EventArgs @@ -10,15 +10,15 @@ public class NavigatedEventArgs : EventArgs public NavigatedEventArgs() { } public NavigatedEventArgs(NavigationEventArgs e, Page page) { - Page = page; - PageType = e.SourcePageType; + Content = page; + SourcePageType = e.SourcePageType; Parameter = e.Parameter; NavigationMode = e.NavigationMode; } public NavigationMode NavigationMode { get; set; } - public Type PageType { get; set; } + public Type SourcePageType { get; set; } public object Parameter { get; set; } - public Page Page { get; set; } + public Page Content { get; set; } } } diff --git a/Unigram/Unigram/Services/Navigation/NavigatingEventArgs.cs b/Unigram/Unigram/Navigation/Services/NavigatingEventArgs.cs similarity index 57% rename from Unigram/Unigram/Services/Navigation/NavigatingEventArgs.cs rename to Unigram/Unigram/Navigation/Services/NavigatingEventArgs.cs index 7c5ed776aa..6230326888 100644 --- a/Unigram/Unigram/Services/Navigation/NavigatingEventArgs.cs +++ b/Unigram/Unigram/Navigation/Services/NavigatingEventArgs.cs @@ -1,26 +1,22 @@ using System; -using Unigram.Navigation; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; -namespace Unigram.Services.Navigation +namespace Unigram.Navigation.Services { // DOCS: https://github.com/Windows-XAML/Template10/wiki/Docs-%7C-NavigationService public class NavigatingEventArgs : NavigatedEventArgs { - DeferralManager Manager; - public Deferral GetDeferral() => Manager.GetDeferral(); - - public NavigatingEventArgs(DeferralManager manager) : base() + public NavigatingEventArgs() { - Manager = manager; + } - public NavigatingEventArgs(DeferralManager manager, NavigatingCancelEventArgs e, Page page, Type targetPageType, object parameter, object targetPageParameter) : this(manager) + public NavigatingEventArgs(NavigatingCancelEventArgs e, Page page, Type targetPageType, object parameter, object targetPageParameter) { NavigationMode = e.NavigationMode; - PageType = e.SourcePageType; - Page = page; + SourcePageType = e.SourcePageType; + Content = page; Parameter = parameter; TargetPageType = targetPageType; TargetPageParameter = targetPageParameter; diff --git a/Unigram/Unigram/Services/Navigation/NavigationService.cs b/Unigram/Unigram/Navigation/Services/NavigationService.cs similarity index 54% rename from Unigram/Unigram/Services/Navigation/NavigationService.cs rename to Unigram/Unigram/Navigation/Services/NavigationService.cs index 25b48e570a..d4387ac74a 100644 --- a/Unigram/Unigram/Services/Navigation/NavigationService.cs +++ b/Unigram/Unigram/Navigation/Services/NavigationService.cs @@ -5,9 +5,8 @@ using System.Runtime.CompilerServices; using System.Threading.Tasks; using Unigram.Common; -using Unigram.Navigation; -using Unigram.Services.Serialization; using Unigram.Services.ViewService; +using Unigram.Views; using Windows.ApplicationModel.Core; using Windows.Foundation; using Windows.UI.ViewManagement; @@ -16,14 +15,13 @@ using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Navigation; -namespace Unigram.Services.Navigation +namespace Unigram.Navigation.Services { // DOCS: https://github.com/Windows-XAML/Template10/wiki/Docs-%7C-NavigationService public partial class NavigationService : INavigationService { - private readonly IViewService viewService = new ViewService.ViewService(); - FrameFacade FrameFacadeInternal { get; } - public FrameFacade FrameFacade => FrameFacadeInternal; + private readonly IViewService viewService = new ViewService(); + public FrameFacade FrameFacade { get; } public bool IsInMainView { get; } public Frame Frame => FrameFacade.Frame; public object Content => Frame.Content; @@ -39,11 +37,13 @@ public string NavigationState public int SessionId { get; private set; } + public IDictionary CacheKeyToChatId { get; } = new Dictionary(); + #region Debug [Conditional("DEBUG")] - static void DebugWrite(string text = null, Services.Logging.Severities severity = Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => - Services.Logging.LoggingService.WriteLine(text, severity, caller: $"NavigationService.{caller}"); + static void DebugWrite(string text = null, Unigram.Services.Logging.Severities severity = Unigram.Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => + Unigram.Services.Logging.LoggingService.WriteLine(text, severity, caller: $"NavigationService.{caller}"); #endregion @@ -52,16 +52,15 @@ public string NavigationState public NavigationService(Frame frame, int session, string id) { - SerializationService = Services.Serialization.SerializationService.Json; IsInMainView = CoreApplication.MainView == CoreApplication.GetCurrentView(); SessionId = session; - FrameFacadeInternal = new FrameFacade(this, frame, id); - FrameFacadeInternal.Navigating += async (s, e) => + FrameFacade = new FrameFacade(this, frame, id); + FrameFacade.Navigating += async (s, e) => { if (e.Suspending) return; - var page = FrameFacadeInternal.Content as Page; + var page = FrameFacade.Content as Page; if (page != null) { // call navagable override (navigating) @@ -69,7 +68,7 @@ public NavigationService(Frame frame, int session, string id) if (dataContext != null) { // allow the viewmodel to cancel navigation - e.Cancel = !(await NavigatingFromAsync(page, e.PageType, e.Parameter, dataContext, false, e.NavigationMode)); + e.Cancel = !NavigatingFrom(page, e.SourcePageType, e.Parameter, dataContext, false, e.NavigationMode); if (!e.Cancel) { await NavigateFromAsync(page, dataContext, false).ConfigureAwait(false); @@ -82,16 +81,21 @@ public NavigationService(Frame frame, int session, string id) } } }; - FrameFacadeInternal.Navigated += async (s, e) => + FrameFacade.Navigated += async (s, e) => { - var parameter = SerializationService.Deserialize(e.Parameter?.ToString()); - var currentContent = FrameFacadeInternal.Frame.Content; + var parameter = e.Parameter; + if (parameter is string cacheKey && e.SourcePageType == typeof(ChatPage)) + { + parameter = CacheKeyToChatId[cacheKey]; + } + + var currentContent = FrameFacade.Frame.Content; //await this.GetDispatcherWrapper().DispatchAsync(async () => //{ try { - if (currentContent == FrameFacadeInternal.Frame.Content) - await NavigateToAsync(e.NavigationMode, parameter, FrameFacadeInternal.Frame.Content).ConfigureAwait(false); + if (currentContent == FrameFacade.Frame.Content) + await NavigateToAsync(e.NavigationMode, parameter, FrameFacade.Frame.Content).ConfigureAwait(false); } catch (Exception ex) { @@ -118,7 +122,7 @@ private INavigable ResolveForPage(Page page) } // before navigate (cancellable) - async Task NavigatingFromAsync(Page page, Type targetPageType, object targetPageParameter, INavigable dataContext, bool suspending, NavigationMode mode) + bool NavigatingFrom(Page page, Type targetPageType, object targetPageParameter, INavigable dataContext, bool suspending, NavigationMode mode) { DebugWrite($"Suspending: {suspending}"); @@ -126,18 +130,16 @@ async Task NavigatingFromAsync(Page page, Type targetPageType, object targ dataContext.Dispatcher = this.GetDispatcherWrapper(); dataContext.SessionState = BootStrapper.Current.SessionState; - var deferral = new DeferralManager(); - var args = new NavigatingEventArgs(deferral) + var args = new NavigatingEventArgs { NavigationMode = mode, - PageType = FrameFacadeInternal.CurrentPageType, - Parameter = FrameFacadeInternal.CurrentPageParam, + SourcePageType = FrameFacade.CurrentPageType, + Parameter = FrameFacade.CurrentPageParam, Suspending = suspending, TargetPageType = targetPageType, TargetPageParameter = targetPageParameter }; - await deferral.WaitForDeferralsAsync(); - await dataContext.OnNavigatingFromAsync(args).ConfigureAwait(false); + dataContext.OnNavigatingFrom(args); return !args.Cancel; } @@ -150,7 +152,7 @@ async Task NavigateFromAsync(Page page, INavigable dataContext, bool suspending) dataContext.Dispatcher = this.GetDispatcherWrapper(); dataContext.SessionState = BootStrapper.Current.SessionState; - var pageState = FrameFacadeInternal.PageStateSettingsService(page.GetType()).Values; + var pageState = FrameFacade.PageStateSettingsService(page.GetType()).Values; await dataContext.OnNavigatedFromAsync(pageState, suspending).ConfigureAwait(false); } @@ -158,13 +160,15 @@ async Task NavigateToAsync(NavigationMode mode, object parameter, object frameCo { DebugWrite($"Mode: {mode}, Parameter: {parameter} FrameContent: {frameContent}"); - frameContent = frameContent ?? FrameFacadeInternal.Frame.Content; + frameContent = frameContent ?? FrameFacade.Frame.Content; var page = frameContent as Page; if (page != null) { + var cleaned = false; if (page is IActivablePage cleanup) { + cleaned = true; cleanup.Activate(); } @@ -181,12 +185,12 @@ async Task NavigateToAsync(NavigationMode mode, object parameter, object frameCo dataContext.NavigationService = this; dataContext.Dispatcher = this.GetDispatcherWrapper(); dataContext.SessionState = BootStrapper.Current.SessionState; - var pageState = FrameFacadeInternal.PageStateSettingsService(page.GetType(), parameter: parameter).Values; + var pageState = FrameFacade.PageStateSettingsService(page.GetType(), parameter: parameter).Values; await dataContext.OnNavigatedToAsync(parameter, mode, pageState); // update bindings after NavTo initializes data //XamlUtils.InitializeBindings(page); - if (page.Content is UserControl pageWith) + if (page.Content is UserControl pageWith && !cleaned) { XamlUtils.UpdateBindings(pageWith); } @@ -210,7 +214,7 @@ public Task OpenAsync(Func content, object param return viewService.OpenAsync(content, parameter); } - public async Task NavigateAsync(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) + public bool Navigate(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) { DebugWrite($"Page: {page}, Parameter: {parameter}, NavigationTransitionInfo: {infoOverride}"); @@ -227,103 +231,39 @@ public async Task NavigateAsync(Type page, object parameter = null, IDicti if (state != null) { - var pageState = FrameFacadeInternal.PageStateSettingsService(page, 1, parameter).Values; + var pageState = FrameFacade.PageStateSettingsService(page, 1, parameter).Values; foreach (var item in state) { pageState[item.Key] = item.Value; } } - //var frameContent = FrameFacadeInternal.Frame.Content; - //if (frameContent is Page current && frameContent is IDisposable disposable && page.FullName == CurrentPageType?.FullName) - //{ - // if (current.DataContext is INavigable dataContext) - // { - // await NavigatingFromAsync(current, page, parameter, dataContext, false, NavigationMode.Refresh); - // await NavigateFromAsync(current, dataContext, false); - // } - - // await NavigateToAsync(NavigationMode.Refresh, parameter, current); - - // FrameFacade.CurrentPageParam = parameter; - // FrameFacade.Frame.BackStack.Add(new PageStackEntry(page, SerializationService.Serialize(CurrentPageParam), infoOverride)); - // return true; - //} - - parameter = SerializationService.Serialize(parameter); - - return FrameFacadeInternal.Navigate(page, parameter, infoOverride); - } - - public void Navigate(Type page, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) - { - DebugWrite($"Page: {page}, Parameter: {parameter}, NavigationTransitionInfo: {infoOverride}"); - - NavigateAsync(page, parameter, state, infoOverride).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - /// - /// Navigate allows developers to navigate using a - /// page key instead of the view type.This is accomplished by - /// creating a custom Enum and setting up the PageKeys dict - /// with the Key/Type pairs for your views.The dict is - /// shared by all NavigationServices and is stored in - /// the BootStrapper (or Application) of the app. - /// - /// Implementation example: - /// - /// // define your Enum - /// public Enum Pages { MainPage, DetailPage } - /// - /// // setup the keys dict - /// var keys = BootStrapper.PageKeys(); - /// keys.Add(Pages.MainPage, typeof(Views.MainPage)); - /// keys.Add(Pages.DetailPage, typeof(Views.DetailPage)); - /// - /// // use Navigate() - /// NavigationService.Navigate(Pages.MainPage); - /// - /// T must be the same custom Enum used with BootStrapper.PageKeys() - public async Task NavigateAsync(T key, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) - where T : struct, IConvertible - { - DebugWrite($"Key: {key}, Parameter: {parameter}, NavigationTransitionInfo: {infoOverride}"); - - var keys = BootStrapper.Current.PageKeys(); - - if (!keys.ContainsKey(key)) - throw new KeyNotFoundException(key.ToString()); - - var page = keys[key]; - - return await NavigateAsync(page, parameter, state, infoOverride).ConfigureAwait(false); - } + if (page == typeof(ChatPage)) + { + var cacheKey = Guid.NewGuid().ToString(); + var chatId = (long)parameter; - public void Navigate(T key, object parameter = null, IDictionary state = null, NavigationTransitionInfo infoOverride = null) - where T : struct, IConvertible - { - DebugWrite($"Key: {key}, Parameter: {parameter}, NavigationTransitionInfo: {infoOverride}"); + parameter = cacheKey; + CacheKeyToChatId[cacheKey] = chatId; + } - NavigateAsync(key, parameter, state, infoOverride).ConfigureAwait(false).GetAwaiter().GetResult(); + return FrameFacade.Navigate(page, parameter, infoOverride); } - public ISerializationService SerializationService { get; set; } - public event EventHandler> BeforeSavingNavigation; - public async Task SaveAsync() { - DebugWrite($"Frame: {FrameFacadeInternal.FrameId}"); + DebugWrite($"Frame: {FrameFacade.FrameId}"); if (CurrentPageType == null) return; - var args = new CancelEventArgs(FrameFacadeInternal.CurrentPageType); + var args = new CancelEventArgs(FrameFacade.CurrentPageType); BeforeSavingNavigation?.Invoke(this, args); if (args.Cancel) return; - var state = FrameFacadeInternal.PageStateSettingsService(GetType().ToString()); + var state = FrameFacade.PageStateSettingsService(GetType().ToString()); if (state == null) { throw new InvalidOperationException("State container is unexpectedly null"); @@ -331,95 +271,83 @@ public async Task SaveAsync() state.Write("CurrentPageType", CurrentPageType.AssemblyQualifiedName); state.Write("CurrentPageParam", CurrentPageParam); - state.Write("NavigateState", FrameFacadeInternal?.NavigationService.NavigationState); + state.Write("NavigateState", FrameFacade?.NavigationService.NavigationState); await Task.CompletedTask; } - [Obsolete("SaveNavigationAsync() is obsolete - please use SaveAsync() instead.")] - public async Task SaveNavigationAsync() - { - await SaveAsync(); - } - public event TypedEventHandler AfterRestoreSavedNavigation; public async Task LoadAsync() { - DebugWrite($"Frame: {FrameFacadeInternal.FrameId}"); + DebugWrite($"Frame: {FrameFacade.FrameId}"); try { - var state = FrameFacadeInternal.PageStateSettingsService(GetType().ToString()); + var state = FrameFacade.PageStateSettingsService(GetType().ToString()); if (state == null || !state.Exists("CurrentPageType")) { return false; } - FrameFacadeInternal.CurrentPageType = Type.GetType(state.Read("CurrentPageType")); - FrameFacadeInternal.CurrentPageParam = state.Read("CurrentPageParam"); - FrameFacadeInternal.NavigationService.NavigationState = state.Read("NavigateState"); + FrameFacade.CurrentPageType = Type.GetType(state.Read("CurrentPageType")); + FrameFacade.CurrentPageParam = state.Read("CurrentPageParam"); + FrameFacade.NavigationService.NavigationState = state.Read("NavigateState"); - await NavigateToAsync(NavigationMode.Refresh, FrameFacadeInternal.CurrentPageParam); - while (FrameFacadeInternal.Frame.Content == null) + await NavigateToAsync(NavigationMode.Refresh, FrameFacade.CurrentPageParam); + while (FrameFacade.Frame.Content == null) { await Task.Delay(1); } - AfterRestoreSavedNavigation?.Invoke(this, FrameFacadeInternal.CurrentPageType); + AfterRestoreSavedNavigation?.Invoke(this, FrameFacade.CurrentPageType); return true; } catch { return false; } } - [Obsolete("RestoreSavedNavigationAsync is obsolete - please use LoadAsync() instead.")] - public async Task RestoreSavedNavigationAsync() - { - return await LoadAsync(); - } - - public void Refresh() { FrameFacadeInternal.Refresh(); } - public void Refresh(object param) { FrameFacadeInternal.Refresh(param); } + public void Refresh() { FrameFacade.Refresh(); } + public void Refresh(object param) { FrameFacade.Refresh(param); } public void GoBack(NavigationTransitionInfo infoOverride = null) { - if (FrameFacadeInternal.CanGoBack) FrameFacadeInternal.GoBack(infoOverride); + if (FrameFacade.CanGoBack) FrameFacade.GoBack(infoOverride); } - public bool CanGoBack => FrameFacadeInternal.CanGoBack; + public bool CanGoBack => FrameFacade.CanGoBack; - public void GoForward() { FrameFacadeInternal.GoForward(); } + public void GoForward() { FrameFacade.GoForward(); } - public bool CanGoForward => FrameFacadeInternal.CanGoForward; + public bool CanGoForward => FrameFacade.CanGoForward; public void ClearCache(bool removeCachedPagesInBackStack = false) { - DebugWrite($"Frame: {FrameFacadeInternal.FrameId}"); + DebugWrite($"Frame: {FrameFacade.FrameId}"); - int currentSize = FrameFacadeInternal.Frame.CacheSize; + int currentSize = FrameFacade.Frame.CacheSize; if (removeCachedPagesInBackStack) { - FrameFacadeInternal.Frame.CacheSize = 0; + FrameFacade.Frame.CacheSize = 0; } else { - if (FrameFacadeInternal.Frame.BackStackDepth == 0) - FrameFacadeInternal.Frame.CacheSize = 1; + if (FrameFacade.Frame.BackStackDepth == 0) + FrameFacade.Frame.CacheSize = 1; else - FrameFacadeInternal.Frame.CacheSize = FrameFacadeInternal.Frame.BackStackDepth; + FrameFacade.Frame.CacheSize = FrameFacade.Frame.BackStackDepth; } - FrameFacadeInternal.Frame.CacheSize = currentSize; + FrameFacade.Frame.CacheSize = currentSize; } - public void ClearHistory() { FrameFacadeInternal.Frame.BackStack.Clear(); } + public void ClearHistory() { FrameFacade.Frame.BackStack.Clear(); } public async void Resuming() { - DebugWrite($"Frame: {FrameFacadeInternal.FrameId}"); + DebugWrite($"Frame: {FrameFacade.FrameId}"); - var page = FrameFacadeInternal.Content as Page; + var page = FrameFacade.Content as Page; if (page != null) { var dataContext = ResolveForPage(page); @@ -428,8 +356,8 @@ public async void Resuming() dataContext.NavigationService = this; dataContext.Dispatcher = this.GetDispatcherWrapper(); dataContext.SessionState = BootStrapper.Current.SessionState; - var pageState = FrameFacadeInternal.PageStateSettingsService(page.GetType(), parameter: FrameFacadeInternal.CurrentPageParam).Values; - await dataContext.OnNavigatedToAsync(FrameFacadeInternal.CurrentPageParam, NavigationMode.Refresh, pageState); + var pageState = FrameFacade.PageStateSettingsService(page.GetType(), parameter: FrameFacade.CurrentPageParam).Values; + await dataContext.OnNavigatedToAsync(FrameFacade.CurrentPageParam, NavigationMode.Refresh, pageState); // update bindings after NavTo initializes data //XamlUtils.InitializeBindings(page); @@ -447,11 +375,11 @@ public async void Resuming() public async Task SuspendingAsync() { - DebugWrite($"Frame: {FrameFacadeInternal.FrameId}"); + DebugWrite($"Frame: {FrameFacade.FrameId}"); await SaveAsync(); - var page = FrameFacadeInternal.Content as Page; + var page = FrameFacade.Content as Page; if (page != null) { var dataContext = ResolveForPage(page); @@ -462,8 +390,8 @@ public async Task SuspendingAsync() } } - public Type CurrentPageType => FrameFacadeInternal.CurrentPageType; - public object CurrentPageParam => FrameFacadeInternal.CurrentPageParam; + public Type CurrentPageType => FrameFacade.CurrentPageType; + public object CurrentPageParam => FrameFacade.CurrentPageParam; } } diff --git a/Unigram/Unigram/Services/Navigation/NavigationServiceList.cs b/Unigram/Unigram/Navigation/Services/NavigationServiceList.cs similarity index 95% rename from Unigram/Unigram/Services/Navigation/NavigationServiceList.cs rename to Unigram/Unigram/Navigation/Services/NavigationServiceList.cs index 08602131e9..d0a3d5bc47 100644 --- a/Unigram/Unigram/Services/Navigation/NavigationServiceList.cs +++ b/Unigram/Unigram/Navigation/Services/NavigationServiceList.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace Unigram.Services.Navigation +namespace Unigram.Navigation.Services { public class NavigationServiceList : List { diff --git a/Unigram/Unigram/Navigation/Services/NavigationState.cs b/Unigram/Unigram/Navigation/Services/NavigationState.cs new file mode 100644 index 0000000000..38213272f4 --- /dev/null +++ b/Unigram/Unigram/Navigation/Services/NavigationState.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Unigram.Navigation.Services +{ + public class NavigationState : Dictionary + { + public static NavigationState GetChatMember(long chatId, int userId) + { + return new NavigationState { { "chatId", chatId }, { "userId", userId } }; + } + } +} diff --git a/Unigram/Unigram/Navigation/ViewModelBase.cs b/Unigram/Unigram/Navigation/ViewModelBase.cs index 6c09a8c4af..593de5648c 100644 --- a/Unigram/Unigram/Navigation/ViewModelBase.cs +++ b/Unigram/Unigram/Navigation/ViewModelBase.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using System.Collections.Generic; using System.Threading.Tasks; -using Unigram.Services.Navigation; +using Unigram.Navigation.Services; using Windows.UI.Xaml.Navigation; namespace Unigram.Navigation @@ -19,9 +19,9 @@ public virtual Task OnNavigatedFromAsync(IDictionary pageState, return Task.CompletedTask; } - public virtual Task OnNavigatingFromAsync(NavigatingEventArgs args) + public virtual void OnNavigatingFrom(NavigatingEventArgs args) { - return Task.CompletedTask; + } [JsonIgnore] diff --git a/Unigram/Unigram/Navigation/WindowContext.cs b/Unigram/Unigram/Navigation/WindowContext.cs index d3694a34cb..657376bb4e 100644 --- a/Unigram/Unigram/Navigation/WindowContext.cs +++ b/Unigram/Unigram/Navigation/WindowContext.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; -using Unigram.Services.Navigation; +using Unigram.Navigation.Services; using Windows.ApplicationModel.Core; using Windows.Foundation; using Windows.Graphics.Display; @@ -19,8 +19,8 @@ public class WindowContext #region Debug [Conditional("DEBUG")] - static void DebugWrite(string text = null, Services.Logging.Severities severity = Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => - Services.Logging.LoggingService.WriteLine(text, severity, caller: $"WindowWrapper.{caller}"); + static void DebugWrite(string text = null, Unigram.Services.Logging.Severities severity = Unigram.Services.Logging.Severities.Template10, [CallerMemberName] string caller = null) => + Unigram.Services.Logging.LoggingService.WriteLine(text, severity, caller: $"WindowWrapper.{caller}"); #endregion diff --git a/Unigram/Unigram/Package.appxmanifest b/Unigram/Unigram/Package.appxmanifest index 2283ba0338..81e0340026 100644 --- a/Unigram/Unigram/Package.appxmanifest +++ b/Unigram/Unigram/Package.appxmanifest @@ -1,6 +1,6 @@  - + Unigram - A Telegram universal experience diff --git a/Unigram/Unigram/Services/EventAggregator.cs b/Unigram/Unigram/Services/EventAggregator.cs index 3bc08b5bf3..ebbd87e7ca 100644 --- a/Unigram/Unigram/Services/EventAggregator.cs +++ b/Unigram/Unigram/Services/EventAggregator.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; -using System.Diagnostics; +using System.Collections.Immutable; using System.Linq; using System.Reflection; -using Unigram.Common; namespace Unigram.Services { @@ -30,14 +29,6 @@ public interface IHandle : IHandle /// public interface IEventAggregator { - /// - /// Gets or sets the default publication thread marshaller. - /// - /// - /// The default publication thread marshaller. - /// - Action PublicationThreadMarshaller { get; set; } - /// /// Searches the subscribed handlers to check if we have a handler for /// the message type supplied. @@ -66,14 +57,6 @@ public interface IEventAggregator /// Uses the default thread marshaller during publication. /// void Publish(object message); - void Publish(object message, object subscriber); - - /// - /// Publishes a message. - /// - /// The message instance. - /// Allows the publisher to provide a custom thread marshaller for the message publication. - void Publish(object message, object subscriber, Action marshal); } /// @@ -83,28 +66,6 @@ public class EventAggregator : IEventAggregator { readonly List handlers = new List(); - /// - /// The default thread marshaller used for publication; - /// - public static Action DefaultPublicationThreadMarshaller = action => action(); - - /// - /// Processing of handler results on publication thread. - /// - public static Action HandlerResultProcessing = (target, result) => { }; - - public static IEventAggregator Instance { get; protected set; } - - /// - /// Initializes a new instance of the class. - /// - public EventAggregator() - { - PublicationThreadMarshaller = DefaultPublicationThreadMarshaller; - - Instance = this; - } - /// /// Gets or sets the default publication thread marshaller. /// @@ -166,16 +127,10 @@ public virtual void Unsubscribe(object subscriber) } } - public static bool LogPublish { get; set; } - /// /// Publishes a message. /// /// The message instance. - /// - /// Does not marshall the the publication to any special thread by default. - /// - [DebuggerStepThrough] public virtual void Publish(object message) { if (message == null) @@ -183,81 +138,25 @@ public virtual void Publish(object message) throw new ArgumentNullException("message"); } -#if DEBUG - if (LogPublish) - { - Debug.WriteLine("Publish " + message.GetType()); - } -#endif - - Publish(message, null, PublicationThreadMarshaller); - } - - [DebuggerStepThrough] - public virtual void Publish(object message, object subscriber) - { - if (message == null) - { - throw new ArgumentNullException("message"); - } - -#if DEBUG - if (LogPublish) - { - Debug.WriteLine("Publish " + message.GetType()); - } -#endif - - Publish(message, subscriber, PublicationThreadMarshaller); - } - - /// - /// Publishes a message. - /// - /// The message instance. - /// Allows the publisher to provide a custom thread marshaller for the message publication. - public virtual void Publish(object message, object subscriber, Action marshal) - { - if (message == null) - { - throw new ArgumentNullException("message"); - } - if (marshal == null) - { - throw new ArgumentNullException("marshal"); - } - Handler[] toNotify; lock (handlers) { - var found = handlers.FirstOrDefault(x => x.Matches(subscriber)); - - if (found != null) - { - toNotify = handlers.Except(new[] { found }).ToArray(); - } - else - { - toNotify = handlers.ToArray(); - } + toNotify = handlers.ToArray(); } - marshal(() => - { - var messageType = message.GetType(); + var messageType = message.GetType(); - var dead = toNotify - .Where(handler => !handler.Handle(messageType, message)) - .ToList(); + var dead = toNotify + .Where(handler => !handler.Handle(messageType, message)) + .ToImmutableHashSet(); - if (dead.Any()) + if (dead.Any()) + { + lock (handlers) { - lock (handlers) - { - dead.Apply(x => handlers.Remove(x)); - } + handlers.RemoveAll(x => dead.Contains(x)); } - }); + } } class Handler @@ -274,28 +173,15 @@ public Handler(object handler) { reference = new WeakReference(handler); -#if WIN_RT - var handlerInfo = typeof(IHandle).GetTypeInfo(); - var interfaces = handler.GetType().GetTypeInfo().ImplementedInterfaces - .Where(x => handlerInfo.IsAssignableFrom(x.GetTypeInfo()) && x.GetTypeInfo().IsGenericType); - - foreach (var @interface in interfaces) - { - var type = @interface.GenericTypeArguments[0]; - var method = @interface.GetTypeInfo().DeclaredMethods.First(x => x.Name == "Handle"); - supportedHandlers[type] = method; - } -#else var interfaces = handler.GetType().GetInterfaces() .Where(x => typeof(IHandle).IsAssignableFrom(x) && x.IsConstructedGenericType); foreach (var @interface in interfaces) { - var type = @interface.GetGenericArguments()[0]; + var type = @interface.GenericTypeArguments[0]; var method = @interface.GetMethod("Handle"); supportedHandlers[type] = method; } -#endif } public bool Matches(object instance) @@ -311,16 +197,9 @@ public bool Handle(Type messageType, object message) return false; } - foreach (var pair in supportedHandlers) + if (supportedHandlers.TryGetValue(messageType, out MethodInfo method)) { - if (pair.Key.IsAssignableFrom(messageType)) - { - var result = pair.Value.Invoke(target, new[] { message }); - if (result != null) - { - HandlerResultProcessing(target, result); - } - } + method.Invoke(target, new[] { message }); } return true; @@ -328,7 +207,7 @@ public bool Handle(Type messageType, object message) public bool Handles(Type messageType) { - return supportedHandlers.Any(pair => pair.Key.IsAssignableFrom(messageType)); + return supportedHandlers.ContainsKey(messageType); } } } diff --git a/Unigram/Unigram/Services/ProtoService.cs b/Unigram/Unigram/Services/ProtoService.cs index ff309f3146..6bcb3f1da6 100644 --- a/Unigram/Unigram/Services/ProtoService.cs +++ b/Unigram/Unigram/Services/ProtoService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -24,6 +23,8 @@ public interface IProtoService : ICacheService void Send(Function function, Action handler = null); Task SendAsync(Function function); + Task GetFileAsync(File file); + void DownloadFile(int fileId, int priority, int offset = 0, int limit = 0, bool synchronous = false); void CancelDownloadFile(int fileId, bool onlyIfPending = false); bool IsDownloadFileCanceled(int fileId); @@ -195,7 +196,7 @@ private void Initialize(bool online = true) var parameters = new TdlibParameters { - DatabaseDirectory = Path.Combine(ApplicationData.Current.LocalFolder.Path, $"{_session}"), + DatabaseDirectory = System.IO.Path.Combine(ApplicationData.Current.LocalFolder.Path, $"{_session}"), UseSecretChats = true, UseMessageDatabase = true, ApiId = Constants.ApiId, @@ -311,7 +312,7 @@ int TuesdayDate() { InitializeDiagnostics(); - _client.Send(new SetOption("language_pack_database_path", new OptionValueString(Path.Combine(ApplicationData.Current.LocalFolder.Path, "langpack")))); + _client.Send(new SetOption("language_pack_database_path", new OptionValueString(System.IO.Path.Combine(ApplicationData.Current.LocalFolder.Path, "langpack")))); _client.Send(new SetOption("localization_target", new OptionValueString("android"))); _client.Send(new SetOption("language_pack_id", new OptionValueString(SettingsService.Current.LanguagePackId))); //_client.Send(new SetOption("online", new OptionValueBoolean(online))); @@ -326,7 +327,7 @@ int TuesdayDate() private void InitializeDiagnostics() { - Client.Execute(new SetLogStream(new LogStreamFile(Path.Combine(ApplicationData.Current.LocalFolder.Path, "tdlib_log.txt"), 100 * 1024 * 1024))); + Client.Execute(new SetLogStream(new LogStreamFile(System.IO.Path.Combine(ApplicationData.Current.LocalFolder.Path, "tdlib_log.txt"), 100 * 1024 * 1024))); Client.Execute(new SetLogVerbosityLevel(SettingsService.Current.VerbosityLevel)); var tags = Client.Execute(new GetLogTags()) as LogTags; @@ -472,6 +473,26 @@ public Task SendAsync(Function function) private ConcurrentBag _canceledDownloads = new ConcurrentBag(); + public async Task GetFileAsync(File file) + { + if (file.Local.IsDownloadingCompleted) + { + var path = System.IO.Path.GetRelativePath(ApplicationData.Current.LocalFolder.Path, file.Local.Path); + + var item = await ApplicationData.Current.LocalFolder.TryGetItemAsync(path) as StorageFile; + if (item != null) + { + return item; + } + else + { + Send(new DeleteFileW(file.Id)); + } + } + + return null; + } + public void DownloadFile(int fileId, int priority, int offset = 0, int limit = 0, bool synchronous = false) { _client.Send(new DownloadFile(fileId, priority, offset, limit, synchronous)); diff --git a/Unigram/Unigram/Services/Serialization/ISerializationService.cs b/Unigram/Unigram/Services/Serialization/ISerializationService.cs deleted file mode 100644 index 0ede7c70da..0000000000 --- a/Unigram/Unigram/Services/Serialization/ISerializationService.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Unigram.Services.Serialization -{ - public interface ISerializationService - { - /// - /// Serializes the parameter. - /// - string Serialize(object parameter); - - /// - /// Deserializes the parameter. - /// - object Deserialize(string parameter); - - /// - /// Deserializes the parameter. - /// - T Deserialize(string parameter); - - /// - /// Attempts to deserialize the parameter. - /// - bool TryDeserialize(string parameter, out T result); - } -} \ No newline at end of file diff --git a/Unigram/Unigram/Services/Serialization/JsonSerializationService.cs b/Unigram/Unigram/Services/Serialization/JsonSerializationService.cs deleted file mode 100644 index 0cae806e14..0000000000 --- a/Unigram/Unigram/Services/Serialization/JsonSerializationService.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Runtime.CompilerServices; - -namespace Unigram.Services.Serialization -{ - using System.Diagnostics; - - public sealed class JsonSerializationService : ISerializationService - { - private readonly JsonSerializerSettings settings; - - #region Debug - - [Conditional("DEBUG")] - static void DebugWrite(string text = null, Services.Logging.Severities severity = Logging.Severities.Template10, [CallerMemberName] string caller = null) => - Logging.LoggingService.WriteLine(text, severity, caller: $"{nameof(JsonSerializationService)}.{caller}"); - - #endregion - - /// - /// Initializes a new instance of the class. - /// - internal JsonSerializationService() - { - settings = new JsonSerializerSettings() - { - Formatting = Formatting.None, - TypeNameHandling = TypeNameHandling.Auto, - //TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, - TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, - PreserveReferencesHandling = PreserveReferencesHandling.All, - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - ObjectCreationHandling = ObjectCreationHandling.Auto, - ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor - }; - } - - /// - /// JSON serializer settings. - /// - public JsonSerializerSettings Settings => settings; - - /// - /// Serializes the value. - /// - public string Serialize(object value) - { - DebugWrite($"Value {value}"); - - if (value == null) - return null; - - if (value as string == string.Empty) - return string.Empty; - - // Serialize to json - var container = new Container - { - Type = value.GetType().AssemblyQualifiedName, - Data = JsonConvert.SerializeObject(value, Formatting.None, settings) - }; - return JsonConvert.SerializeObject(container); - } - - /// - /// Deserializes the value. - /// - public object Deserialize(string value) - { - DebugWrite($"Value {value}"); - - if (value == null) - return null; - - if (value == string.Empty) - return string.Empty; - - // Deserialize from json - var container = JsonConvert.DeserializeObject(value); - var type = Type.GetType(container.Type); - return JsonConvert.DeserializeObject(container.Data, type, settings); - } - - /// - /// Deserializes the value. - /// - public T Deserialize(string value) - { - object result = this.Deserialize(value); - if (result != null) - { - return (T)result; - } - return default(T); - } - - /// - /// Attempts to deserialize the value by absorbing the InvalidCastException that may occur. - /// - /// - /// True if deserialization succeeded with non-null result, otherwise False. - /// - /// - /// On success (or return True) deserialized result is copied to the 'out' variable. - /// On fail (or return False) default(T) is copied to the 'out' variable. - /// - public bool TryDeserialize(string value, out T result) - { - object r = this.Deserialize(value); - if (r == null) - { - result = default(T); - return false; - } - - try - { - result = (T)r; - return true; - } - catch - { - result = default(T); - return false; - } - } - - #region Internal Container Class - - sealed class Container - { - public string Type { get; set; } - public string Data { get; set; } - } - - #endregion - } -} \ No newline at end of file diff --git a/Unigram/Unigram/Services/Serialization/SerializationService.cs b/Unigram/Unigram/Services/Serialization/SerializationService.cs deleted file mode 100644 index ceb60ee4b3..0000000000 --- a/Unigram/Unigram/Services/Serialization/SerializationService.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Unigram.Services.Serialization -{ - public static class SerializationService - { - private static ISerializationService _json; - public static ISerializationService Json => _json ?? (_json = new JsonSerializationService()); - } -} \ No newline at end of file diff --git a/Unigram/Unigram/Services/SettingsSearchService.cs b/Unigram/Unigram/Services/SettingsSearchService.cs index db5f4c361b..752447b9bd 100644 --- a/Unigram/Unigram/Services/SettingsSearchService.cs +++ b/Unigram/Unigram/Services/SettingsSearchService.cs @@ -4,6 +4,7 @@ using Telegram.Td.Api; using Unigram.Common; using Unigram.Views; +using Unigram.Views.Folders; using Unigram.Views.Settings; using Unigram.Views.Settings.Privacy; @@ -51,7 +52,7 @@ private IEnumerable Search(string query, SettingsSearchEntr var results = new List(); //var sane = "\\b" + Regex.Escape(query).Replace(' ', '.'); - var sane = "\\b" + query.Replace(' ', '.'); + var sane = "\\b" + query.Replace(' ', '.').Replace("\\", "\\\\"); if (entry.IsValid && Regex.IsMatch(entry.Text, sane, RegexOptions.IgnoreCase)) { var clone = entry.Clone(); @@ -89,8 +90,7 @@ private async void BuildSearchIndex() BuildAppearance(), new SettingsSearchPage(null, Strings.Resources.Language, "\uE164"), new SettingsSearchPage(null, Strings.Resources.AskAQuestion, "\uED15"), - - //new SettingsSearchPage(typeof(WalletPage), Strings.Resources.Wallet, "\uD83D\uDC8E") + new SettingsSearchPage(typeof(FoldersPage), Strings.Resources.Filters, "\uF12B") }; // FAQ indexing is done asyncronously diff --git a/Unigram/Unigram/Services/SettingsService.cs b/Unigram/Unigram/Services/SettingsService.cs index 4dd8e4fbf8..997864439e 100644 --- a/Unigram/Unigram/Services/SettingsService.cs +++ b/Unigram/Unigram/Services/SettingsService.cs @@ -194,8 +194,8 @@ public SettingsService(int session) #region App version - public const ulong CurrentVersion = (4UL << 48) | (1UL << 32) | (5204UL << 16); - public const string CurrentChangelog = "• New built-in themes: Classic, Day, Dark and Night.\r\n• Preview stickers and GIFs by click and hold them.\r\n• Improved app memory management.\r\n• More than a hundred between bug fixes and improvements."; + public const ulong CurrentVersion = (4UL << 48) | (2UL << 32) | (5263UL << 16); + public const string CurrentChangelog = "• Groups and channels Recent actions section is now feature complete."; public int Session => _session; diff --git a/Unigram/Unigram/Services/TLSerializationService.cs b/Unigram/Unigram/Services/TLSerializationService.cs deleted file mode 100644 index a4d6aee33b..0000000000 --- a/Unigram/Unigram/Services/TLSerializationService.cs +++ /dev/null @@ -1,193 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using Unigram.Services.Serialization; - -namespace Unigram.Services -{ - public class TLSerializationService : ISerializationService - { - private static TLSerializationService _current; - public static TLSerializationService Current - { - get - { - if (_current == null) - _current = new TLSerializationService(); - - return _current; - } - } - - public object Deserialize(string parameter) - { - return Deserialize(parameter); - } - - public bool TryDeserialize(string parameter, out T result) - { - try - { - result = Deserialize(parameter); - return true; - } - catch { } - - result = default(T); - return false; - } - - public T Deserialize(string parameter) - { - if (string.IsNullOrEmpty(parameter)) - { - return (T)(object)null; - } - - if (parameter.StartsWith("{")) - { - var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; - settings.Converters.Add(new TdConverter()); - - var container = JsonConvert.DeserializeObject(parameter); - var type = Type.GetType(container.Type); - - return (T)JsonConvert.DeserializeObject(container.Data, type, settings); - //return SerializationService.Json.Deserialize(parameter); - } - - return default(T); - - //var length = parameter.Length; - //var bytes = new byte[length / 2]; - //for (int i = 0; i < length; i += 2) - // bytes[i / 2] = Convert.ToByte(parameter.Substring(i, 2), 16); - - //if (parameter.StartsWith("0EFFFFFF")) - //{ - // return (T)(object)bytes.Skip(4).ToArray(); - //} - - //var buffer = bytes.AsBuffer(); - //var from = TLObjectSerializer.Deserialize(buffer); - - //return (T)(object)from; - } - - public string Serialize(object parameter) - { - if (parameter == null) - { - return null; - } - - //if (parameter is ITLObject obj) - //{ - // var buffer = TLObjectSerializer.Serialize(obj); - // var array = buffer.ToArray(); - - // return BitConverter.ToString(array).Replace("-", string.Empty); - //} - - var container = new Container - { - Type = parameter.GetType().AssemblyQualifiedName, - Data = JsonConvert.SerializeObject(parameter, Formatting.None, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full }) - }; - - return JsonConvert.SerializeObject(container); - //return SerializationService.Json.Serialize(parameter); - } - - sealed class Container - { - public string Type { get; set; } - public string Data { get; set; } - } - } - - class TdConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return objectType.Namespace.StartsWith("Telegram.Td.Api") || objectType.Namespace.StartsWith("Ton.Tonlib.Api"); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.StartObject) - { - //var read = reader.Read(); - //var type = reader.ReadAsString(); - - var jobj = JObject.Load(reader); - var type = jobj["$type"].Value(); - - var obj = Activator.CreateInstance(Type.GetType(type)); - serializer.Populate(jobj.CreateReader(), obj); - - return obj; - } - - return null; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - } - - public class TdBundle : List - { - public void Add(string key, object value) - { - Add(new TdItem { Key = key, Value = value }); - } - - public bool TryGetValue(string key, out T value) - { - var result = this.FirstOrDefault(x => x.Key == key); - if (result != null) - { - value = (T)result.Value; - return true; - } - - value = default(T); - return false; - } - - public static TdBundle Deserialize(string data) - { - return JsonConvert.DeserializeObject(data, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); - } - - public string Serialize() - { - return JsonConvert.SerializeObject(this, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); - } - - - } - - public class TdItem - { - public string Key { get; set; } - public object Value { get; set; } - } - - public class ChatMemberNavigation - { - public long ChatId { get; set; } - public int UserId { get; set; } - - public ChatMemberNavigation(long chatId, int userId) - { - ChatId = chatId; - UserId = userId; - } - } -} diff --git a/Unigram/Unigram/Services/ViewService/ViewLifetimeControl.cs b/Unigram/Unigram/Services/ViewService/ViewLifetimeControl.cs index d5473370d1..1c517d98c8 100644 --- a/Unigram/Unigram/Services/ViewService/ViewLifetimeControl.cs +++ b/Unigram/Unigram/Services/ViewService/ViewLifetimeControl.cs @@ -23,7 +23,7 @@ using System; using System.Collections.Concurrent; using Unigram.Navigation; -using Unigram.Services.Navigation; +using Unigram.Navigation.Services; using Windows.UI.Core; using Windows.UI.ViewManagement; using Windows.UI.Xaml; diff --git a/Unigram/Unigram/Themes/Generic.xaml b/Unigram/Unigram/Themes/Generic.xaml index d7e1cde5db..5b477c685a 100644 --- a/Unigram/Unigram/Themes/Generic.xaml +++ b/Unigram/Unigram/Themes/Generic.xaml @@ -3467,13 +3467,10 @@ + Text="{TemplateBinding UncheckedGlyph}"/> 0 OnApplicationRun 7.3 - True + False 0 SHA256 @@ -474,8 +474,20 @@ + + + + + + + + + + + ChatEventLogPage.xaml + ChatSharedMediaPage.xaml @@ -515,7 +527,6 @@ - @@ -538,16 +549,6 @@ - - - - - - - - - - @@ -591,7 +592,7 @@ - + @@ -744,7 +745,6 @@ - @@ -1076,12 +1076,6 @@ SupergroupEditTypePage.xaml - - SupergroupEventLogPage.xaml - - - SupergroupEventLogPage.xaml - SupergroupMembersPage.xaml @@ -2031,6 +2025,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -2567,10 +2565,6 @@ Designer MSBuild:Compile - - MSBuild:Compile - Designer - Designer MSBuild:Compile diff --git a/Unigram/Unigram/ViewModelLocator.cs b/Unigram/Unigram/ViewModelLocator.cs index b01cf2a916..1ed735020b 100644 --- a/Unigram/Unigram/ViewModelLocator.cs +++ b/Unigram/Unigram/ViewModelLocator.cs @@ -172,12 +172,12 @@ public IContainer Configure(int id) builder.RegisterType(); builder.RegisterType(); //.WithParameter((a, b) => a.Name == "dispatcher", (a, b) => WindowWrapper.Current().Dispatcher); builder.RegisterType(); + builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); - builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); builder.RegisterType(); diff --git a/Unigram/Unigram/ViewModels/BackgroundViewModel.cs b/Unigram/Unigram/ViewModels/BackgroundViewModel.cs index 66bbc217b2..f12e15cc76 100644 --- a/Unigram/Unigram/ViewModels/BackgroundViewModel.cs +++ b/Unigram/Unigram/ViewModels/BackgroundViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Telegram.Td.Api; using Unigram.Collections; @@ -165,14 +166,24 @@ public bool IsMotionEnabled public BackgroundColor Color1 { get => _color1; - set => Set(ref _color1, value); + set => SetColor(ref _color1, value); } private BackgroundColor _color2; public BackgroundColor Color2 { get => _color2; - set => Set(ref _color2, value); + set => SetColor(ref _color2, value); + } + + private void SetColor(ref BackgroundColor storage, BackgroundColor value, [CallerMemberName] string propertyName = null) + { + Set(ref storage, value, propertyName); + + if (_item?.Type is BackgroundTypePattern) + { + //RaisePropertyChanged(() => Item); + } } public BackgroundFill GetFill() @@ -193,6 +204,24 @@ public BackgroundFill GetFill() return null; } + public Color GetPatternForeground() + { + if (!_color1.IsEmpty && !_color2.IsEmpty) + { + return ColorEx.GetPatternColor(ColorEx.GetAverageColor(_color1, _color2)); + } + else if (!_color1.IsEmpty) + { + return ColorEx.GetPatternColor(_color1); + } + else if (!_color2.IsEmpty) + { + return ColorEx.GetPatternColor(_color2); + } + + return Color.FromArgb(0x66, 0xFF, 0xFF, 0xFF); + } + private bool _isColor1Checked = true; public bool IsColor1Checked { diff --git a/Unigram/Unigram/ViewModels/Chats/ChatSearchViewModel.cs b/Unigram/Unigram/ViewModels/Chats/ChatSearchViewModel.cs index c97f20c831..333d083f7e 100644 --- a/Unigram/Unigram/ViewModels/Chats/ChatSearchViewModel.cs +++ b/Unigram/Unigram/ViewModels/Chats/ChatSearchViewModel.cs @@ -174,7 +174,11 @@ public SearchChatMessagesCollection Items public async void Search(string query, User from, SearchMessagesFilter filter) { - if (string.Equals(_query, query) && _from?.Id == from?.Id && _filter?.GetType() == filter?.GetType() && PreviousCanExecute()) + if (Dialog.Type == DialogType.EventLog) + { + await Dialog.LoadEventLogSliceAsync(query); + } + else if (string.Equals(_query, query) && _from?.Id == from?.Id && _filter?.GetType() == filter?.GetType() && PreviousCanExecute()) { PreviousExecute(); } diff --git a/Unigram/Unigram/ViewModels/ChatsViewModel.cs b/Unigram/Unigram/ViewModels/ChatsViewModel.cs index edabf1075b..f1798dbd03 100644 --- a/Unigram/Unigram/ViewModels/ChatsViewModel.cs +++ b/Unigram/Unigram/ViewModels/ChatsViewModel.cs @@ -8,6 +8,7 @@ using Unigram.Collections; using Unigram.Common; using Unigram.Controls; +using Unigram.Navigation.Services; using Unigram.Services; using Unigram.ViewModels.Delegates; using Unigram.Views.Folders; @@ -635,7 +636,7 @@ private async void FolderRemoveExecute((int ChatFilterId, Chat Chat) data) public RelayCommand FolderCreateCommand { get; } private void FolderCreateExecute(Chat chat) { - NavigationService.Navigate(typeof(FolderPage), state: new Dictionary { { "included_chat_id", chat.Id } }); + NavigationService.Navigate(typeof(FolderPage), state: new NavigationState { { "included_chat_id", chat.Id } }); } #endregion diff --git a/Unigram/Unigram/ViewModels/DialogEventLogViewModel.cs b/Unigram/Unigram/ViewModels/DialogEventLogViewModel.cs new file mode 100644 index 0000000000..cde35109d9 --- /dev/null +++ b/Unigram/Unigram/ViewModels/DialogEventLogViewModel.cs @@ -0,0 +1,792 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Telegram.Td.Api; +using Unigram.Common; +using Unigram.Controls; +using Unigram.Converters; +using Unigram.Services; +using Unigram.Services.Factories; +using Unigram.Views.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Unigram.ViewModels +{ + public class DialogEventLogViewModel : DialogViewModel + { + public DialogEventLogViewModel(IProtoService protoService, ICacheService cacheService, ISettingsService settingsService, IEventAggregator aggregator, ILocationService locationService, INotificationsService pushService, IPlaybackService playbackService, IVoIPService voipService, INetworkService networkService, IMessageFactory messageFactory) + : base(protoService, cacheService, settingsService, aggregator, locationService, pushService, playbackService, voipService, networkService, messageFactory) + { + HelpCommand = new RelayCommand(HelpExecute); + } + + public override DialogType Type => DialogType.EventLog; + + private long _minEventId = long.MaxValue; + + private ChatEventLogFilters _filters = new ChatEventLogFilters(true, true, true, true, true, true, true, true, true, true); + public ChatEventLogFilters Filters + { + get => _filters; + set => Set(ref _filters, value); + } + + private IList _userIds = new int[0]; + public IList UserIds + { + get => _userIds; + set => Set(ref _userIds, value); + } + + public override string Subtitle + { + get + { + if (_filters.InfoChanges && + _filters.MemberInvites && + _filters.MemberJoins && + _filters.MemberLeaves && + _filters.MemberPromotions && + _filters.MemberRestrictions && + _filters.MessageDeletions && + _filters.MessageEdits && + _filters.MessagePins && + _filters.SettingChanges && + _userIds.IsEmpty()) + { + return Strings.Resources.EventLogAllEvents; + } + + return Strings.Resources.EventLogSelectedEvents; + } + } + + protected override async void FilterExecute() + { + var chat = _chat; + if (chat == null) + { + return; + } + + var supergroup = CacheService.GetSupergroup(chat); + if (supergroup == null) + { + return; + } + + var dialog = new SupergroupEventLogFiltersPopup(); + + var confirm = await dialog.ShowAsync(ProtoService, supergroup.Id, _filters, _userIds); + if (confirm == ContentDialogResult.Primary) + { + Filters = dialog.Filters; + UserIds = dialog.UserIds; + + RaisePropertyChanged(() => Subtitle); + + await LoadEventLogSliceAsync(); + } + } + + public RelayCommand HelpCommand { get; } + private async void HelpExecute() + { + var chat = _chat; + if (chat == null) + { + return; + } + + await MessagePopup.ShowAsync(chat.Type is ChatTypeSupergroup supergroup && supergroup.IsChannel ? Strings.Resources.EventLogInfoDetailChannel : Strings.Resources.EventLogInfoDetail, Strings.Resources.EventLogInfoTitle, Strings.Resources.OK); + } + + public override async Task LoadEventLogSliceAsync(string query = "") + { + using (await _loadMoreLock.WaitAsync()) + { + var chat = _chat; + if (chat == null) + { + return; + } + + if (_isLoadingNextSlice || _isLoadingPreviousSlice) + { + return; + } + + _isLoadingNextSlice = true; + _isLoadingPreviousSlice = true; + IsLastSliceLoaded = null; + IsFirstSliceLoaded = null; + IsLoading = true; + + System.Diagnostics.Debug.WriteLine("DialogViewModel: LoadScheduledSliceAsync"); + + var response = await ProtoService.SendAsync(new GetChatEventLog(chat.Id, query, 0, 50, _filters, _userIds)); + if (response is ChatEvents events) + { + _groupedMessages.Clear(); + + if (events.Events.Count > 0) + { + SetScrollMode(ItemsUpdatingScrollMode.KeepLastItemInView, true); + Logs.Logger.Debug(Logs.Target.Chat, "Setting scroll mode to KeepLastItemInView"); + } + + var replied = ProcessEvents(events); + await ProcessMessagesAsync(chat, replied); + + var target = replied.FirstOrDefault(); + if (target != null) + { + replied.Insert(0, _messageFactory.Create(this, new Message(0, target.SenderUserId, target.ChatId, null, target.SchedulingState, target.IsOutgoing, false, false, true, false, target.IsChannelPost, false, target.Date, 0, null, 0, 0, 0, 0, string.Empty, 0, 0, string.Empty, new MessageHeaderDate(), null))); + } + + Items.ReplaceWith(replied); + + IsLastSliceLoaded = false; + IsFirstSliceLoaded = true; + } + + _isLoadingNextSlice = false; + _isLoadingPreviousSlice = false; + IsLoading = false; + } + + var already = Items.LastOrDefault(); + if (already != null) + { + var field = ListField; + if (field != null) + { + await field.ScrollToItem(already, VerticalAlignment.Bottom, false, int.MaxValue, ScrollIntoViewAlignment.Leading, true); + } + } + } + + public async override Task LoadNextSliceAsync(bool force = false, bool init = false) + { + using (await _loadMoreLock.WaitAsync()) + { + try + { + // We don't want to flood with requests when the chat gets initialized + if (init && ListField?.ScrollingHost?.ScrollableHeight >= 200) + { + return; + } + } + catch { } + + var chat = _migratedChat ?? _chat; + if (chat == null) + { + return; + } + + if (_isLoadingNextSlice || _isLoadingPreviousSlice || Items.Count < 1 || IsLastSliceLoaded == true) + { + return; + } + + _isLoadingNextSlice = true; + IsLoading = true; + + System.Diagnostics.Debug.WriteLine("DialogViewModel: LoadNextSliceAsync"); + System.Diagnostics.Debug.WriteLine("DialogViewModel: LoadNextSliceAsync: Begin request"); + + var response = await ProtoService.SendAsync(new GetChatEventLog(chat.Id, string.Empty, _minEventId, 50, _filters, _userIds)); + if (response is ChatEvents events) + { + if (events.Events.Count > 0) + { + SetScrollMode(ItemsUpdatingScrollMode.KeepLastItemInView, force); + Logs.Logger.Debug(Logs.Target.Chat, "Setting scroll mode to KeepLastItemInView"); + } + + var replied = ProcessEvents(events); + await ProcessMessagesAsync(chat, replied); + + foreach (var message in replied) + { + Items.Insert(0, message); + } + + IsLastSliceLoaded = replied.IsEmpty(); + + if (replied.IsEmpty()) + { + await AddHeaderAsync(); + } + } + + _isLoadingNextSlice = false; + IsLoading = false; + } + } + + private Message CreateMessage(long chatId, bool isChannel, ChatEvent chatEvent) + { + var userId = chatEvent.UserId; + + if (chatEvent.Action is ChatEventMessageDeleted messageDeleted) + { + userId = messageDeleted.Message.SenderUserId; + } + else if (chatEvent.Action is ChatEventMessageEdited messageEdited) + { + userId = messageEdited.NewMessage.SenderUserId; + } + else if (chatEvent.Action is ChatEventMessagePinned messagePinned) + { + userId = messagePinned.Message.SenderUserId; + } + else if (chatEvent.Action is ChatEventPollStopped pollStopped) + { + userId = pollStopped.Message.SenderUserId; + } + + return new Message(chatEvent.Id, userId, chatId, null, null, false, false, false, false, false, isChannel, false, chatEvent.Date, 0, null, 0, 0, 0.0d, 0, string.Empty, 0, 0, string.Empty, null, null); + } + + private MessageViewModel GetMessage(long chatId, bool isChannel, ChatEvent chatEvent) + { + var message = _messageFactory.Create(this, CreateMessage(chatId, isChannel, chatEvent)); + message.IsFirst = true; + message.IsLast = true; + + return message; + } + + private IList ProcessEvents(ChatEvents events) + { + var result = new List(); + var channel = _chat.Type is ChatTypeSupergroup super && super.IsChannel; + + foreach (var item in events.Events) + { + _minEventId = Math.Min(_minEventId, item.Id); + + MessageViewModel message = null; + switch (item.Action) + { + case ChatEventMemberInvited memberInvited: + message = GetMessage(_chat.Id, channel, item); + message.Content = new MessageChatAddMembers(new[] { memberInvited.UserId }); + break; + case ChatEventSlowModeDelayChanged slowModeDelayChanged: + case ChatEventPermissionsChanged permissionsChanged: + case ChatEventMemberRestricted memberRestricted: + case ChatEventMemberPromoted memberPromoted: + message = GetMessage(_chat.Id, channel, item); + //message.Content = new MessageChatEvent(item, true); + message.Content = GetMessageContent(item, channel); + break; + case ChatEventSignMessagesToggled signMessagesToggled: + case ChatEventStickerSetChanged stickerSetChanged: + case ChatEventInvitesToggled invitesToggled: + case ChatEventIsAllHistoryAvailableToggled isAllHistoryAvailableToggled: + case ChatEventMessageUnpinned messageUnpinned: + case ChatEventLinkedChatChanged linkedChatChanged: + case ChatEventLocationChanged locationChanged: + message = GetMessage(_chat.Id, channel, item); + message.Content = new MessageChatEvent(item); + break; + case ChatEventMemberLeft memberLeft: + message = GetMessage(_chat.Id, channel, item); + message.Content = new MessageChatDeleteMember(item.UserId); + break; + case ChatEventMessageDeleted messageDeleted: + case ChatEventMessageEdited messageEdited: + case ChatEventDescriptionChanged descriptionChanged: + case ChatEventMessagePinned messagePinned: + case ChatEventUsernameChanged usernameChanged: + case ChatEventPollStopped pollStopped: + message = GetMessage(_chat.Id, channel, item); + //message.Content = new MessageChatEvent(item, true); + message.Content = GetMessageContent(item, channel); + result.Add(message); + message = GetMessage(_chat.Id, channel, item); + message.Content = new MessageChatEvent(item); + break; + case ChatEventPhotoChanged photoChanged: + message = GetMessage(_chat.Id, channel, item); + if (photoChanged.NewPhoto == null) + { + message.Content = new MessageChatDeletePhoto(); + break; + } + + message.Content = new MessageChatChangePhoto(photoChanged.NewPhoto); + break; + case ChatEventMemberJoined memberJoined: + message = GetMessage(_chat.Id, channel, item); + message.Content = new MessageChatAddMembers(new int[] { item.UserId }); + break; + case ChatEventTitleChanged titleChanged: + message = GetMessage(_chat.Id, channel, item); + message.Content = new MessageChatChangeTitle(titleChanged.NewTitle); + break; + } + + if (message != null) + { + result.Add(message); + } + } + + //if (response.Result.Events.Count < 50) + //{ + // _hasMore = false; + //} + + result.Reverse(); + return result; + } + + private MessageContent GetMessageContent(ChatEvent item, bool channel) + { + if (item.Action is ChatEventDescriptionChanged descriptionChanged) + { + var text = new FormattedText(descriptionChanged.NewDescription, new TextEntity[0]); + var webPage = string.IsNullOrEmpty(descriptionChanged.OldDescription) ? null : new WebPage { SiteName = Strings.Resources.EventLogPreviousGroupDescription, Description = new FormattedText { Text = descriptionChanged.OldDescription } }; + + return new MessageText(text, webPage); + } + else if (item.Action is ChatEventUsernameChanged usernameChanged) + { + var link = string.IsNullOrEmpty(usernameChanged.NewUsername) ? string.Empty : MeUrlPrefixConverter.Convert(CacheService, usernameChanged.NewUsername); + + var text = new FormattedText(link, new[] { new TextEntity(0, link.Length, new TextEntityTypeUrl()) }); + var webPage = string.IsNullOrEmpty(usernameChanged.OldUsername) ? null : new WebPage { SiteName = Strings.Resources.EventLogPreviousLink, Description = new FormattedText { Text = MeUrlPrefixConverter.Convert(CacheService, usernameChanged.OldUsername) } }; + + return new MessageText(text, webPage); + } + else if (item.Action is ChatEventPermissionsChanged permissionChanged) + { + var text = string.Empty; + var entities = new List(); + + ChatPermissions o = permissionChanged.OldPermissions; + ChatPermissions n = permissionChanged.NewPermissions; + + if (o == null) + { + o = new ChatPermissions(); + } + if (n == null) + { + n = new ChatPermissions(); + } + + var rights = new StringBuilder(Strings.Resources.EventLogDefaultPermissions); + var added = false; + + void AppendChange(bool value, string label) + { + if (!added) + { + rights.Append('\n'); + added = true; + } + + rights.Append('\n').Append(value ? '+' : '-').Append(' '); + rights.Append(label); + } + + //if (o.IsViewMessages != n.IsViewMessages) + //{ + // AppendChange(n.IsViewMessages, Strings.Resources.EventLogRestrictedReadMessages); + //} + if (o.CanSendMessages != n.CanSendMessages) + { + AppendChange(n.CanSendMessages, Strings.Resources.EventLogRestrictedSendMessages); + } + if (o.CanSendOtherMessages != n.CanSendOtherMessages) + { + AppendChange(n.CanSendOtherMessages, Strings.Resources.EventLogRestrictedSendStickers); + } + if (o.CanSendMediaMessages != n.CanSendMediaMessages) + { + AppendChange(n.CanSendMediaMessages, Strings.Resources.EventLogRestrictedSendMedia); + } + if (o.CanSendPolls != n.CanSendPolls) + { + AppendChange(n.CanSendPolls, Strings.Resources.EventLogRestrictedSendPolls); + } + if (o.CanAddWebPagePreviews != n.CanAddWebPagePreviews) + { + AppendChange(n.CanAddWebPagePreviews, Strings.Resources.EventLogRestrictedSendEmbed); + } + if (o.CanChangeInfo != n.CanChangeInfo) + { + AppendChange(n.CanChangeInfo, Strings.Resources.EventLogRestrictedChangeInfo); + } + if (o.CanInviteUsers != n.CanInviteUsers) + { + AppendChange(n.CanInviteUsers, Strings.Resources.EventLogRestrictedSendEmbed); + } + if (o.CanPinMessages != n.CanPinMessages) + { + AppendChange(n.CanPinMessages, Strings.Resources.EventLogRestrictedPinMessages); + } + + text = rights.ToString(); + + return new MessageText(new FormattedText(text, entities), null); + } + else if (item.Action is ChatEventMemberRestricted memberRestricted) + { + var text = string.Empty; + var entities = new List(); + + var whoUser = CacheService.GetUser(memberRestricted.UserId); + ChatMemberStatusRestricted o = null; + ChatMemberStatusRestricted n = null; + + if (memberRestricted.OldStatus is ChatMemberStatusRestricted oldRestricted) + { + o = oldRestricted; + } + else if (memberRestricted.OldStatus is ChatMemberStatusBanned oldBanned) + { + o = new ChatMemberStatusRestricted(false, oldBanned.BannedUntilDate, new ChatPermissions(false, false, false, false, false, false, false, false)); + } + else if (memberRestricted.OldStatus is ChatMemberStatusMember) + { + o = new ChatMemberStatusRestricted(true, 0, new ChatPermissions(true, true, true, true, true, true, true, true)); + } + + if (memberRestricted.NewStatus is ChatMemberStatusRestricted newRestricted) + { + n = newRestricted; + } + else if (memberRestricted.NewStatus is ChatMemberStatusBanned newBanned) + { + n = new ChatMemberStatusRestricted(false, newBanned.BannedUntilDate, new ChatPermissions(false, false, false, false, false, false, false, false)); + } + else if (memberRestricted.NewStatus is ChatMemberStatusMember) + { + n = new ChatMemberStatusRestricted(true, 0, new ChatPermissions(true, true, true, true, true, true, true, true)); + } + + if (!channel && (n == null || n != null && o != null /*&& n.RestrictedUntilDate != o.RestrictedUntilDate*/)) + { + StringBuilder rights; + String bannedDuration; + if (n != null && !n.IsForever()) + { + bannedDuration = ""; + int duration = n.RestrictedUntilDate - item.Date; + int days = duration / 60 / 60 / 24; + duration -= days * 60 * 60 * 24; + int hours = duration / 60 / 60; + duration -= hours * 60 * 60; + int minutes = duration / 60; + int count = 0; + for (int a = 0; a < 3; a++) + { + String addStr = null; + if (a == 0) + { + if (days != 0) + { + addStr = Locale.Declension("Days", days); + count++; + } + } + else if (a == 1) + { + if (hours != 0) + { + addStr = Locale.Declension("Hours", hours); + count++; + } + } + else + { + if (minutes != 0) + { + addStr = Locale.Declension("Minutes", minutes); + count++; + } + } + if (addStr != null) + { + if (bannedDuration.Length > 0) + { + bannedDuration += ", "; + } + bannedDuration += addStr; + } + if (count == 2) + { + break; + } + } + } + else + { + bannedDuration = Strings.Resources.UserRestrictionsUntilForever; + } + + var str = Strings.Resources.EventLogRestrictedUntil; + rights = new StringBuilder(String.Format(str, GetUserName(whoUser, entities, str.IndexOf("{0}")), bannedDuration)); + var added = false; + if (o == null) + { + o = new ChatMemberStatusRestricted(true, 0, new ChatPermissions(true, true, true, true, true, true, true, true)); + } + if (n == null) + { + n = new ChatMemberStatusRestricted(true, 0, new ChatPermissions(true, true, true, true, true, true, true, true)); + } + + void AppendChange(bool value, string label) + { + if (!added) + { + rights.Append('\n'); + added = true; + } + + rights.Append('\n').Append(value ? '+' : '-').Append(' '); + rights.Append(label); + } + + //if (o.IsViewMessages != n.IsViewMessages) + //{ + // AppendChange(n.IsViewMessages, Strings.Resources.EventLogRestrictedReadMessages); + //} + if (o.Permissions.CanSendMessages != n.Permissions.CanSendMessages) + { + AppendChange(n.Permissions.CanSendMessages, Strings.Resources.EventLogRestrictedSendMessages); + } + if (o.Permissions.CanSendOtherMessages != n.Permissions.CanSendOtherMessages) + { + AppendChange(n.Permissions.CanSendOtherMessages, Strings.Resources.EventLogRestrictedSendStickers); + } + if (o.Permissions.CanSendMediaMessages != n.Permissions.CanSendMediaMessages) + { + AppendChange(n.Permissions.CanSendMediaMessages, Strings.Resources.EventLogRestrictedSendMedia); + } + if (o.Permissions.CanSendPolls != n.Permissions.CanSendPolls) + { + AppendChange(n.Permissions.CanSendPolls, Strings.Resources.EventLogRestrictedSendPolls); + } + if (o.Permissions.CanAddWebPagePreviews != n.Permissions.CanAddWebPagePreviews) + { + AppendChange(n.Permissions.CanAddWebPagePreviews, Strings.Resources.EventLogRestrictedSendEmbed); + } + if (o.Permissions.CanChangeInfo != n.Permissions.CanChangeInfo) + { + AppendChange(n.Permissions.CanChangeInfo, Strings.Resources.EventLogRestrictedChangeInfo); + } + if (o.Permissions.CanInviteUsers != n.Permissions.CanInviteUsers) + { + AppendChange(n.Permissions.CanInviteUsers, Strings.Resources.EventLogRestrictedSendEmbed); + } + if (o.Permissions.CanPinMessages != n.Permissions.CanPinMessages) + { + AppendChange(n.Permissions.CanPinMessages, Strings.Resources.EventLogRestrictedPinMessages); + } + + text = rights.ToString(); + } + else + { + String str; + if (o == null || memberRestricted.NewStatus is ChatMemberStatusBanned) + { + str = Strings.Resources.EventLogChannelRestricted; + } + else + { + str = Strings.Resources.EventLogChannelUnrestricted; + } + + text = String.Format(str, GetUserName(whoUser, entities, str.IndexOf("{0}"))); + } + + return new MessageText(new FormattedText(text, entities), null); + } + else if (item.Action is ChatEventMemberPromoted memberPromoted) + { + var entities = new List(); + + var whoUser = CacheService.GetUser(memberPromoted.UserId); + var str = memberPromoted.NewStatus is ChatMemberStatusCreator + ? Strings.Resources.EventLogChangedOwnership + : Strings.Resources.EventLogPromoted; + var userName = GetUserName(whoUser, entities, str.IndexOf("{0}")); + var builder = new StringBuilder(string.Format(str, userName)); + var added = false; + + if (memberPromoted.NewStatus is ChatMemberStatusCreator) + { + return new MessageText(new FormattedText(builder.ToString(), entities), null); + } + + ChatMemberStatusAdministrator o = null; + ChatMemberStatusAdministrator n = null; + + if (memberPromoted.OldStatus is ChatMemberStatusAdministrator oldAdmin) + { + o = oldAdmin; + } + if (memberPromoted.NewStatus is ChatMemberStatusAdministrator newAdmin) + { + n = newAdmin; + } + + if (o == null) + { + o = new ChatMemberStatusAdministrator(); + } + if (n == null) + { + n = new ChatMemberStatusAdministrator(); + } + + if (!string.Equals(o.CustomTitle, n.CustomTitle)) + { + if (!added) + { + builder.Append('\n'); + added = true; + } + + if (string.IsNullOrEmpty(n.CustomTitle)) + { + builder.Append('\n').Append('-').Append(' '); + builder.Append(Strings.Resources.EventLogPromotedRemovedTitle); + } + else + { + builder.Append('\n').Append('+').Append(' '); + builder.AppendFormat(Strings.Resources.EventLogPromotedTitle, n.CustomTitle); + } + } + + void AppendChange(bool value, string label) + { + if (!added) + { + builder.Append('\n'); + added = true; + } + + builder.Append('\n').Append(value ? '+' : '-').Append(' '); + builder.Append(label); + } + + if (o.CanChangeInfo != n.CanChangeInfo) + { + AppendChange(n.CanChangeInfo, channel ? Strings.Resources.EventLogPromotedChangeChannelInfo : Strings.Resources.EventLogPromotedChangeGroupInfo); + } + + if (channel) + { + if (o.CanPostMessages != n.CanPostMessages) + { + AppendChange(n.CanPostMessages, Strings.Resources.EventLogPromotedPostMessages); + } + if (o.CanEditMessages != n.CanEditMessages) + { + AppendChange(n.CanEditMessages, Strings.Resources.EventLogPromotedEditMessages); + } + } + if (o.CanDeleteMessages != n.CanDeleteMessages) + { + AppendChange(n.CanDeleteMessages, Strings.Resources.EventLogPromotedDeleteMessages); + } + if (o.CanPromoteMembers != n.CanPromoteMembers) + { + AppendChange(n.CanPromoteMembers, Strings.Resources.EventLogPromotedAddAdmins); + } + if (!channel) + { + if (o.CanRestrictMembers != n.CanRestrictMembers) + { + AppendChange(n.CanRestrictMembers, Strings.Resources.EventLogPromotedBanUsers); + } + } + if (o.CanInviteUsers != n.CanInviteUsers) + { + AppendChange(n.CanInviteUsers, Strings.Resources.EventLogPromotedAddUsers); + } + if (!channel) + { + if (o.CanPinMessages != n.CanPinMessages) + { + AppendChange(n.CanPinMessages, Strings.Resources.EventLogPromotedPinMessages); + } + } + + return new MessageText(new FormattedText(builder.ToString(), entities), null); + } + else if (item.Action is ChatEventMessageDeleted messageDeleted) + { + return messageDeleted.Message.Content; + } + else if (item.Action is ChatEventMessageEdited messageEdited) + { + if (messageEdited.NewMessage.Content is MessageText editedText && messageEdited.OldMessage.Content is MessageText oldText) + { + editedText.WebPage = new WebPage + { + SiteName = Strings.Resources.EventLogOriginalMessages, + Description = oldText.Text + }; + } + + return messageEdited.NewMessage.Content; + } + else if (item.Action is ChatEventMessagePinned messagePinned) + { + return messagePinned.Message.Content; + } + else if (item.Action is ChatEventPollStopped pollStopped) + { + return pollStopped.Message.Content; + } + + return new MessageChatEvent(item); + } + + private string GetUserName(User user, List entities, int offset) + { + string name; + if (user == null) + { + name = string.Empty; + } + else + { + name = user.GetFullName(); + } + + if (offset >= 0) + { + entities.Add(new TextEntity(offset, name.Length, new TextEntityTypeMentionName(user.Id))); + } + + if (string.IsNullOrEmpty(user.Username)) + { + return name; + } + + if (offset >= 0) + { + entities.Add(new TextEntity((name.Length + offset) + 2, user.Username.Length + 1, new TextEntityTypeMentionName(user.Id))); + } + + return string.Format("{0} (@{1})", name, user.Username); + } + } +} diff --git a/Unigram/Unigram/ViewModels/DialogScheduledViewModel.cs b/Unigram/Unigram/ViewModels/DialogScheduledViewModel.cs index c5aa6ac2db..418e58c7b5 100644 --- a/Unigram/Unigram/ViewModels/DialogScheduledViewModel.cs +++ b/Unigram/Unigram/ViewModels/DialogScheduledViewModel.cs @@ -10,6 +10,6 @@ public DialogScheduledViewModel(IProtoService protoService, ICacheService cacheS { } - public override bool IsSchedule => true; + public override DialogType Type => DialogType.ScheduledMessages; } } diff --git a/Unigram/Unigram/ViewModels/DialogViewModel.Delegate.cs b/Unigram/Unigram/ViewModels/DialogViewModel.Delegate.cs index 9c183f8afa..d5c29bb2e0 100644 --- a/Unigram/Unigram/ViewModels/DialogViewModel.Delegate.cs +++ b/Unigram/Unigram/ViewModels/DialogViewModel.Delegate.cs @@ -241,17 +241,11 @@ public async void OpenFile(File file) return; } - try + var temp = await ProtoService.GetFileAsync(file); + if (temp != null) { - var temp = await StorageFile.GetFileFromPathAsync(file.Local.Path); - var result = await Windows.System.Launcher.LaunchFileAsync(temp); - //var folder = await temp.GetParentAsync(); - //var options = new Windows.System.FolderLauncherOptions(); - //options.ItemsToSelect.Add(temp); - - //var result = await Windows.System.Launcher.LaunchFolderAsync(folder, options); + await Windows.System.Launcher.LaunchFileAsync(temp); } - catch { } } } @@ -504,6 +498,18 @@ public async void OpenMedia(MessageViewModel message, FrameworkElement target) await new PollResultsPopup(ProtoService, CacheService, Settings, Aggregator, this, message.ChatId, message.Id, poll.Poll).ShowQueuedAsync(); return; } + else if (message.Content is MessageGame game && message.ReplyMarkup is ReplyMarkupInlineKeyboard inline) + { + foreach (var row in inline.Rows) + foreach (var button in row) + { + if (button.Type is InlineKeyboardButtonTypeCallbackGame) + { + KeyboardButtonExecute(message, button); + return; + } + } + } GalleryViewModelBase viewModel = null; @@ -518,7 +524,7 @@ public async void OpenMedia(MessageViewModel message, FrameworkElement target) } } - if (viewModel == null && (message.Content is MessageVideoNote || (webPage != null && webPage.VideoNote != null) || message.Content is MessageAnimation || (webPage != null && webPage.Animation != null) || (message.Content is MessageGame game && game.Game.Animation != null))) + if (viewModel == null && (message.Content is MessageVideoNote || (webPage != null && webPage.VideoNote != null) || message.Content is MessageAnimation || (webPage != null && webPage.Animation != null))) { Delegate?.PlayMessage(message, target); } diff --git a/Unigram/Unigram/ViewModels/DialogViewModel.Handle.cs b/Unigram/Unigram/ViewModels/DialogViewModel.Handle.cs index 6d3f9daf0e..0b15c545ed 100644 --- a/Unigram/Unigram/ViewModels/DialogViewModel.Handle.cs +++ b/Unigram/Unigram/ViewModels/DialogViewModel.Handle.cs @@ -378,12 +378,12 @@ public void Handle(UpdateChatLastMessage update) private bool CheckSchedulingState(Message message) { - if (_isSchedule) + if (_type == DialogType.ScheduledMessages) { return message.SchedulingState != null; } - return message.SchedulingState == null; + return message.SchedulingState == null && _type == DialogType.Normal; } public void Handle(UpdateNewMessage update) @@ -408,59 +408,56 @@ public void Handle(UpdateDeleteMessages update) { using (await _insertLock.WaitAsync()) { + var table = new HashSet(update.MessageIds); + for (int i = 0; i < Items.Count; i++) { - for (int j = 0; j < update.MessageIds.Count; j++) + var message = Items[i]; + if (message.MediaAlbumId != 0 && message.Content is MessageAlbum album) { - var message = Items[i]; - if (message.MediaAlbumId != 0 && message.Content is MessageAlbum album) - { - var found = false; + var found = false; - for (int k = 0; k < album.Layout.Messages.Count; k++) + for (int k = 0; k < album.Layout.Messages.Count; k++) + { + if (table.Contains(album.Layout.Messages[k].Id)) { - if (album.Layout.Messages[k].Id == update.MessageIds[j]) + album.Layout.Messages.RemoveAt(k); + + if (album.Layout.Messages.Count > 0) { - album.Layout.Messages.RemoveAt(k); - - if (album.Layout.Messages.Count > 0) - { - message.UpdateWith(album.Layout.Messages[0]); - album.Layout.Calculate(); - - Handle(new UpdateMessageContent(message.ChatId, message.Id, album)); - } - else - { - Items.RemoveAt(i); - i--; - } - - found = true; - break; + message.UpdateWith(album.Layout.Messages[0]); + album.Layout.Calculate(); + + Handle(new UpdateMessageContent(message.ChatId, message.Id, album)); + } + else + { + Items.RemoveAt(i); + i--; } - } - if (found) - { - continue; + found = true; + break; } } - if (message.Id == update.MessageIds[j]) + if (found) { - Items.RemoveAt(i); - i--; - - break; + continue; } - else if (message.ReplyToMessageId == update.MessageIds[j]) - { - message.ReplyToMessage = null; - message.ReplyToMessageState = ReplyToMessageState.Deleted; + } - Handle(message, bubble => bubble.UpdateMessageReply(message), service => service.UpdateMessage(message)); - } + if (table.Contains(message.Id)) + { + Items.RemoveAt(i); + i--; + } + else if (table.Contains(message.ReplyToMessageId)) + { + message.ReplyToMessage = null; + message.ReplyToMessageState = ReplyToMessageState.Deleted; + + Handle(message, bubble => bubble.UpdateMessageReply(message), service => service.UpdateMessage(message)); } if (i >= 0 && i == Items.Count - 1 && Items[i].Content is MessageHeaderUnread) @@ -560,7 +557,7 @@ public void Handle(UpdateMessageMentionRead update) Handle(update.MessageId, message => { message.ContainsUnreadMention = false; - }, (bubble, message) => { }); + }); BeginOnUIThread(() => Delegate?.UpdateChatUnreadMentionCount(_chat, update.UnreadMentionCount)); } @@ -648,41 +645,16 @@ public void Handle(UpdateFile update) } } - private void Handle(long messageId, Action update, Action action) + private void Handle(long messageId, Action update, Action action = null) { BeginOnUIThread(async () => { - //var message = Items.FirstOrDefault(x => x.Id == messageId); - //if (message == null) - //{ - // return; - //} - - //update(message); - var field = ListField; if (field == null) { return; } - //var container = field.ContainerFromItem(message) as ListViewItem; - //if (container == null) - //{ - // return; - //} - - //var content = container.ContentTemplateRoot as FrameworkElement; - //if (content is Grid grid) - //{ - // content = grid.FindName("Bubble") as FrameworkElement; - //} - - //if (content is MessageBubble bubble) - //{ - // action(bubble, message); - //} - using (await _insertLock.WaitAsync()) { for (int i = 0; i < Items.Count; i++) @@ -692,34 +664,34 @@ private void Handle(long messageId, Action update, Action update, Action files, FormattedTex var self = CacheService.IsSavedMessages(_chat); - var dialog = new SendFilesPopup(items, media, _chat.Type is ChatTypePrivate && !self, !_isSchedule, self); + var dialog = new SendFilesPopup(items, media, _chat.Type is ChatTypePrivate && !self, _type == DialogType.Normal, self); dialog.ViewModel = this; dialog.Caption = caption; @@ -560,7 +560,7 @@ public async Task PickSendMessageOptionsAsync(bool? schedule return null; } - if (schedule == true || (_isSchedule && schedule == null)) + if (schedule == true || (_type == DialogType.ScheduledMessages && schedule == null)) { var user = CacheService.GetUser(chat); @@ -956,7 +956,7 @@ private async void EditCurrentExecute() return; } - var file = await StorageFile.GetFileFromPathAsync(fileInfo.File.Local.Path); + var file = await ProtoService.GetFileAsync(fileInfo.File); if (file == null) { return; diff --git a/Unigram/Unigram/ViewModels/DialogViewModel.Messages.cs b/Unigram/Unigram/ViewModels/DialogViewModel.Messages.cs index 722ed67af7..faa98b4247 100644 --- a/Unigram/Unigram/ViewModels/DialogViewModel.Messages.cs +++ b/Unigram/Unigram/ViewModels/DialogViewModel.Messages.cs @@ -1118,7 +1118,7 @@ public async void KeyboardButtonExecute(MessageViewModel message, object button) var response = await ProtoService.SendAsync(new GetCallbackQueryAnswer(chat.Id, message.Id, new CallbackQueryPayloadGame(game.Game.ShortName))); if (response is CallbackQueryAnswer answer && !string.IsNullOrEmpty(answer.Url)) { - var bundle = new TdBundle(); + var bundle = new Dictionary(); bundle.Add("title", game.Game.Title); bundle.Add("url", answer.Url); bundle.Add("message", message.Id); @@ -1252,6 +1252,12 @@ private async void MessageSaveMediaExecute(MessageViewModel message) return; } + var cached = await ProtoService.GetFileAsync(file); + if (cached == null) + { + return; + } + var fileName = result.FileName; if (string.IsNullOrEmpty(fileName)) { @@ -1280,7 +1286,6 @@ private async void MessageSaveMediaExecute(MessageViewModel message) { try { - var cached = await StorageFile.GetFileFromPathAsync(file.Local.Path); await cached.CopyAndReplaceAsync(picked); } catch { } @@ -1363,12 +1368,14 @@ private async void MessageOpenWithExecute(MessageViewModel message) return; } - var item = await StorageFile.GetFileFromPathAsync(file.Local.Path); - - var options = new LauncherOptions(); - options.DisplayApplicationPicker = true; + var item = await ProtoService.GetFileAsync(file); + if (item != null) + { + var options = new LauncherOptions(); + options.DisplayApplicationPicker = true; - await Launcher.LaunchFileAsync(item, options); + await Launcher.LaunchFileAsync(item, options); + } } #endregion @@ -1386,13 +1393,16 @@ private async void MessageOpenFolderExecute(MessageViewModel message) return; } - var item = await StorageFile.GetFileFromPathAsync(file.Local.Path); - var folder = await item.GetParentAsync(); + var item = await ProtoService.GetFileAsync(file); + if (item != null) + { + var folder = await item.GetParentAsync(); - var options = new FolderLauncherOptions(); - options.ItemsToSelect.Add(item); + var options = new FolderLauncherOptions(); + options.ItemsToSelect.Add(item); - await Launcher.LaunchFolderAsync(folder, options); + await Launcher.LaunchFolderAsync(folder, options); + } } #endregion diff --git a/Unigram/Unigram/ViewModels/DialogViewModel.cs b/Unigram/Unigram/ViewModels/DialogViewModel.cs index 6380802b83..1b136ba63b 100644 --- a/Unigram/Unigram/ViewModels/DialogViewModel.cs +++ b/Unigram/Unigram/ViewModels/DialogViewModel.cs @@ -10,9 +10,9 @@ using Unigram.Controls; using Unigram.Controls.Chats; using Unigram.Navigation; +using Unigram.Navigation.Services; using Unigram.Services; using Unigram.Services.Factories; -using Unigram.Services.Navigation; using Unigram.Services.Updates; using Unigram.ViewModels.Chats; using Unigram.ViewModels.Delegates; @@ -69,22 +69,22 @@ public void ExpandSelection(IEnumerable vector) SelectedItems = messages; } - private readonly ConcurrentDictionary _groupedMessages = new ConcurrentDictionary(); + protected readonly ConcurrentDictionary _groupedMessages = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary> _admins = new ConcurrentDictionary>(); + protected static readonly ConcurrentDictionary> _admins = new ConcurrentDictionary>(); - private readonly DisposableMutex _loadMoreLock = new DisposableMutex(); - private readonly DisposableMutex _insertLock = new DisposableMutex(); + protected readonly DisposableMutex _loadMoreLock = new DisposableMutex(); + protected readonly DisposableMutex _insertLock = new DisposableMutex(); - private readonly StickerDrawerViewModel _stickers; - private readonly AnimationDrawerViewModel _animations; + protected readonly StickerDrawerViewModel _stickers; + protected readonly AnimationDrawerViewModel _animations; - private readonly ILocationService _locationService; - private readonly INotificationsService _pushService; - private readonly IPlaybackService _playbackService; - private readonly IVoIPService _voipService; - private readonly INetworkService _networkService; - private readonly IMessageFactory _messageFactory; + protected readonly ILocationService _locationService; + protected readonly INotificationsService _pushService; + protected readonly IPlaybackService _playbackService; + protected readonly IVoIPService _voipService; + protected readonly INetworkService _networkService; + protected readonly IMessageFactory _messageFactory; public IPlaybackService PlaybackService => _playbackService; @@ -237,7 +237,7 @@ public override IDispatcherWrapper Dispatcher } } - private Chat _migratedChat; + protected Chat _migratedChat; public Chat MigratedChat { get @@ -250,14 +250,14 @@ public Chat MigratedChat } } - private Chat _linkedChat; + protected Chat _linkedChat; public Chat LinkedChat { get => _linkedChat; set => Set(ref _linkedChat, value); } - private Chat _chat; + protected Chat _chat; public Chat Chat { get @@ -276,13 +276,13 @@ public Chat Chat // get => _isSchedule; // set => Set(ref _isSchedule, value); //} - private bool _isSchedule => IsSchedule; - public virtual bool IsSchedule => false; + private DialogType _type => Type; + public virtual DialogType Type => DialogType.Normal; private string _lastSeen; public string LastSeen { - get { return _lastSeen; } + get { return _type == DialogType.EventLog ? Strings.Resources.EventLog : _lastSeen; } set { Set(ref _lastSeen, value); RaisePropertyChanged(() => Subtitle); } } @@ -293,7 +293,7 @@ public string OnlineCount set { Set(ref _onlineCount, value); RaisePropertyChanged(() => Subtitle); } } - public string Subtitle + public virtual string Subtitle { get { @@ -595,12 +595,12 @@ public override bool IsLoading } } - private bool _isLoadingNextSlice; - private bool _isLoadingPreviousSlice; + protected bool _isLoadingNextSlice; + protected bool _isLoadingPreviousSlice; - private Stack _goBackStack = new Stack(); + protected Stack _goBackStack = new Stack(); - public async Task LoadNextSliceAsync(bool force = false, bool init = false) + public async virtual Task LoadNextSliceAsync(bool force = false, bool init = false) { using (await _loadMoreLock.WaitAsync()) { @@ -620,7 +620,7 @@ public async Task LoadNextSliceAsync(bool force = false, bool init = false) return; } - if (_isSchedule || _isLoadingNextSlice || _isLoadingPreviousSlice || Items.Count < 1 || IsLastSliceLoaded == true) + if (_type != DialogType.Normal || _isLoadingNextSlice || _isLoadingPreviousSlice || Items.Count < 1 || IsLastSliceLoaded == true) { return; } @@ -723,7 +723,7 @@ public async Task LoadPreviousSliceAsync(bool force = false, bool init = false) return; } - if (_isSchedule || _isLoadingNextSlice || _isLoadingPreviousSlice || chat == null || Items.Count < 1) + if (_type != DialogType.Normal || _isLoadingNextSlice || _isLoadingPreviousSlice || chat == null || Items.Count < 1) { return; } @@ -800,7 +800,7 @@ public async Task LoadPreviousSliceAsync(bool force = false, bool init = false) } } - private async Task AddHeaderAsync() + protected async Task AddHeaderAsync() { var previous = Items.FirstOrDefault(); if (previous != null && (previous.Content is MessageHeaderDate || previous.Id == 0)) @@ -809,7 +809,7 @@ private async Task AddHeaderAsync() } var chat = _chat; - if (chat == null) + if (chat == null || _type != DialogType.Normal) { goto AddDate; } @@ -944,59 +944,24 @@ private async void NextMentionExecute() } } } - - //var dialog = _dialog; - //if (dialog == null) - //{ - // return; - //} - - //var responsez = await LegacyService.GetUnreadMentionsAsync(_peer, 0, dialog.UnreadMentionsCount - 1, 1, 0, 0); - //if (responsez.IsSucceeded && responsez.Result is ITLMessages result) - //{ - // var count = 0; - // if (responsez.Result is TLMessagesChannelMessages channelMessages) - // { - // count = channelMessages.Count; - // } - - // dialog.UnreadMentionsCount = count; - // dialog.RaisePropertyChanged(() => dialog.UnreadMentionsCount); - - // //if (response.Result.Messages.IsEmpty()) - // //{ - // // dialog.UnreadMentionsCount = 0; - // // dialog.RaisePropertyChanged(() => dialog.UnreadMentionsCount); - // // return; - // //} - - // var commonMessage = result.Messages.FirstOrDefault() as TLMessageCommonBase; - // if (commonMessage == null) - // { - // return; - // } - - // commonMessage.IsMediaUnread = false; - // commonMessage.RaisePropertyChanged(() => commonMessage.IsMediaUnread); - - // // DO NOT AWAIT - // LoadMessageSliceAsync(null, commonMessage.Id); - - // if (With is TLChannel channel) - // { - // await LegacyService.ReadMessageContentsAsync(channel.ToInputChannel(), new TLVector { commonMessage.Id }); - // } - // else - // { - // await LegacyService.ReadMessageContentsAsync(new TLVector { commonMessage.Id }); - // } - //} } public RelayCommand PreviousSliceCommand { get; } private async void PreviousSliceExecute() { - if (_goBackStack.Count > 0) + if (_type == DialogType.ScheduledMessages || _type == DialogType.EventLog) + { + var already = Items.LastOrDefault(); + if (already != null) + { + var field = ListField; + if (field != null) + { + await field.ScrollToItem(already, VerticalAlignment.Bottom, false, int.MaxValue, ScrollIntoViewAlignment.Leading, false); + } + } + } + else if (_goBackStack.Count > 0) { await LoadMessageSliceAsync(null, _goBackStack.Pop()); } @@ -1016,6 +981,11 @@ private async void PreviousSliceExecute() public async Task LoadMessageSliceAsync(long? previousId, long maxId, VerticalAlignment alignment = VerticalAlignment.Center, double? pixel = null, ScrollIntoViewAlignment? direction = null, bool? disableAnimation = null, bool second = false) { + if (_type != DialogType.Normal) + { + return; + } + if (direction == null) { var field = ListField; @@ -1237,6 +1207,11 @@ public async Task LoadScheduledSliceAsync() } } + public virtual Task LoadEventLogSliceAsync(string query = "") + { + return Task.CompletedTask; + } + public async Task LoadDateSliceAsync(int dateOffset) { var chat = _chat; @@ -1285,7 +1260,7 @@ public void ScrollToBottom(object item) public MessageCollection Items { get; } = new MessageCollection(); - private async Task ProcessMessagesAsync(Chat chat, IList messages) + protected async Task ProcessMessagesAsync(Chat chat, IList messages) { if (Settings.IsLargeEmojiEnabled) { @@ -1352,11 +1327,13 @@ private void ProcessFiles(Chat chat, IList messages, MessageVi var target = parent ?? message; var content = message.GeneratedContent ?? message.Content as object; + if (content is MessageAlbum albumMessage) { ProcessFiles(chat, albumMessage.Layout.Messages, message); continue; } + if (content is MessageAnimation animationMessage) { content = animationMessage.Animation; @@ -1743,12 +1720,18 @@ public override async Task OnNavigatedToAsync(object parameter, NavigationMode m } #pragma warning disable CS4014 - if (_isSchedule) + if (_type == DialogType.ScheduledMessages) { Logs.Logger.Debug(Logs.Target.Chat, string.Format("{0} - Loadings scheduled messages", chat.Id)); LoadScheduledSliceAsync(); } + else if (_type == DialogType.EventLog) + { + Logs.Logger.Debug(Logs.Target.Chat, string.Format("{0} - Loadings event log", chat.Id)); + + LoadEventLogSliceAsync(); + } else if (state.TryGet("message_id", out long navigation)) { Logs.Logger.Debug(Logs.Target.Chat, string.Format("{0} - Loading messages from specific id", chat.Id)); @@ -1898,7 +1881,7 @@ int TodayDate(int hour, int minute) ProtoService.Send(new GetChatAdministrators(chat.Id), result => { - if (result is Telegram.Td.Api.ChatAdministrators users) + if (result is ChatAdministrators users) { _admins[chat.Id] = users.Administrators; } @@ -1923,7 +1906,7 @@ int TodayDate(int hour, int minute) ProtoService.Send(new GetChatAdministrators(chat.Id), result => { - if (result is Telegram.Td.Api.ChatAdministrators users) + if (result is ChatAdministrators users) { _admins[chat.Id] = users.Administrators; } @@ -1935,7 +1918,7 @@ int TodayDate(int hour, int minute) ShowPinnedMessage(chat); ShowSwitchInline(state); - if (App.DataPackages.TryRemove(chat.Id, out DataPackageView package)) + if (_type == DialogType.Normal && App.DataPackages.TryRemove(chat.Id, out DataPackageView package)) { await HandlePackageAsync(package); } @@ -1950,14 +1933,14 @@ int TodayDate(int hour, int minute) // } //} - public override Task OnNavigatingFromAsync(NavigatingEventArgs args) + public override void OnNavigatingFrom(NavigatingEventArgs args) { Aggregator.Unsubscribe(this); var chat = _chat; if (chat == null) { - return Task.CompletedTask; + return; } #if !DEBUG @@ -1979,7 +1962,7 @@ public override Task OnNavigatingFromAsync(NavigatingEventArgs args) Logs.Logger.Debug(Logs.Target.Chat, string.Format("{0} - Removing scrolling position, generic reason", chat.Id)); - return Task.CompletedTask; + return; } var panel = field.ItemsPanelRoot as ItemsStackPanel; @@ -2034,17 +2017,12 @@ public override Task OnNavigatingFromAsync(NavigatingEventArgs args) Logs.Logger.Debug(Logs.Target.Chat, string.Format("{0} - Removing scrolling position, exception", chat.Id)); } - if (Dispatcher != null) - { - Dispatcher.Dispatch(SaveDraft); - } - - return Task.CompletedTask; + SaveDraft(); } private void ShowSwitchInline(IDictionary state) { - if (state.TryGet("switch_query", out string query) && state.TryGet("switch_bot", out int userId)) + if (_type == DialogType.Normal && state.TryGet("switch_query", out string query) && state.TryGet("switch_bot", out int userId)) { state.Remove("switch_query"); state.Remove("switch_bot"); @@ -2062,7 +2040,7 @@ private void ShowSwitchInline(IDictionary state) private async void ShowReplyMarkup(Chat chat) { - if (chat.ReplyMarkupMessageId == 0) + if (chat.ReplyMarkupMessageId == 0 || _type != DialogType.Normal) { Delegate?.UpdateChatReplyMarkup(chat, null); } @@ -2082,7 +2060,7 @@ private async void ShowReplyMarkup(Chat chat) public async void ShowPinnedMessage(Chat chat) { - if (chat == null || chat.PinnedMessageId == 0) + if (chat == null || chat.PinnedMessageId == 0 || _type != DialogType.Normal) { Delegate?.UpdatePinnedMessage(chat, null, false); } @@ -2112,7 +2090,7 @@ public async void ShowPinnedMessage(Chat chat) private async void ShowDraftMessage(Chat chat) { var draft = chat.DraftMessage; - if (draft == null) + if (draft == null || _type != DialogType.Normal) { SetText(null as string); ComposerHeader = null; @@ -3196,6 +3174,11 @@ private void ScheduledExecute() #region Action + protected virtual void FilterExecute() + { + + } + public RelayCommand ActionCommand { get; } private void ActionExecute() { @@ -3205,7 +3188,11 @@ private void ActionExecute() return; } - if (chat.Type is ChatTypePrivate privata) + if (_type == DialogType.EventLog) + { + FilterExecute(); + } + else if (chat.Type is ChatTypePrivate privata) { var fullInfo = ProtoService.GetUserFull(privata.UserId); if (fullInfo == null) @@ -3330,8 +3317,7 @@ public UserCommand(int userId, BotCommand command) public class MessageCollection : MvxObservableCollection { - private readonly Dictionary _messages = new Dictionary(); - private readonly HashSet _dates = new HashSet(); + private readonly HashSet _messages = new HashSet(); public Action> AttachChanged; @@ -3343,12 +3329,12 @@ public class MessageCollection : MvxObservableCollection public bool ContainsKey(long id) { - return _messages.ContainsKey(id); + return _messages.Contains(id); } protected override void InsertItem(int index, MessageViewModel item) { - _messages[item.Id] = item.Id; + _messages.Add(item.Id); item.IsFirst = true; item.IsLast = true; @@ -3611,4 +3597,11 @@ public enum ReplyToMessageState Loading, Deleted } + + public enum DialogType + { + Normal, + ScheduledMessages, + EventLog + } } diff --git a/Unigram/Unigram/ViewModels/Folders/FolderViewModel.cs b/Unigram/Unigram/ViewModels/Folders/FolderViewModel.cs index c5cae14ec5..19ece75c04 100644 --- a/Unigram/Unigram/ViewModels/Folders/FolderViewModel.cs +++ b/Unigram/Unigram/ViewModels/Folders/FolderViewModel.cs @@ -129,7 +129,7 @@ public ChatFilter Filter set => Set(ref _filter, value); } - private string _title; + private string _title = string.Empty; public string Title { get => _title; @@ -281,7 +281,7 @@ private bool SendCanExecute() private ChatFilter GetFilter() { var filter = new ChatFilter(); - filter.Title = Title; + filter.Title = Title ?? string.Empty; filter.IconName = _iconPicked ? Enum.GetName(typeof(ChatFilterIcon), Icon) : string.Empty; filter.PinnedChatIds = new List(); filter.IncludedChatIds = new List(); diff --git a/Unigram/Unigram/ViewModels/Gallery/GalleryViewModelBase.cs b/Unigram/Unigram/ViewModels/Gallery/GalleryViewModelBase.cs index 33688d4aa6..ff567d7143 100644 --- a/Unigram/Unigram/ViewModels/Gallery/GalleryViewModelBase.cs +++ b/Unigram/Unigram/ViewModels/Gallery/GalleryViewModelBase.cs @@ -260,6 +260,12 @@ protected virtual async void SaveExecute() return; } + var cached = await ProtoService.GetFileAsync(file); + if (cached == null) + { + return; + } + var fileName = result.FileName; if (string.IsNullOrEmpty(fileName)) { @@ -282,7 +288,6 @@ protected virtual async void SaveExecute() { try { - var cached = await StorageFile.GetFileFromPathAsync(file.Local.Path); await cached.CopyAndReplaceAsync(picked); } catch { } @@ -301,17 +306,14 @@ protected virtual async void OpenWithExecute() var file = item.GetFile(); if (file != null && file.Local.IsDownloadingCompleted) { - try + var temp = await ProtoService.GetFileAsync(file); + if (temp != null) { - var temp = await StorageFile.GetFileFromPathAsync(file.Local.Path); - var options = new LauncherOptions(); options.DisplayApplicationPicker = true; await Launcher.LaunchFileAsync(temp, options); - } - catch { } } } diff --git a/Unigram/Unigram/ViewModels/ProfileViewModel.cs b/Unigram/Unigram/ViewModels/ProfileViewModel.cs index 7add448506..1308d2b857 100644 --- a/Unigram/Unigram/ViewModels/ProfileViewModel.cs +++ b/Unigram/Unigram/ViewModels/ProfileViewModel.cs @@ -8,6 +8,7 @@ using Unigram.Common; using Unigram.Controls; using Unigram.Converters; +using Unigram.Navigation.Services; using Unigram.Services; using Unigram.ViewModels.Chats; using Unigram.ViewModels.Delegates; @@ -1125,7 +1126,7 @@ private void MemberPromoteExecute(ChatMember member) return; } - NavigationService.Navigate(typeof(SupergroupEditAdministratorPage), new ChatMemberNavigation(chat.Id, member.UserId)); + NavigationService.Navigate(typeof(SupergroupEditAdministratorPage), state: NavigationState.GetChatMember(chat.Id, member.UserId)); } public RelayCommand MemberRestrictCommand { get; } @@ -1137,7 +1138,7 @@ private void MemberRestrictExecute(ChatMember member) return; } - NavigationService.Navigate(typeof(SupergroupEditRestrictedPage), new ChatMemberNavigation(chat.Id, member.UserId)); + NavigationService.Navigate(typeof(SupergroupEditRestrictedPage), state: NavigationState.GetChatMember(chat.Id, member.UserId)); } public RelayCommand MemberRemoveCommand { get; } diff --git a/Unigram/Unigram/ViewModels/Settings/Privacy/SettingsPrivacyViewModelBase.cs b/Unigram/Unigram/ViewModels/Settings/Privacy/SettingsPrivacyViewModelBase.cs index f13e52f36a..bab48e0cb7 100644 --- a/Unigram/Unigram/ViewModels/Settings/Privacy/SettingsPrivacyViewModelBase.cs +++ b/Unigram/Unigram/ViewModels/Settings/Privacy/SettingsPrivacyViewModelBase.cs @@ -30,7 +30,7 @@ public SettingsPrivacyViewModelBase(IProtoService protoService, ICacheService ca public override Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary state) { - if (mode != NavigationMode.Back) + //if (mode != NavigationMode.Back) { UpdatePrivacyAsync(); } diff --git a/Unigram/Unigram/ViewModels/Settings/SettingsAppearanceViewModel.cs b/Unigram/Unigram/ViewModels/Settings/SettingsAppearanceViewModel.cs index 45c8ef0af4..d7b8c03052 100644 --- a/Unigram/Unigram/ViewModels/Settings/SettingsAppearanceViewModel.cs +++ b/Unigram/Unigram/ViewModels/Settings/SettingsAppearanceViewModel.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Threading.Tasks; using Unigram.Common; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.Services.Updates; using Unigram.Views.Popups; using Windows.Foundation.Metadata; @@ -50,7 +50,7 @@ public override async Task OnNavigatedToAsync(object parameter, NavigationMode m await base.OnNavigatedToAsync(parameter, mode, state); } - public override Task OnNavigatingFromAsync(NavigatingEventArgs args) + public override Task OnNavigatedFromAsync(IDictionary pageState, bool suspending) { if (UseThreeLinesLayout != Settings.UseThreeLinesLayout) { @@ -58,7 +58,7 @@ public override Task OnNavigatingFromAsync(NavigatingEventArgs args) Aggregator.Publish(new UpdateChatListLayout(UseThreeLinesLayout)); } - return base.OnNavigatingFromAsync(args); + return base.OnNavigatedFromAsync(pageState, suspending); } private string _emojiSet; diff --git a/Unigram/Unigram/ViewModels/Settings/SettingsBlockedUsersViewModel.cs b/Unigram/Unigram/ViewModels/Settings/SettingsBlockedUsersViewModel.cs index 4cbc0a8729..6dc51cab62 100644 --- a/Unigram/Unigram/ViewModels/Settings/SettingsBlockedUsersViewModel.cs +++ b/Unigram/Unigram/ViewModels/Settings/SettingsBlockedUsersViewModel.cs @@ -7,8 +7,8 @@ using Unigram.Collections; using Unigram.Common; using Unigram.Controls; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Unigram.ViewModels.Delegates; using Unigram.Views.Popups; using Windows.Foundation; @@ -37,7 +37,7 @@ public override Task OnNavigatedToAsync(object parameter, NavigationMode mode, I return Task.CompletedTask; } - public override Task OnNavigatingFromAsync(NavigatingEventArgs args) + public override Task OnNavigatedFromAsync(IDictionary pageState, bool suspending) { Aggregator.Unsubscribe(this); return Task.CompletedTask; diff --git a/Unigram/Unigram/ViewModels/Supergroups/SupergroupAddRestrictedViewModel.cs b/Unigram/Unigram/ViewModels/Supergroups/SupergroupAddRestrictedViewModel.cs index 7b43cd3e16..71b428c830 100644 --- a/Unigram/Unigram/ViewModels/Supergroups/SupergroupAddRestrictedViewModel.cs +++ b/Unigram/Unigram/ViewModels/Supergroups/SupergroupAddRestrictedViewModel.cs @@ -1,6 +1,7 @@ using Telegram.Td.Api; using Unigram.Collections; using Unigram.Common; +using Unigram.Navigation.Services; using Unigram.Services; using Unigram.Views.Supergroups; @@ -50,7 +51,7 @@ private async void AddExecute(int userId) } else { - NavigationService.Navigate(typeof(SupergroupEditRestrictedPage), new ChatMemberNavigation(chat.Id, userId)); + NavigationService.Navigate(typeof(SupergroupEditRestrictedPage), state: NavigationState.GetChatMember(chat.Id, userId)); } } } diff --git a/Unigram/Unigram/ViewModels/Supergroups/SupergroupAdministratorsViewModel.cs b/Unigram/Unigram/ViewModels/Supergroups/SupergroupAdministratorsViewModel.cs index c2d5e3cd34..1d2ef342e2 100644 --- a/Unigram/Unigram/ViewModels/Supergroups/SupergroupAdministratorsViewModel.cs +++ b/Unigram/Unigram/ViewModels/Supergroups/SupergroupAdministratorsViewModel.cs @@ -1,6 +1,7 @@ using Telegram.Td.Api; using Unigram.Common; using Unigram.Services; +using Unigram.Views; using Unigram.Views.Supergroups; namespace Unigram.ViewModels.Supergroups @@ -24,7 +25,7 @@ private void EventLogExecute() return; } - NavigationService.Navigate(typeof(SupergroupEventLogPage), chat.Id); + NavigationService.Navigate(typeof(ChatEventLogPage), chat.Id); } public RelayCommand AddCommand { get; } diff --git a/Unigram/Unigram/ViewModels/Supergroups/SupergroupEditAdministratorViewModel.cs b/Unigram/Unigram/ViewModels/Supergroups/SupergroupEditAdministratorViewModel.cs index a8344255fd..d96ba7fa38 100644 --- a/Unigram/Unigram/ViewModels/Supergroups/SupergroupEditAdministratorViewModel.cs +++ b/Unigram/Unigram/ViewModels/Supergroups/SupergroupEditAdministratorViewModel.cs @@ -55,13 +55,10 @@ public ChatMember Member public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary state) { - var bundle = parameter as ChatMemberNavigation; - if (bundle == null) - { - return; - } + state.TryGet("chatId", out long chatId); + state.TryGet("userId", out int userId); - Chat = ProtoService.GetChat(bundle.ChatId); + Chat = ProtoService.GetChat(chatId); var chat = _chat; if (chat == null) @@ -69,7 +66,7 @@ public override async Task OnNavigatedToAsync(object parameter, NavigationMode m return; } - var response = await ProtoService.SendAsync(new GetChatMember(chat.Id, bundle.UserId)); + var response = await ProtoService.SendAsync(new GetChatMember(chat.Id, userId)); if (response is ChatMember member) { var item = ProtoService.GetUser(member.UserId); diff --git a/Unigram/Unigram/ViewModels/Supergroups/SupergroupEditRestrictedViewModel.cs b/Unigram/Unigram/ViewModels/Supergroups/SupergroupEditRestrictedViewModel.cs index 73b46a8b2e..9e802d82f8 100644 --- a/Unigram/Unigram/ViewModels/Supergroups/SupergroupEditRestrictedViewModel.cs +++ b/Unigram/Unigram/ViewModels/Supergroups/SupergroupEditRestrictedViewModel.cs @@ -53,13 +53,10 @@ public ChatMember Member public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary state) { - var bundle = parameter as ChatMemberNavigation; - if (bundle == null) - { - return; - } + state.TryGet("chatId", out long chatId); + state.TryGet("userId", out int userId); - Chat = ProtoService.GetChat(bundle.ChatId); + Chat = ProtoService.GetChat(chatId); var chat = _chat; if (chat == null) @@ -67,7 +64,7 @@ public override async Task OnNavigatedToAsync(object parameter, NavigationMode m return; } - var response = await ProtoService.SendAsync(new GetChatMember(chat.Id, bundle.UserId)); + var response = await ProtoService.SendAsync(new GetChatMember(chat.Id, userId)); if (response is ChatMember member) { var item = ProtoService.GetUser(member.UserId); diff --git a/Unigram/Unigram/ViewModels/Supergroups/SupergroupEventLogViewModel.cs b/Unigram/Unigram/ViewModels/Supergroups/SupergroupEventLogViewModel.cs deleted file mode 100644 index 86469cd962..0000000000 --- a/Unigram/Unigram/ViewModels/Supergroups/SupergroupEventLogViewModel.cs +++ /dev/null @@ -1,953 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Telegram.Td.Api; -using Unigram.Collections; -using Unigram.Common; -using Unigram.Controls; -using Unigram.Converters; -using Unigram.Services; -using Unigram.Services.Factories; -using Unigram.ViewModels.Delegates; -using Unigram.Views.Popups; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Navigation; - -namespace Unigram.ViewModels.Supergroups -{ - public class SupergroupEventLogViewModel : TLViewModelBase, IDelegable, IMessageDelegate - { - private readonly IMessageFactory _messageFactory; - - public SupergroupEventLogViewModel(IProtoService protoService, ICacheService cacheService, ISettingsService settingsService, IMessageFactory messageFactory, IEventAggregator aggregator) - : base(protoService, cacheService, settingsService, aggregator) - { - _messageFactory = messageFactory; - - FiltersCommand = new RelayCommand(FiltersExecute); - HelpCommand = new RelayCommand(HelpExecute); - } - - public IChatDelegate Delegate { get; set; } - - protected Chat _chat; - public Chat Chat - { - get - { - return _chat; - } - set - { - Set(ref _chat, value); - } - } - - private ChatEventLogFilters _filters = new ChatEventLogFilters(true, true, true, true, true, true, true, true, true, true); - public ChatEventLogFilters Filters - { - get - { - return _filters; - } - set - { - Set(ref _filters, value); - } - } - - private IList _userIds = new int[0]; - public IList UserIds - { - get - { - return _userIds; - } - set - { - Set(ref _userIds, value); - } - } - - private bool _isEmpty = true; - public bool IsEmpty - { - get - { - return _isEmpty && !IsLoading; - } - set - { - Set(ref _isEmpty, value); - } - } - - public override bool IsLoading - { - get - { - return base.IsLoading; - } - set - { - base.IsLoading = value; - RaisePropertyChanged(() => IsEmpty); - } - } - - public override Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary state) - { - var chatId = (long)parameter; - - Chat = ProtoService.GetChat(chatId); - - var chat = _chat; - if (chat == null) - { - return Task.CompletedTask; - } - - Delegate?.UpdateChat(chat); - - Items = new ItemsCollection(ProtoService, _messageFactory, this, chat.Id, chat.Type is ChatTypeSupergroup supergroup && supergroup.IsChannel, _filters, _userIds); - Items.CollectionChanged += (s, args) => IsEmpty = Items.Count == 0; - - RaisePropertyChanged(() => Items); - - return Task.CompletedTask; - } - - public RelayCommand FiltersCommand { get; } - private async void FiltersExecute() - { - var chat = _chat; - if (chat == null) - { - return; - } - - var supergroup = CacheService.GetSupergroup(chat); - if (supergroup == null) - { - return; - } - - var dialog = new SupergroupEventLogFiltersPopup(); - - var confirm = await dialog.ShowAsync(ProtoService, supergroup.Id, _filters, _userIds); - if (confirm == ContentDialogResult.Primary) - { - Filters = dialog.Filters; - UserIds = dialog.UserIds; - Items = new ItemsCollection(ProtoService, _messageFactory, this, chat.Id, supergroup.IsChannel, dialog.Filters, dialog.AreAllAdministratorsSelected ? new int[0] : dialog.UserIds); - - RaisePropertyChanged(() => Items); - } - } - - public RelayCommand HelpCommand { get; } - private async void HelpExecute() - { - var chat = _chat; - if (chat == null) - { - return; - } - - await MessagePopup.ShowAsync(chat.Type is ChatTypeSupergroup supergroup && supergroup.IsChannel ? Strings.Resources.EventLogInfoDetailChannel : Strings.Resources.EventLogInfoDetail, Strings.Resources.EventLogInfoTitle, Strings.Resources.OK); - } - - public ItemsCollection Items { get; protected set; } - - public class ItemsCollection : IncrementalCollection - { - private readonly IProtoService _protoService; - private readonly IMessageFactory _messageFactory; - private readonly IMessageDelegate _delegate; - private readonly long _chatId; - private readonly bool _channel; - private readonly ChatEventLogFilters _filters; - private readonly IList _userIds; - - private long _minEventId = long.MaxValue; - private bool _hasMore; - - public ItemsCollection(IProtoService protoService, IMessageFactory messageFactory, IMessageDelegate delegato, long chatId, bool channel, ChatEventLogFilters filters, IList userIds) - { - _protoService = protoService; - _messageFactory = messageFactory; - _delegate = delegato; - _chatId = chatId; - _channel = channel; - _filters = filters; - _userIds = userIds; - - _hasMore = true; - } - - private Message newMessage(long chatId, bool isChannel, ChatEvent chatEvent) - { - return new Message(chatEvent.Id, chatEvent.UserId, chatId, null, null, false, false, false, false, false, isChannel, false, chatEvent.Date, 0, null, 0, 0, 0.0d, 0, string.Empty, 0, 0, string.Empty, null, null); - } - - private MessageViewModel GetMessage(long chatId, bool isChannel, ChatEvent chatEvent) - { - var message = _messageFactory.Create(_delegate, newMessage(chatId, isChannel, chatEvent)); - message.IsFirst = true; - message.IsLast = true; - - return message; - } - - public override async Task> LoadDataAsync() - { - _hasMore = false; - - var maxId = Count > 0 ? _minEventId : 0; - - var response = await _protoService.SendAsync(new GetChatEventLog(_chatId, string.Empty, 0, 50, _filters, _userIds)); - if (response is ChatEvents events) - { - var result = new List(); - - foreach (var item in events.Events) - { - _minEventId = Math.Min(_minEventId, item.Id); - - MessageViewModel message = null; - switch (item.Action) - { - case ChatEventMemberInvited memberInvited: - message = GetMessage(_chatId, _channel, item); - message.Content = new MessageChatAddMembers(new[] { memberInvited.UserId }); - break; - case ChatEventSlowModeDelayChanged slowModeDelayChanged: - case ChatEventPermissionsChanged permissionsChanged: - case ChatEventMemberRestricted memberRestricted: - case ChatEventMemberPromoted memberPromoted: - message = GetMessage(_chatId, _channel, item); - //message.Content = new MessageChatEvent(item, true); - message.Content = GetMessageContent(item); - break; - case ChatEventSignMessagesToggled signMessagesToggled: - case ChatEventStickerSetChanged stickerSetChanged: - case ChatEventInvitesToggled invitesToggled: - case ChatEventIsAllHistoryAvailableToggled isAllHistoryAvailableToggled: - case ChatEventMessageUnpinned messageUnpinned: - case ChatEventLinkedChatChanged linkedChatChanged: - case ChatEventLocationChanged locationChanged: - message = GetMessage(_chatId, _channel, item); - message.Content = new MessageChatEvent(item, false); - break; - case ChatEventMemberLeft memberLeft: - message = GetMessage(_chatId, _channel, item); - message.Content = new MessageChatDeleteMember(item.UserId); - break; - case ChatEventMessageDeleted messageDeleted: - case ChatEventMessageEdited messageEdited: - case ChatEventDescriptionChanged descriptionChanged: - case ChatEventMessagePinned messagePinned: - case ChatEventUsernameChanged usernameChanged: - case ChatEventPollStopped pollStopped: - message = GetMessage(_chatId, _channel, item); - //message.Content = new MessageChatEvent(item, true); - message.Content = GetMessageContent(item); - result.Add(message); - message = GetMessage(_chatId, _channel, item); - message.Content = new MessageChatEvent(item, false); - break; - case ChatEventPhotoChanged photoChanged: - message = GetMessage(_chatId, _channel, item); - if (photoChanged.NewPhoto == null) - { - message.Content = new MessageChatDeletePhoto(); - break; - } - - message.Content = new MessageChatChangePhoto(photoChanged.NewPhoto); - break; - case ChatEventMemberJoined memberJoined: - message = GetMessage(_chatId, _channel, item); - message.Content = new MessageChatAddMembers(new int[] { item.UserId }); - break; - case ChatEventTitleChanged titleChanged: - message = GetMessage(_chatId, _channel, item); - message.Content = new MessageChatChangeTitle(titleChanged.NewTitle); - break; - } - - - if (message != null) - { - result.Add(message); - } - } - - //if (response.Result.Events.Count < 50) - //{ - // _hasMore = false; - //} - - result.Reverse(); - return result; - } - - return new MessageViewModel[0]; - } - - private MessageContent GetMessageContent(ChatEvent item) - { - if (item.Action is ChatEventDescriptionChanged descriptionChanged) - { - var text = new FormattedText(descriptionChanged.NewDescription, new TextEntity[0]); - var webPage = string.IsNullOrEmpty(descriptionChanged.OldDescription) ? null : new WebPage { SiteName = Strings.Resources.EventLogPreviousGroupDescription, Description = new FormattedText { Text = descriptionChanged.OldDescription } }; - - return new MessageText(text, webPage); - } - else if (item.Action is ChatEventUsernameChanged usernameChanged) - { - var link = string.IsNullOrEmpty(usernameChanged.NewUsername) ? string.Empty : MeUrlPrefixConverter.Convert(_protoService, usernameChanged.NewUsername); - - var text = new FormattedText(link, new[] { new TextEntity(0, link.Length, new TextEntityTypeUrl()) }); - var webPage = string.IsNullOrEmpty(usernameChanged.OldUsername) ? null : new WebPage { SiteName = Strings.Resources.EventLogPreviousLink, Description = new FormattedText { Text = MeUrlPrefixConverter.Convert(_protoService, usernameChanged.OldUsername) } }; - - return new MessageText(text, webPage); - } - else if (item.Action is ChatEventPermissionsChanged permissionChanged) - { - var text = string.Empty; - var entities = new List(); - - ChatPermissions o = permissionChanged.OldPermissions; - ChatPermissions n = permissionChanged.NewPermissions; - - if (o == null) - { - o = new ChatPermissions(); - } - if (n == null) - { - n = new ChatPermissions(); - } - - var rights = new StringBuilder(Strings.Resources.EventLogDefaultPermissions); - var added = false; - - void AppendChange(bool value, string label) - { - if (!added) - { - rights.Append('\n'); - added = true; - } - - rights.Append('\n').Append(value ? '+' : '-').Append(' '); - rights.Append(label); - } - - //if (o.IsViewMessages != n.IsViewMessages) - //{ - // AppendChange(n.IsViewMessages, Strings.Resources.EventLogRestrictedReadMessages); - //} - if (o.CanSendMessages != n.CanSendMessages) - { - AppendChange(n.CanSendMessages, Strings.Resources.EventLogRestrictedSendMessages); - } - if (o.CanSendOtherMessages != n.CanSendOtherMessages) - { - AppendChange(n.CanSendOtherMessages, Strings.Resources.EventLogRestrictedSendStickers); - } - if (o.CanSendMediaMessages != n.CanSendMediaMessages) - { - AppendChange(n.CanSendMediaMessages, Strings.Resources.EventLogRestrictedSendMedia); - } - if (o.CanSendPolls != n.CanSendPolls) - { - AppendChange(n.CanSendPolls, Strings.Resources.EventLogRestrictedSendPolls); - } - if (o.CanAddWebPagePreviews != n.CanAddWebPagePreviews) - { - AppendChange(n.CanAddWebPagePreviews, Strings.Resources.EventLogRestrictedSendEmbed); - } - if (o.CanChangeInfo != n.CanChangeInfo) - { - AppendChange(n.CanChangeInfo, Strings.Resources.EventLogRestrictedChangeInfo); - } - if (o.CanInviteUsers != n.CanInviteUsers) - { - AppendChange(n.CanInviteUsers, Strings.Resources.EventLogRestrictedSendEmbed); - } - if (o.CanPinMessages != n.CanPinMessages) - { - AppendChange(n.CanPinMessages, Strings.Resources.EventLogRestrictedPinMessages); - } - - text = rights.ToString(); - - return new MessageText(new FormattedText(text, entities), null); - } - else if (item.Action is ChatEventMemberRestricted memberRestricted) - { - var text = string.Empty; - var entities = new List(); - - var whoUser = _protoService.GetUser(memberRestricted.UserId); - ChatMemberStatusRestricted o = null; - ChatMemberStatusRestricted n = null; - - if (memberRestricted.OldStatus is ChatMemberStatusRestricted oldRestricted) - { - o = oldRestricted; - } - else if (memberRestricted.OldStatus is ChatMemberStatusBanned oldBanned) - { - o = new ChatMemberStatusRestricted(false, oldBanned.BannedUntilDate, new ChatPermissions(false, false, false, false, false, false, false, false)); - } - else if (memberRestricted.OldStatus is ChatMemberStatusMember) - { - o = new ChatMemberStatusRestricted(true, 0, new ChatPermissions(true, true, true, true, true, true, true, true)); - } - - if (memberRestricted.NewStatus is ChatMemberStatusRestricted newRestricted) - { - n = newRestricted; - } - else if (memberRestricted.NewStatus is ChatMemberStatusBanned newBanned) - { - n = new ChatMemberStatusRestricted(false, newBanned.BannedUntilDate, new ChatPermissions(false, false, false, false, false, false, false, false)); - } - else if (memberRestricted.NewStatus is ChatMemberStatusMember) - { - n = new ChatMemberStatusRestricted(true, 0, new ChatPermissions(true, true, true, true, true, true, true, true)); - } - - if (!_channel && (n == null || n != null && o != null /*&& n.RestrictedUntilDate != o.RestrictedUntilDate*/)) - { - StringBuilder rights; - String bannedDuration; - if (n != null && !n.IsForever()) - { - bannedDuration = ""; - int duration = n.RestrictedUntilDate - item.Date; - int days = duration / 60 / 60 / 24; - duration -= days * 60 * 60 * 24; - int hours = duration / 60 / 60; - duration -= hours * 60 * 60; - int minutes = duration / 60; - int count = 0; - for (int a = 0; a < 3; a++) - { - String addStr = null; - if (a == 0) - { - if (days != 0) - { - addStr = Locale.Declension("Days", days); - count++; - } - } - else if (a == 1) - { - if (hours != 0) - { - addStr = Locale.Declension("Hours", hours); - count++; - } - } - else - { - if (minutes != 0) - { - addStr = Locale.Declension("Minutes", minutes); - count++; - } - } - if (addStr != null) - { - if (bannedDuration.Length > 0) - { - bannedDuration += ", "; - } - bannedDuration += addStr; - } - if (count == 2) - { - break; - } - } - } - else - { - bannedDuration = Strings.Resources.UserRestrictionsUntilForever; - } - - var str = Strings.Resources.EventLogRestrictedUntil; - rights = new StringBuilder(String.Format(str, GetUserName(whoUser, entities, str.IndexOf("{0}")), bannedDuration)); - var added = false; - if (o == null) - { - o = new ChatMemberStatusRestricted(true, 0, new ChatPermissions(true, true, true, true, true, true, true, true)); - } - if (n == null) - { - n = new ChatMemberStatusRestricted(true, 0, new ChatPermissions(true, true, true, true, true, true, true, true)); - } - - void AppendChange(bool value, string label) - { - if (!added) - { - rights.Append('\n'); - added = true; - } - - rights.Append('\n').Append(value ? '+' : '-').Append(' '); - rights.Append(label); - } - - //if (o.IsViewMessages != n.IsViewMessages) - //{ - // AppendChange(n.IsViewMessages, Strings.Resources.EventLogRestrictedReadMessages); - //} - if (o.Permissions.CanSendMessages != n.Permissions.CanSendMessages) - { - AppendChange(n.Permissions.CanSendMessages, Strings.Resources.EventLogRestrictedSendMessages); - } - if (o.Permissions.CanSendOtherMessages != n.Permissions.CanSendOtherMessages) - { - AppendChange(n.Permissions.CanSendOtherMessages, Strings.Resources.EventLogRestrictedSendStickers); - } - if (o.Permissions.CanSendMediaMessages != n.Permissions.CanSendMediaMessages) - { - AppendChange(n.Permissions.CanSendMediaMessages, Strings.Resources.EventLogRestrictedSendMedia); - } - if (o.Permissions.CanSendPolls != n.Permissions.CanSendPolls) - { - AppendChange(n.Permissions.CanSendPolls, Strings.Resources.EventLogRestrictedSendPolls); - } - if (o.Permissions.CanAddWebPagePreviews != n.Permissions.CanAddWebPagePreviews) - { - AppendChange(n.Permissions.CanAddWebPagePreviews, Strings.Resources.EventLogRestrictedSendEmbed); - } - if (o.Permissions.CanChangeInfo != n.Permissions.CanChangeInfo) - { - AppendChange(n.Permissions.CanChangeInfo, Strings.Resources.EventLogRestrictedChangeInfo); - } - if (o.Permissions.CanInviteUsers != n.Permissions.CanInviteUsers) - { - AppendChange(n.Permissions.CanInviteUsers, Strings.Resources.EventLogRestrictedSendEmbed); - } - if (o.Permissions.CanPinMessages != n.Permissions.CanPinMessages) - { - AppendChange(n.Permissions.CanPinMessages, Strings.Resources.EventLogRestrictedPinMessages); - } - - text = rights.ToString(); - } - else - { - String str; - if (o == null || memberRestricted.NewStatus is ChatMemberStatusBanned) - { - str = Strings.Resources.EventLogChannelRestricted; - } - else - { - str = Strings.Resources.EventLogChannelUnrestricted; - } - - text = String.Format(str, GetUserName(whoUser, entities, str.IndexOf("{0}"))); - } - - return new MessageText(new FormattedText(text, entities), null); - } - else if (item.Action is ChatEventMemberPromoted memberPromoted) - { - var entities = new List(); - - var whoUser = _protoService.GetUser(memberPromoted.UserId); - var str = Strings.Resources.EventLogPromoted; - var userName = GetUserName(whoUser, entities, str.IndexOf("{0}")); - var builder = new StringBuilder(string.Format(str, userName)); - var added = false; - - ChatMemberStatusAdministrator o = null; - ChatMemberStatusAdministrator n = null; - - if (memberPromoted.OldStatus is ChatMemberStatusAdministrator oldAdmin) - { - o = oldAdmin; - } - if (memberPromoted.NewStatus is ChatMemberStatusAdministrator newAdmin) - { - n = newAdmin; - } - - if (o == null) - { - o = new ChatMemberStatusAdministrator(); - } - if (n == null) - { - n = new ChatMemberStatusAdministrator(); - } - - void AppendChange(bool value, string label) - { - if (!added) - { - builder.Append('\n'); - added = true; - } - - builder.Append('\n').Append(value ? '+' : '-').Append(' '); - builder.Append(label); - } - - if (o.CanChangeInfo != n.CanChangeInfo) - { - AppendChange(n.CanChangeInfo, _channel ? Strings.Resources.EventLogPromotedChangeChannelInfo : Strings.Resources.EventLogPromotedChangeGroupInfo); - } - - if (_channel) - { - if (o.CanPostMessages != n.CanPostMessages) - { - AppendChange(n.CanPostMessages, Strings.Resources.EventLogPromotedPostMessages); - } - if (o.CanEditMessages != n.CanEditMessages) - { - AppendChange(n.CanEditMessages, Strings.Resources.EventLogPromotedEditMessages); - } - } - if (o.CanDeleteMessages != n.CanDeleteMessages) - { - AppendChange(n.CanDeleteMessages, Strings.Resources.EventLogPromotedDeleteMessages); - } - if (o.CanPromoteMembers != n.CanPromoteMembers) - { - AppendChange(n.CanPromoteMembers, Strings.Resources.EventLogPromotedAddAdmins); - } - if (!_channel) - { - if (o.CanRestrictMembers != n.CanRestrictMembers) - { - AppendChange(n.CanRestrictMembers, Strings.Resources.EventLogPromotedBanUsers); - } - } - if (o.CanInviteUsers != n.CanInviteUsers) - { - AppendChange(n.CanInviteUsers, Strings.Resources.EventLogPromotedAddUsers); - } - if (!_channel) - { - if (o.CanPinMessages != n.CanPinMessages) - { - AppendChange(n.CanPinMessages, Strings.Resources.EventLogPromotedPinMessages); - } - } - - return new MessageText(new FormattedText(builder.ToString(), entities), null); - } - - return new MessageChatEvent(item, true); - } - - private string GetUserName(User user, List entities, int offset) - { - string name; - if (user == null) - { - name = string.Empty; - } - else - { - name = user.GetFullName(); - } - - if (offset >= 0) - { - entities.Add(new TextEntity(offset, name.Length, new TextEntityTypeMentionName(user.Id))); - } - - if (string.IsNullOrEmpty(user.Username)) - { - return name; - } - - if (offset >= 0) - { - entities.Add(new TextEntity((name.Length + offset) + 2, user.Username.Length + 1, new TextEntityTypeMentionName(user.Id))); - } - - return string.Format("{0} (@{1})", name, user.Username); - } - - protected override bool GetHasMoreItems() - { - return _hasMore; - } - - #region Insert - - //protected override void InsertItem(int index, MessageViewModel item) - //{ - // base.InsertItem(index, item); - - // var previous = index > 0 ? this[index - 1] : null; - // var next = index < Count - 1 ? this[index + 1] : null; - - // //if (next is TLMessageEmpty) - // //{ - // // next = index > 1 ? this[index - 2] : null; - // //} - // //if (previous is TLMessageEmpty) - // //{ - // // previous = index < Count - 2 ? this[index + 2] : null; - // //} - - // UpdateSeparatorOnInsert(item, next, index); - // UpdateSeparatorOnInsert(previous, item, index - 1); - - // UpdateAttach(next, item, index + 1); - // UpdateAttach(item, previous, index); - //} - - //protected override void RemoveItem(int index) - //{ - // var next = index > 0 ? this[index - 1] : null; - // var previous = index < Count - 1 ? this[index + 1] : null; - - // UpdateAttach(previous, next, index + 1); - - // base.RemoveItem(index); - - // UpdateSeparatorOnRemove(next, previous, index); - //} - - //private void UpdateSeparatorOnInsert(MessageViewModel item, MessageViewModel previous, int index) - //{ - // if (item != null && previous != null) - // { - // if ((item is TLMessageService itemService && itemService.Action is TLMessageActionAdminLogEvent) || (previous is TLMessageService previousService && previousService.Action is TLMessageActionAdminLogEvent)) - // { - // return; - // } - - // if (item.Id == previous.Id) - // { - // return; - // } - - // var itemDate = Utils.UnixTimestampToDateTime(item.Date); - // var previousDate = Utils.UnixTimestampToDateTime(previous.Date); - // if (previousDate.Date != itemDate.Date) - // { - // var timestamp = (int)Utils.DateTimeToUnixTimestamp(previousDate.Date); - // var service = new TLMessageService - // { - // Date = timestamp, - // FromId = SettingsHelper.UserId, - // HasFromId = true, - // Action = new TLMessageActionDate - // { - // Date = timestamp - // } - // }; - - // base.InsertItem(index + 1, service); - // } - // } - //} - - //private void UpdateSeparatorOnRemove(MessageViewModel next, MessageViewModel previous, int index) - //{ - // if (next is TLMessageService && previous != null) - // { - // var action = ((TLMessageService)next).Action as TLMessageActionDate; - // if (action != null) - // { - // var itemDate = Utils.UnixTimestampToDateTime(action.Date); - // var previousDate = Utils.UnixTimestampToDateTime(previous.Date); - // if (previousDate.Date != itemDate.Date) - // { - // base.RemoveItem(index - 1); - // } - // } - // } - // else if (next is TLMessageService && previous == null) - // { - // var action = ((TLMessageService)next).Action as TLMessageActionDate; - // if (action != null) - // { - // base.RemoveItem(index - 1); - // } - // } - //} - - //private void UpdateAttach(MessageViewModel item, MessageViewModel previous, int index) - //{ - // if (item == null) - // { - // if (previous != null) - // { - // previous.IsLast = true; - // } - - // return; - // } - - // var oldFirst = item.IsFirst; - // var isItemPost = false; - // if (item is TLMessage) isItemPost = ((TLMessage)item).IsPost; - - // if (!isItemPost) - // { - // var attach = false; - // if (previous != null) - // { - // var isPreviousPost = false; - // if (previous is TLMessage) isPreviousPost = ((TLMessage)previous).IsPost; - - // attach = !isPreviousPost && - // !(previous is TLMessageService && !(((TLMessageService)previous).Action is TLMessageActionPhoneCall)) && - // !(previous.IsService()) && - // !(previous is TLMessageEmpty) && - // previous.FromId == item.FromId && - // item.Date - previous.Date < 900; - // } - - // item.IsFirst = !attach; - - // if (previous != null) - // { - // previous.IsLast = item.IsFirst || item.IsService(); - // } - // } - // else - // { - // item.IsFirst = true; - - // if (previous != null) - // { - // previous.IsLast = false; - // } - // } - //} - - #endregion - } - - #region Delegate - - public bool CanBeDownloaded(MessageViewModel message) - { - return false; - } - - public void DownloadFile(MessageViewModel message, File file) - { - } - - public void ReplyToMessage(MessageViewModel message) - { - } - - public void OpenReply(MessageViewModel message) - { - } - - public void OpenFile(File file) - { - } - - public void OpenWebPage(WebPage webPage) - { - } - - public void OpenSticker(Sticker sticker) - { - } - - public void OpenLocation(Location location, string title) - { - } - - public void OpenLiveLocation(MessageViewModel message) - { - - } - - public void OpenInlineButton(MessageViewModel message, InlineKeyboardButton button) - { - } - - public void OpenMedia(MessageViewModel message, FrameworkElement target) - { - } - - public void PlayMessage(MessageViewModel message) - { - } - - public void OpenUsername(string username) - { - } - - public void OpenHashtag(string hashtag) - { - } - - public void OpenBankCardNumber(string number) - { - } - - public void OpenUser(int userId) - { - } - - public void OpenChat(long chatId) - { - } - - public void OpenChat(long chatId, long messageId) - { - } - - public void OpenViaBot(int viaBotUserId) - { - } - - public void OpenUrl(string url, bool untrust) - { - } - - public void SendBotCommand(string command) - { - } - - public bool IsAdmin(int userId) - { - return false; - } - - public void Call(MessageViewModel message) - { - throw new NotImplementedException(); - } - - public void VotePoll(MessageViewModel message, IList options) - { - throw new NotImplementedException(); - } - - public string GetAdminTitle(int userId) - { - return null; - } - - #endregion - } -} diff --git a/Unigram/Unigram/ViewModels/Supergroups/SupergroupMembersViewModel.cs b/Unigram/Unigram/ViewModels/Supergroups/SupergroupMembersViewModel.cs index dfef8e5faf..48a7e85f38 100644 --- a/Unigram/Unigram/ViewModels/Supergroups/SupergroupMembersViewModel.cs +++ b/Unigram/Unigram/ViewModels/Supergroups/SupergroupMembersViewModel.cs @@ -4,6 +4,7 @@ using Telegram.Td.Api; using Unigram.Common; using Unigram.Controls; +using Unigram.Navigation.Services; using Unigram.Services; using Unigram.ViewModels.Delegates; using Unigram.Views.Popups; @@ -172,7 +173,7 @@ private void MemberPromoteExecute(ChatMember member) return; } - NavigationService.Navigate(typeof(SupergroupEditAdministratorPage), new ChatMemberNavigation(chat.Id, member.UserId)); + NavigationService.Navigate(typeof(SupergroupEditAdministratorPage), state: NavigationState.GetChatMember(chat.Id, member.UserId)); } public RelayCommand MemberRestrictCommand { get; } @@ -184,7 +185,7 @@ private void MemberRestrictExecute(ChatMember member) return; } - NavigationService.Navigate(typeof(SupergroupEditRestrictedPage), new ChatMemberNavigation(chat.Id, member.UserId)); + NavigationService.Navigate(typeof(SupergroupEditRestrictedPage), state: NavigationState.GetChatMember(chat.Id, member.UserId)); } public RelayCommand MemberRemoveCommand { get; } diff --git a/Unigram/Unigram/ViewModels/TLMultipleViewModelBase.cs b/Unigram/Unigram/ViewModels/TLMultipleViewModelBase.cs index ff662d2ebc..551ba31106 100644 --- a/Unigram/Unigram/ViewModels/TLMultipleViewModelBase.cs +++ b/Unigram/Unigram/ViewModels/TLMultipleViewModelBase.cs @@ -2,8 +2,8 @@ using System.Linq; using System.Threading.Tasks; using Unigram.Navigation; +using Unigram.Navigation.Services; using Unigram.Services; -using Unigram.Services.Navigation; using Windows.UI.Xaml.Navigation; namespace Unigram.ViewModels @@ -72,19 +72,19 @@ public override INavigationService NavigationService public override async Task OnNavigatedFromAsync(IDictionary pageState, bool suspending) { await base.OnNavigatedFromAsync(pageState, suspending); - await Task.WhenAll(Children.ToList().Select(x => x.OnNavigatedFromAsync(pageState, suspending))); + await Task.WhenAll(Children.Select(x => x.OnNavigatedFromAsync(pageState, suspending))); } public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary state) { await base.OnNavigatedToAsync(parameter, mode, state); - await Task.WhenAll(Children.ToList().Select(x => x.OnNavigatedToAsync(parameter, mode, state))); + await Task.WhenAll(Children.Select(x => x.OnNavigatedToAsync(parameter, mode, state))); } - public override async Task OnNavigatingFromAsync(NavigatingEventArgs args) + public override void OnNavigatingFrom(NavigatingEventArgs args) { - await base.OnNavigatingFromAsync(args); - await Task.WhenAll(Children.ToList().Select(x => x.OnNavigatingFromAsync(args))); + base.OnNavigatingFrom(args); + Children.ForEach(x => x.OnNavigatingFrom(args)); } } diff --git a/Unigram/Unigram/ViewModels/TLViewModelBase.cs b/Unigram/Unigram/ViewModels/TLViewModelBase.cs index e9e28ddd6b..fe4fb42bad 100644 --- a/Unigram/Unigram/ViewModels/TLViewModelBase.cs +++ b/Unigram/Unigram/ViewModels/TLViewModelBase.cs @@ -14,8 +14,6 @@ public class TLViewModelBase : ViewModelBase private readonly ISettingsService _settingsService; private readonly IEventAggregator _aggregator; - private readonly IDispatcherWrapper _dispatcher; - public TLViewModelBase(IProtoService protoService, ICacheService cacheService, ISettingsService settingsService, IEventAggregator aggregator) { _protoService = protoService; diff --git a/Unigram/Unigram/Views/BackgroundPage.xaml.cs b/Unigram/Unigram/Views/BackgroundPage.xaml.cs index ed5b99aecb..56a7e417ac 100644 --- a/Unigram/Unigram/Views/BackgroundPage.xaml.cs +++ b/Unigram/Unigram/Views/BackgroundPage.xaml.cs @@ -205,7 +205,7 @@ private async void Image_DataContextChanged(FrameworkElement sender, DataContext //rectangle.Opacity = pattern.Intensity / 100d if (string.Equals(wallpaper.Document.MimeType, "application/x-tgwallpattern", StringComparison.OrdinalIgnoreCase)) { - rectangle.Fill = new TiledBrush { SvgSource = PlaceholderHelper.GetVectorSurface(ViewModel.ProtoService, big.DocumentValue, pattern.GetForeground()) }; + rectangle.Fill = new TiledBrush { SvgSource = PlaceholderHelper.GetVectorSurface(ViewModel.ProtoService, big.DocumentValue, ViewModel.GetPatternForeground()) }; } else { @@ -278,6 +278,11 @@ public async void UpdateBackground(Background wallpaper) #region Binding + private Background ConvertForeground(BackgroundColor color1, BackgroundColor color2, Background wallpaper) + { + return wallpaper; + } + private Brush ConvertBackground(BackgroundColor color1, BackgroundColor color2, int rotation) { var panel = PatternList.ItemsPanelRoot as ItemsStackPanel; @@ -378,7 +383,7 @@ public void Handle(UpdateFile update) //rectangle.Opacity = pattern.Intensity / 100d; if (string.Equals(wallpaper.Document.MimeType, "application/x-tgwallpattern", StringComparison.OrdinalIgnoreCase)) { - rectangle.Fill = new TiledBrush { SvgSource = PlaceholderHelper.GetVectorSurface(ViewModel.ProtoService, big.DocumentValue, pattern.GetForeground()) }; + rectangle.Fill = new TiledBrush { SvgSource = PlaceholderHelper.GetVectorSurface(ViewModel.ProtoService, big.DocumentValue, ViewModel.GetPatternForeground()) }; } else { diff --git a/Unigram/Unigram/Views/ChatEventLogPage.xaml b/Unigram/Unigram/Views/ChatEventLogPage.xaml new file mode 100644 index 0000000000..59539a29a2 --- /dev/null +++ b/Unigram/Unigram/Views/ChatEventLogPage.xaml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/Unigram/Unigram/Views/ChatEventLogPage.xaml.cs b/Unigram/Unigram/Views/ChatEventLogPage.xaml.cs new file mode 100644 index 0000000000..b395e4f160 --- /dev/null +++ b/Unigram/Unigram/Views/ChatEventLogPage.xaml.cs @@ -0,0 +1,43 @@ +using System.ComponentModel; +using Unigram.Navigation; +using Unigram.ViewModels; +using Unigram.ViewModels.Delegates; +using Windows.UI.Xaml.Navigation; + +namespace Unigram.Views +{ + public sealed partial class ChatEventLogPage : HostedPage, INavigablePage, ISearchablePage, IActivablePage + { + public DialogEventLogViewModel ViewModel => DataContext as DialogEventLogViewModel; + public ChatView View => Content as ChatView; + + public ChatEventLogPage() + { + InitializeComponent(); + + Content = new ChatView(deleg => (DataContext = TLContainer.Current.Resolve(deleg)) as DialogEventLogViewModel); + Header = View.Header; + NavigationCacheMode = NavigationCacheMode.Required; + } + + public void OnBackRequested(HandledEventArgs args) + { + View.OnBackRequested(args); + } + + public void Search() + { + View.Search(); + } + + public void Dispose() + { + View.Dispose(); + } + + public void Activate() + { + View.Activate(); + } + } +} diff --git a/Unigram/Unigram/Views/ChatPage.xaml.cs b/Unigram/Unigram/Views/ChatPage.xaml.cs index 27b2a2c19f..d135d4b0a4 100644 --- a/Unigram/Unigram/Views/ChatPage.xaml.cs +++ b/Unigram/Unigram/Views/ChatPage.xaml.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using Unigram.Navigation; using Unigram.ViewModels; using Unigram.ViewModels.Delegates; diff --git a/Unigram/Unigram/Views/ChatScheduledPage.xaml.cs b/Unigram/Unigram/Views/ChatScheduledPage.xaml.cs index dd9538eefb..154f703f25 100644 --- a/Unigram/Unigram/Views/ChatScheduledPage.xaml.cs +++ b/Unigram/Unigram/Views/ChatScheduledPage.xaml.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using Unigram.Navigation; using Unigram.ViewModels; using Unigram.ViewModels.Delegates; diff --git a/Unigram/Unigram/Views/ChatView.Bubbles.xaml.cs b/Unigram/Unigram/Views/ChatView.Bubbles.xaml.cs index 6816c2b39f..97b5e7475e 100644 --- a/Unigram/Unigram/Views/ChatView.Bubbles.xaml.cs +++ b/Unigram/Unigram/Views/ChatView.Bubbles.xaml.cs @@ -496,7 +496,7 @@ public void Play(IEnumerable items, bool auto, bool audio) presenter.AutoPlay = true; presenter.IsLoopingEnabled = true; presenter.IsHitTestVisible = false; - presenter.Source = new Uri("file:///" + data.File.Local.Path); + presenter.Source = UriEx.GetLocal(data.File.Local.Path); //if (data.Clip && ApiInformation.IsTypePresent("Windows.UI.Composition.CompositionGeometricClip")) //{ diff --git a/Unigram/Unigram/Views/ChatView.Drawers.cs b/Unigram/Unigram/Views/ChatView.Drawers.cs index 769419612c..5b7bb2fadd 100644 --- a/Unigram/Unigram/Views/ChatView.Drawers.cs +++ b/Unigram/Unigram/Views/ChatView.Drawers.cs @@ -27,7 +27,7 @@ private void Sticker_ContextRequested(UIElement sender, ContextRequestedEventArg flyout.CreateFlyoutItem(ViewModel.StickerFaveCommand, (Sticker)sticker, Strings.Resources.AddToFavorites, new FontIcon { Glyph = Icons.Favorite }); } - if (!ViewModel.IsSchedule) + if (ViewModel.Type == ViewModels.DialogType.Normal) { var chat = ViewModel.Chat; if (chat == null) @@ -61,7 +61,7 @@ private void Animation_ContextRequested(UIElement sender, ContextRequestedEventA flyout.CreateFlyoutItem(ViewModel.AnimationSaveCommand, animation, Strings.Resources.SaveToGIFs, new FontIcon { Glyph = Icons.Animations }); } - if (!ViewModel.IsSchedule) + if (ViewModel.Type == ViewModels.DialogType.Normal) { var chat = ViewModel.Chat; if (chat == null) diff --git a/Unigram/Unigram/Views/ChatView.xaml b/Unigram/Unigram/Views/ChatView.xaml index 7b22036ef7..68fe59bfa2 100644 --- a/Unigram/Unigram/Views/ChatView.xaml +++ b/Unigram/Unigram/Views/ChatView.xaml @@ -330,67 +330,57 @@ - + - + + + + + + + + + + + + + + + + + Grid.Column="2"/> + Grid.Column="3"/>