/// <summary>
 /// Сохранить данные борды.
 /// </summary>
 /// <param name="data">Данные.</param>
 /// <returns>Таск.</returns>
 public async Task SaveBoardPage(BoardPageTree data)
 {
     var fileName = string.Format(CacheConsts.CacheFileTemplates[CacheConsts.Board], Services.GetServiceOrThrow<ILinkHashService>().GetLinkHash(data.Link));
     var file = await GetCacheFile(fileName);
     var folder = await GetCacheFolder();
     await WriteCacheXmlObject(file, folder, data, true);
 }
 /// <summary>
 /// Парсить данные страницы борды.
 /// </summary>
 /// <param name="data">Данные.</param>
 /// <param name="link">Ссылка.</param>
 /// <returns>Результат.</returns>
 public BoardPageTree ParseBoardPage(BoardEntity2 data, BoardPageLink link)
 {
     var entity = GetEntityTree(data);
     var result = new BoardPageTree()
     {
         Extensions = new List<BoardPageTreeExtension>(),
         LastModified = null,
         Link = link,
         ParentLink = new RootLink()
         {
             Engine = CoreConstants.Engine.Makaba
         },
         PageNumber = link.Page,
         Threads = new List<ThreadPreviewTree>(),
         MaxPage = entity.Pages != null ? entity.Pages.OrderByDescending(p => p).Select(p => (int?)p).FirstOrDefault() : null,
     };
     result.Extensions.Add(new MakabaBoardPageExtension()
     {
         Entity = entity
     });
     if (data.Threads != null)
     {
         foreach (var t in data.Threads)
         {
             var posts = t.Posts.OrderBy(p => p.Number.TryParseWithDefault()).ToArray();
             var tnum = posts.First().Number.TryParseWithDefault();
             var tlink = new ThreadLink()
             {
                 Engine = CoreConstants.Engine.Makaba,
                 Board = link.Board,
                 Thread = tnum,
             };
             var thread = new ThreadPreviewTree()
             {
                 Extensions = new List<PostTreeCollectionExtension>(),
                 Link = tlink,
                 ParentLink = link,
                 ImageCount =
                     t.ImagesCount.TryParseWithDefault() +
                     posts.Sum(p => (p.Files ?? new BoardPostFile2[0]).Length),
                 Omit = t.PostsCount.TryParseWithDefault(),
                 OmitImages = t.ImagesCount.TryParseWithDefault(),
                 ReplyCount = Math.Max(t.PostsCount.TryParseWithDefault() + posts.Count(), 0),
                 Posts = posts.Select(p => Parse(p, tlink, true)).ToList(),
             };
             for (var i = 0; i < thread.Posts.Count; i++)
             {
                 thread.Posts[i].Flags |= PostFlags.ThreadPreview;
                 if (i == 0)
                 {
                     thread.Posts[i].Counter = 1;
                     thread.Posts[i].Flags |= PostFlags.ThreadPreviewOpPost;
                 }
                 else
                 {
                     var ni = thread.Posts.Count - i;
                     thread.Posts[i].Counter = thread.ReplyCount - ni;
                 }
             }
             result.Threads.Add(thread);
         }
     }
     return result;
 }
 /// <summary>
 /// Конструктор.
 /// </summary>
 /// <param name="data">Данные.</param>
 public BoardPageViewModel(BoardPageTree data)
 {
     if (data == null) throw new ArgumentNullException(nameof(data));
     this.data = data;
 }