public PeopleTopicView() { InitializeComponent(); var click = Observable .FromEventPattern <ItemClickEventArgs>(PeopleTopicsList, nameof(PeopleTopicsList.ItemClick)) .Select(x => x.EventArgs.ClickedItem as TopicModel) .ObserveOnCoreDispatcher() .Subscribe(x => PageStack.Next("Right", "Right", typeof(RepliesAndTopicView), x.Id)); NotifyData.LoadDataTask = async count => { var html = await ApiClient.GetFavoriteTopics(NotifyData.CurrentPage); var dom = new HtmlParser().ParseDocument(html); return(new PagesBaseModel <TopicModel> { Pages = DomParse.ParseMaxPage(dom), Entity = DomParse.ParseTopics(dom) ?? new List <TopicModel>() }); }; _events = new List <IDisposable> { click }; }
public CollectedTopicsFragment() { InitializeComponent(); LabelPanel.ItemsSource = _tabs; async Task <IEnumerable <TopicModel> > loadData() { var model = LabelPanel.SelectedItem as CollectedListModel; var html = await ApiClient.GetTopicsWithTab(model.Name); var dom = new HtmlParser().ParseDocument(html); return(DomParse.ParseTopics(dom)); } var selectionChanged = Observable .FromEventPattern <SelectionChangedEventArgs>(LabelPanel, nameof(LabelPanel.SelectionChanged)) .SelectMany(x => loadData()) .Retry(10) .ObserveOnCoreDispatcher() .Subscribe(x => { News.Clear(); foreach (var item in x) { News.Add(item); } }); var click = Observable.FromEventPattern <ItemClickEventArgs>(NewsList, nameof(NewsList.ItemClick)) .Select(x => x.EventArgs.ClickedItem as TopicModel) .ObserveOnCoreDispatcher() .Subscribe(x => PageStack.Next("Left", "Right", typeof(RepliesAndTopicView), x.Id)); var refresh = Observable.FromEventPattern <TappedRoutedEventArgs>(Refresh, nameof(Refresh.Tapped)) .SelectMany(x => loadData()) .Retry(10) .ObserveOnCoreDispatcher() .Subscribe(x => { News.Clear(); foreach (var item in x) { News.Add(item); } }); LabelPanel.SelectedIndex = 0; this.Unloaded += (s, e) => { selectionChanged.Dispose(); click.Dispose(); refresh.Dispose(); }; }
public PeopleNotificationView() { InitializeComponent(); var click = Observable .FromEventPattern <ItemClickEventArgs>(NotificationList, nameof(NotificationList.ItemClick)) .Select(x => x.EventArgs.ClickedItem as NotificationModel) .ObserveOnCoreDispatcher() .Subscribe(x => PageStack.Next("Right", "Right", typeof(RepliesAndTopicView), new Tuple <int, int>(x.Topic.Id, x.ReplyFloor))); NotifyData.LoadDataTask = async count => { var html = await ApiClient.GetNotifications(NotifyData.CurrentPage); var dom = new HtmlParser().ParseDocument(html); var header = dom.GetElementById("Main").QuerySelector("div.header"); var messages = int.Parse(header.QuerySelector("strong.gray")?.TextContent ?? "0"); var pages = messages % 10 != 0 ? messages / 10 + 1 : messages / 10; var notifications = dom.GetElementById("Main").GetElementsByClassName("cell") .Where(node => node.Id != null).Select( node => { var hrefs = node.QuerySelectorAll("a"); var linkPieces = hrefs[2].GetAttribute("href").Split('/', '#'); return(new NotificationModel { Topic = new TopicModel { Id = int.Parse(linkPieces[2]) }, Member = new MemberModel { Image = node.QuerySelector("img").GetAttribute("src") }, Id = int.Parse(node.Id.Replace("n_", "")), Title = node.QuerySelector("span.fade").TextContent, ReplyDate = node.QuerySelector("span.snow").TextContent, Content = node.QuerySelector("div.payload")?.TextContent, ReplyFloor = int.Parse(linkPieces[3].Replace("reply", "")) }); }); return(new PagesBaseModel <NotificationModel> { Pages = pages, Entity = notifications }); }; _events = new List <IDisposable> { click }; }
public NodesFragment() { InitializeComponent(); var loaded = Observable.FromEventPattern <RoutedEventArgs>(AllNodesFragment, nameof(AllNodesFragment.Loaded)) .SelectMany(x => ApiClient.GetNodes()) .Retry(10) .Select(x => { return(x.GroupBy(y => { var begin = y.Title.Trim().First(); if (begin >= 'A' && begin <= 'Z') { return $"{begin}"; } if (begin >= 'a' && begin <= 'z') { return $"{(char)(begin + 'A' - 'a')}"; } if (begin >= 0 && begin <= 256) { return "0-9"; } var first = PinYin.GetFirstPinyin(begin.ToString()); return string.IsNullOrEmpty(first) ? "其他" : first; }) .Select(y => new NodeInGroup { Key = y.Key, NodeContent = y.ToList() }) .OrderBy(y => y.Key)); }) .ObserveOnCoreDispatcher() .Subscribe(x => { SortNodesCVS.Source = x; OutView.ItemsSource = SortNodesCVS.View.CollectionGroups; InView.ItemsSource = SortNodesCVS.View; }); var click = Observable.FromEventPattern <ItemClickEventArgs>(InView, nameof(InView.ItemClick)) .Select(x => x.EventArgs.ClickedItem as NodeModel) .ObserveOnCoreDispatcher() .Subscribe(x => PageStack.Next("Left", "Right", typeof(OneNodeTopicsView), x)); this.Unloaded += (s, e) => { loaded.Dispose(); click.Dispose(); }; }
public PeopleNodeView() { InitializeComponent(); async Task <IEnumerable <NodeModel> > loadData() { var html = await ApiClient.GetFavoriteNodes(); return(new HtmlParser().ParseDocument(html).GetElementById("my-nodes").GetElementsByClassName("grid_item") .Select( child => { var strs = child.TextContent.Split(' '); return new NodeModel { Id = int.Parse(child.Id.Replace("n_", "")), Name = child.GetAttribute("href").Replace("/go/", ""), Image = child.QuerySelector("img").GetAttribute("src"), Title = string.Join("", strs.Take(strs.Length - 1)), Topics = int.Parse(strs.Last()) }; })); } var load = Observable.FromEventPattern <RoutedEventArgs>(PeopleNodePage, nameof(PeopleNodePage.Loaded)) .SelectMany(x => loadData()) .Retry(10) .ObserveOnCoreDispatcher() .Subscribe(x => PeopleNodeList.ItemsSource = x); var click = Observable .FromEventPattern <ItemClickEventArgs>(PeopleNodeList, nameof(PeopleNodeList.ItemClick)) .Select(x => x.EventArgs.ClickedItem as NodeModel) .ObserveOnCoreDispatcher() .Subscribe(x => PageStack.Next("Right", "Right", typeof(OneNodeTopicsView), x)); _events = new List <IDisposable> { load, click }; }
public UserInfoFragment() { InitializeComponent(); async Task <PersonCenterModel> loadData() { var html = await ApiClient.GetMainPage(); var right = new HtmlParser().ParseDocument(html).GetElementById("Rightbar"); var tables = right.QuerySelectorAll("table"); var spans = tables[1].QuerySelectorAll("span.bigger"); return(new PersonCenterModel { Member = new MemberModel { Image = tables[0].QuerySelector("img").GetAttribute("src"), Username = tables[0].QuerySelector("span.bigger").TextContent }, CollectedNodes = spans[0].TextContent, CollectedTopics = spans[1].TextContent, NoticePeople = spans[2].TextContent, Money = right.QuerySelector("div#money").QuerySelector("a").InnerHtml, Notifications = right.QuerySelector("a[href='/notifications']").TextContent.Split(' ') .FirstOrDefault(), IsNotChecked = right.QuerySelector("a[href='/mission/daily']") != null }); } var checkIn = Observable.FromEventPattern <TappedRoutedEventArgs>(CheckInItem, nameof(CheckInItem.Tapped)) .Select(async x => { var html = await ApiClient.GetCheckInInformation(); var href = new HtmlParser().ParseDocument(html).GetElementById("Main").QuerySelector("input") .GetAttribute("onclick").Replace("location.href = '", "").Replace("';", "").Trim(); if (href.Contains("/balance")) { return(CheckInStatus.Gone); } var r = await ApiClient.CheckIn($"https://www.v2ex.com{href}", $"https://www.v2ex.com{href}"); return(CheckInStatus.Success); }) .ObserveOnCoreDispatcher() .Subscribe(async x => { switch (await x) { case CheckInStatus.Gone: Toast.ShowTips("已经签到"); break; case CheckInStatus.Success: Toast.ShowTips("签到成功"); Observable.FromAsync(y => loadData()) .Retry(10) .ObserveOnCoreDispatcher() .Subscribe(y => People = y); break; case CheckInStatus.Failure: Toast.ShowTips("签到失败"); break; } }, ex => Toast.ShowTips("签到失败")); var cancel = Observable.FromEventPattern <TappedRoutedEventArgs>(CancelItem, nameof(CancelItem.Tapped)) .ObserveOnCoreDispatcher() .Subscribe(x => { if (Window.Current.Content is ActivityContainer frame) { PageStack.Clear(); frame.ClearBackStack(); frame.Navigate(typeof(UserLoginView)); } }); var write = Observable.FromEventPattern <TappedRoutedEventArgs>(WriteItem, nameof(WriteItem.Tapped)) .Subscribe(x => PageStack.Next("Left", "Right", typeof(WriteTopicView), null)); var user = Observable.FromEventPattern <TappedRoutedEventArgs>(UserItem, nameof(UserItem.Tapped)) .Subscribe(x => PageStack.Next("Left", "Right", typeof(MemberView), People.Member.Username)); var money = Observable.FromEventPattern <TappedRoutedEventArgs>(MoneyItem, nameof(MoneyItem.Tapped)) .Subscribe(x => PageStack.Next("Left", "Right", typeof(MoneyDetailView), null)); var collectTopic = Observable .FromEventPattern <TappedRoutedEventArgs>(CollectTopicItem, nameof(CollectTopicItem.Tapped)) .Subscribe(x => PageStack.Next("Left", "Right", typeof(PeopleTopicView), Convert.ToInt32(People.CollectedTopics))); var collectNode = Observable.FromEventPattern <TappedRoutedEventArgs>(CollectNodeItem, nameof(CollectNodeItem.Tapped)) .Subscribe(x => PageStack.Next("Left", "Right", typeof(PeopleNodeView), null)); var message = Observable.FromEventPattern <TappedRoutedEventArgs>(MessageItem, nameof(MessageItem.Tapped)) .Subscribe(x => PageStack.Next("Left", "Right", typeof(PeopleNotificationView), null)); var follower = Observable.FromEventPattern <TappedRoutedEventArgs>(FollowerItem, nameof(FollowerItem.Tapped)) .Subscribe(x => PageStack.Next("Left", "Right", typeof(PeopleFollowerView), null)); var loadInformation = Observable .FromEventPattern <RoutedEventArgs>(UserInformationFragment, nameof(UserInformationFragment.Loaded)) .SelectMany(x => loadData()) .Retry(10) .ObserveOnCoreDispatcher() .Subscribe(x => People = x); var refresh = Observable.FromEventPattern <TappedRoutedEventArgs>(Refresh, nameof(Refresh.Tapped)) .SelectMany(x => loadData()) .Retry(10) .ObserveOnCoreDispatcher() .Subscribe(x => People = x); this.Unloaded += (s, e) => { checkIn.Dispose(); cancel.Dispose(); write.Dispose(); user.Dispose(); money.Dispose(); collectTopic.Dispose(); collectNode.Dispose(); message.Dispose(); follower.Dispose(); loadInformation.Dispose(); refresh.Dispose(); }; }
public async Task <Paragraph> HtmlToXaml(string richText) { const string baseUrl = "https://www.v2ex.com"; const string unsafeUrl = "http://www.v2ex.com"; var blocks = new[] { "</p>", "</h1>", "</h2>", "</h2>", "</h3>", "</h4>", "</h5>", "</h6>", "</li>", "</blockquote>" }; foreach (var block in blocks) { richText = richText.Replace(block, $"\\r{block}"); } var dom = await new HtmlParser().ParseDocumentAsync(richText); await Task.Run(() => GetElements(dom.Body)); var paragraph = new Paragraph(); foreach (var render in _renderList) { Run run = null; Inline inline = null; var isCode = false; foreach (var element in render) { switch (element.NodeName) { case "IMG": var img = element as IHtmlImageElement; //var source = img.Source.StartsWith("about:") // ? $"http:{img.Source.Replace("about:", "")}" // : img.Source; var source = img.Source; if (source.StartsWith("about:///")) { source = baseUrl + source.Replace("about://", ""); } else if (source.StartsWith("about:")) { source = $"https:{source.Replace("about:", "")}"; } if (!Uri.IsWellFormedUriString(source, UriKind.Absolute)) { break; } var bitmap = new Image { Source = new BitmapImage(new Uri(source)), Stretch = Stretch.None }; var viewBox = new Viewbox { Child = bitmap, StretchDirection = StretchDirection.DownOnly }; viewBox.Tapped += (v, s) => { PageStack.Next("Right", "Right", typeof(ImageViewerPage), source); }; paragraph.Inlines.Add(new InlineUIContainer { Child = viewBox }); goto Out; case "BR": paragraph.Inlines.Add(new LineBreak()); goto Out; case "#text": run = new Run { Text = element.TextContent }; inline = run; break; case "A": var elementAnchor = element as IHtmlAnchorElement; var href = elementAnchor.Href.StartsWith("about://") ? $"{baseUrl}{elementAnchor.Href.Replace("about://", "")}" : elementAnchor.Href; try { var hrefUri = new Uri(href); var baseUri = new Uri(baseUrl); var unsafeUri = new Uri(unsafeUrl); if (hrefUri.Host == baseUri.Host || hrefUri.Host == unsafeUri.Host) { var segments = hrefUri.Segments; var hyperlink = new Hyperlink { UnderlineStyle = UnderlineStyle.None, Foreground = new SolidColorBrush(Colors.RoyalBlue) }; hyperlink.Click += (sender, e) => { if (segments[1] == "member/") { PageStack.Next("Right", "Right", typeof(MemberView), segments[2]); } else if (segments[1] == "t/") { PageStack.Next("Right", "Right", typeof(RepliesAndTopicView), Convert.ToInt32(segments[2])); } }; hyperlink.Inlines.Add(inline); inline = hyperlink; } else { var hyperlink = new Hyperlink { UnderlineStyle = UnderlineStyle.None, Foreground = new SolidColorBrush(Colors.RoyalBlue), NavigateUri = new Uri(href) }; hyperlink.Inlines.Add(inline); inline = hyperlink; } } catch { inline = new Run { Text = href }; } break; case "STRONG": var bold = new Bold(); bold.Inlines.Add(inline); inline = bold; break; case "H1": case "H2": case "H3": case "H4": case "H5": case "H6": var h = new Bold { FontSize = Convert.ToInt32(element.NodeName.Substring(1)) + 15 }; h.Inlines.Add(inline); inline = h; break; case "CODE": break; case "PRE": var code = new Italic(); code.Inlines.Add(inline); inline = code; isCode = true; break; case "LI": var span = new Span(); span.Inlines.Add(inline); inline = span; break; } } if (!isCode) { run.Text = run.Text.Replace("\n", "").Replace("\\r", "\n"); } paragraph.Inlines.Add(inline); Out :; } return(paragraph); }
public MainPage() { InitializeComponent(); RightPart = RightFrame; LeftPart = LeftPivot; PageStack.Next("Right", "Right", typeof(BlankPage), null); SystemNavigationManager.GetForCurrentView().BackRequested += (sender, el) => { el.Handled = true; if (PageStack.CanGoBack) { PageStack.Back(); } }; var leftChanged = Observable .FromEventPattern <SizeChangedEventArgs>(LeftPivot, nameof(LeftPivot.SizeChanged)) .ObserveOnCoreDispatcher() .Subscribe(x => { var headerpanel = FindVisualChildren <PivotHeaderPanel>(LeftPivot).ToList(); var totalwidth = LeftPivot.ActualWidth; headerpanel[0].Width = totalwidth; var items = FindVisualChildren <PivotHeaderItem>(headerpanel[0]).ToList(); for (var i = 0; i < items.Count; i++) { items[i].Width = totalwidth / items.Count - 1; } }); var rightChanged = Observable .FromEventPattern <SizeChangedEventArgs>(RightFrame, nameof(RightFrame.SizeChanged)) .ObserveOnCoreDispatcher() .Subscribe(x => { if (RootGrid.ActualWidth > 600) { LeftPivot.Width = 500; LeftPivot.Visibility = Visibility.Visible; RightFrame.Visibility = Visibility.Visible; RightFrame.SetValue(RelativePanel.RightOfProperty, LeftPivot); LeftPivot.SetValue(RelativePanel.AlignBottomWithPanelProperty, true); LeftPivot.SetValue(RelativePanel.AlignTopWithPanelProperty, true); LeftPivot.SetValue(RelativePanel.AlignLeftWithPanelProperty, true); LeftPivot.SetValue(RelativePanel.AlignRightWithPanelProperty, false); RightFrame.SetValue(RelativePanel.AlignBottomWithPanelProperty, true); RightFrame.SetValue(RelativePanel.AlignTopWithPanelProperty, true); RightFrame.SetValue(RelativePanel.AlignRightWithPanelProperty, true); RightFrame.SetValue(RelativePanel.AlignLeftWithPanelProperty, false); } else { LeftPivot.Width = double.NaN; if (RightFrame.CanGoBack) { LeftPivot.Visibility = Visibility.Visible; RightFrame.Visibility = Visibility.Collapsed; } else { LeftPivot.Visibility = Visibility.Collapsed; RightFrame.Visibility = Visibility.Visible; } LeftPivot.SetValue(RelativePanel.AlignBottomWithPanelProperty, true); LeftPivot.SetValue(RelativePanel.AlignTopWithPanelProperty, true); LeftPivot.SetValue(RelativePanel.AlignLeftWithPanelProperty, true); LeftPivot.SetValue(RelativePanel.AlignRightWithPanelProperty, true); RightFrame.SetValue(RelativePanel.AlignBottomWithPanelProperty, true); RightFrame.SetValue(RelativePanel.AlignTopWithPanelProperty, true); RightFrame.SetValue(RelativePanel.AlignRightWithPanelProperty, true); RightFrame.SetValue(RelativePanel.AlignLeftWithPanelProperty, true); } }); _events = new List <IDisposable> { leftChanged, rightChanged }; }
public RepliesAndTopicView() { InitializeComponent(); var controls = new List <Control> { Send, ReplyText }; var send = Observable.FromEventPattern <RoutedEventArgs>(Send, nameof(Send.Click)) .Select(x => { controls.ForEach(c => c.IsEnabled = false); return(x); }) .Select(async x => { var content = ReplyText.Text; if (string.IsNullOrEmpty(content)) { return(ReplyStatus.TextEmpty); } var html = await ApiClient.GetTopicInformation(_id); var once = new HtmlParser().ParseDocument(html).QuerySelector("input[name='once']").GetAttribute("value"); var pramas = new Dictionary <string, string> { { "content", content }, { "once", once } }; var text = await ApiClient.ReplyTopic($"https://www.v2ex.com/t{_id}", new FormUrlEncodedContent(pramas), _id); if (text.Contains("你回复过于频繁了")) { return(ReplyStatus.Ban); } return(ReplyStatus.Success); }) .ObserveOnCoreDispatcher() .Subscribe(async x => { try { switch (await x) { case ReplyStatus.Ban: Toast.ShowTips("您被禁言1800秒"); break; case ReplyStatus.TextEmpty: Toast.ShowTips("内容不能为空"); break; case ReplyStatus.Success: Toast.ShowTips("回复成功"); ReplyText.Text = ""; break; } } catch { Toast.ShowTips("回复失败"); } finally { controls.ForEach(c => c.IsEnabled = true); } }); var at = Observable.FromEventPattern <TappedRoutedEventArgs>(UsernamePanel, nameof(UsernamePanel.Tapped)) .ObserveOnCoreDispatcher() .Subscribe(x => ReplyText.Text += $"@{(string) (x.Sender as TextBlock).Tag} "); var collect = Observable .FromEventPattern <TappedRoutedEventArgs>(CollectedPanel, nameof(CollectedPanel.Tapped)) .ObserveOnCoreDispatcher() .Subscribe(async x => { try { var html = await ApiClient.GetTopicInformation(_id); var url = ""; var regexFav = new Regex("<a href=\"(.*)\">加入收藏</a>"); var regexUnFav = new Regex("<a href=\"(.*)\">取消收藏</a>"); if (regexFav.IsMatch(html)) { url = regexFav.Match(html).Groups[1].Value; } if (regexUnFav.IsMatch(html)) { url = regexUnFav.Match(html).Groups[1].Value; } await ApiClient.OnlyGet($"https://www.v2ex.com{url}"); if (Topic.Collect == "加入\n收藏") { Topic.Collect = "已\n收藏"; Toast.ShowTips("收藏成功"); } else { Topic.Collect = "加入\n收藏"; Toast.ShowTips("取消收藏成功"); } } catch { Toast.ShowTips("操作失败"); } }); var tImageTap = Observable.FromEventPattern <TappedRoutedEventArgs>(TImagePanel, nameof(TImagePanel.Tapped)) .ObserveOnCoreDispatcher() .Subscribe(x => PageStack.Next("Right", "Right", typeof(MemberView), TImagePanel.Tag)); var copyLink = Observable .FromEventPattern <TappedRoutedEventArgs>(CopyLinkPanel, nameof(CopyLinkPanel.Tapped)) .ObserveOnCoreDispatcher() .Subscribe(x => { var clipBoardText = new DataPackage(); clipBoardText.SetText(Topic.Url); Clipboard.SetContent(clipBoardText); Toast.ShowTips("已复制到剪贴板"); }); Replies.LoadDataTask = async count => { var html = await ApiClient.GetRepliesAndTopicContent(_id, Replies.CurrentPage); var main = new HtmlParser().ParseDocument(html).GetElementById("Main"); if (Replies.MaxPage == 0) { try { var match = Regex.Match(html, "([0-9]{1,}) 条回复"); var replyString = match.Success ? match.Groups[1].Value : "0"; var maxReply = int.Parse(replyString); var node = main.QuerySelector("div.header"); var topic = new TopicModel { Id = _id, Member = new MemberModel { Image = node.QuerySelector("img").GetAttribute("src"), Username = node.QuerySelector("a").GetAttribute("href").Split('/')[2] }, Title = node.QuerySelector("h1").TextContent, Url = $"https://www.v2ex.com/t/{_id}", Postscript = main.QuerySelectorAll("div.subtle").Select(subtle => new TopicModel { Content = subtle.QuerySelector("div.topic_content").InnerHtml, LastReply = subtle.QuerySelector("span.fade").TextContent }).ToList(), Collect = main.TextContent.Contains("加入收藏") ? "加入\n收藏" : "已\n收藏", Content = main.QuerySelector("div.topic_content")?.InnerHtml, Replies = maxReply, CreateDate = node.QuerySelector("small.gray").TextContent.Split('·')[1] }; Topic = topic; } catch { Toast.ShowTips("加载失败,可能无权访问该主题"); return(new PagesBaseModel <ReplyModel> { Pages = 0, Entity = new List <ReplyModel>() }); } } var replies = main.QuerySelectorAll("table").Where(table => table.ParentElement.Id != null) .Select(table => { var spans = table.QuerySelectorAll("span"); return(new ReplyModel { Id = int.Parse(table.ParentElement.Id.Replace("r_", "")), Avater = table.QuerySelector("img").GetAttribute("src"), Username = table.QuerySelector("strong").TextContent, Content = table.QuerySelector("div.reply_content").InnerHtml.Trim(), Thanks = spans.Length == 3 ? int.Parse(spans[2].TextContent.Replace("♥ ", "")) : 0, Floor = $"#{spans[0].TextContent}", ReplyDate = spans[1].TextContent, IsLz = Topic.Member.Username == table.QuerySelector("strong").TextContent }); }); return(new PagesBaseModel <ReplyModel> { Pages = Topic.Replies % 100 == 0 ? Topic.Replies / 100 : Topic.Replies / 100 + 1, Entity = replies }); }; _events = new List <IDisposable> { send, at, collect, tImageTap, copyLink }; }
public MemberView() { InitializeComponent(); async Task <MemberModel> loadData() { var html = await ApiClient.GetMemberInformation(_username); var cell = new HtmlParser().ParseDocument(html).GetElementById("Main").QuerySelector("div.cell"); var inputs = cell.QuerySelectorAll("input"); if (!inputs.Any()) { return new MemberModel { Image = cell.QuerySelector("img").GetAttribute("src"), Username = cell.QuerySelector("h1").TextContent } } ; return(new MemberModel { Image = cell.QuerySelector("img").GetAttribute("src"), Notice = "https://www.v2ex.com" + inputs[0].GetAttribute("onclick").Split('\'')[3], IsNotice = inputs[0].GetAttribute("value"), Block = "https://www.v2ex.com" + inputs[1].GetAttribute("onclick").Split('\'')[3], IsBlock = inputs[1].GetAttribute("value"), Username = cell.QuerySelector("h1").TextContent }); } var load = Observable.FromEventPattern <RoutedEventArgs>(MemberPage, nameof(MemberPage.Loaded)) .SelectMany(x => loadData()) .Retry(10) .ObserveOnCoreDispatcher() .Subscribe(x => Member = x, x => { }); var notice = Observable.FromEventPattern <RoutedEventArgs>(Notice, nameof(Notice.Tapped)) .Select(async x => await ApiClient.OnlyGet(Member.Notice)) .ObserveOnCoreDispatcher() .Subscribe(x => Member.IsNotice = "取消特别关注", (Exception ex) => { }); var block = Observable.FromEventPattern <RoutedEventArgs>(Block, nameof(Block.Tapped)) .Select(async x => await ApiClient.OnlyGet(Member.Block)) .ObserveOnCoreDispatcher() .Subscribe(x => Member.IsBlock = "取消Block", (Exception ex) => { }); var info = Observable.FromEventPattern <ItemClickEventArgs>(MemberInfoList, nameof(MemberInfoList.ItemClick)) .ObserveOnCoreDispatcher() .Subscribe(x => { var item = x.EventArgs.ClickedItem as TopicModel; PageStack.Next("Right", "Right", typeof(RepliesAndTopicView), item.Id); }); Topics.LoadDataTask = async count => { var html = await ApiClient.GetTopicsByUsername(_username, Topics.CurrentPage); if (html.Contains("主题列表被隐藏")) { ListHiddenPanel.Visibility = Visibility.Visible; return(new PagesBaseModel <TopicModel> { Pages = 0, Entity = new List <TopicModel>() }); } var dom = new HtmlParser().ParseDocument(html); var pages = DomParse.ParseMaxPage(dom); var topics = dom.GetElementById("Main").GetElementsByClassName("cell item").Select(node => { var hrefs = node.QuerySelectorAll("a"); var topic = new TopicModel { Title = hrefs[0].TextContent, NodeName = hrefs[1].TextContent, Member = new MemberModel { Username = hrefs[2].TextContent }, Id = int.Parse(new Regex("/t/([0-9]*)").Match(hrefs[0].GetAttribute("href")).Groups[1].Value) }; if (hrefs.Length == 5) { topic.LastUsername = $"最后回复者 :{hrefs[3].TextContent}"; topic.Replies = int.Parse(hrefs[4].TextContent); var last = node.QuerySelector("span.topic_info").TextContent.Split('•')[2].Trim(); last = last.Contains("最后回复") ? "" : last; topic.LastReply = $"时间 : {last.Trim()}"; } return(topic); }); return(new PagesBaseModel <TopicModel> { Pages = pages, Entity = topics }); }; Notifications.LoadDataTask = async count => { var html = await ApiClient.GetRepliesByUsername(_username, Notifications.CurrentPage); var dom = new HtmlParser().ParseDocument(html); var main = dom.GetElementById("Main"); var nodes = main.QuerySelectorAll("div.dock_area"); var replies = main.QuerySelectorAll("div.reply_content"); var pages = DomParse.ParseMaxPage(dom); var notifications = nodes.Select( (node, i) => { var href = node.QuerySelector("a"); var span = node.QuerySelector("span"); return(new NotificationModel { Title = href.TextContent, Content = replies[i].TextContent, ReplyDate = span.TextContent }); }); return(new PagesBaseModel <NotificationModel> { Pages = pages, Entity = notifications }); }; _events = new List <IDisposable> { load, notice, block, info }; }
public OneNodeTopicsView() { InitializeComponent(); var click = Observable .FromEventPattern <ItemClickEventArgs>(NodeTopcisList, nameof(NodeTopcisList.ItemClick)) .Select(x => x.EventArgs.ClickedItem as TopicModel) .ObserveOnCoreDispatcher() .Subscribe(x => PageStack.Next("Right", "Right", typeof(RepliesAndTopicView), x.Id)); var collect = Observable.FromEventPattern <TappedRoutedEventArgs>(CollectNode, nameof(CollectNode.Tapped)) .SelectMany(x => ApiClient.GetNodeInformation(Node.Name)) .Select(html => { var regexFav = new Regex("<a href=\"(.*)\">加入收藏</a>"); var regexUnFav = new Regex("<a href=\"(.*)\">取消收藏</a>"); var url = ""; if (regexFav.IsMatch(html)) { url = regexFav.Match(html).Groups[1].Value; } if (regexUnFav.IsMatch(html)) { url = regexUnFav.Match(html).Groups[1].Value; } return(url); }) .SelectMany(url => ApiClient.OnlyGet($"https://www.v2ex.com{url}")) .ObserveOnCoreDispatcher() .Subscribe(x => Node.IsCollect = Node.IsCollect == "加入收藏" ? "取消收藏" : "加入收藏", ex => { }); NotifyData.LoadDataTask = async count => { var html = await ApiClient.GetTopicsWithPageN(Node.Name, NotifyData.CurrentPage); var dom = new HtmlParser().ParseDocument(html); if (NotifyData.MaxPage == 0) { var header = dom.GetElementById("Main").QuerySelector("div.node_header"); Node = new NodeModel { Topics = Convert.ToInt32(header.QuerySelector("strong").TextContent), IsCollect = header.QuerySelector("a").TextContent, Cover = header.QuerySelector("img") == null ? "ms-appx:///Assets/default.png" : header.QuerySelector("img").GetAttribute("src"), Title = Node.Title, Name = Node.Name }; } var topics = dom.GetElementById("TopicsNode").Children.Select(node => { var hrefs = node.QuerySelectorAll("a"); var imgs = node.QuerySelector("img.avatar"); var topic = new TopicModel { Title = hrefs[1].TextContent, Member = new MemberModel { Username = hrefs[2].TextContent, Image = imgs.GetAttribute("src") }, Id = int.Parse(node.ClassName.Split('_').Last()) }; if (hrefs.Length == 5) { topic.LastUsername = $"最后回复者 :{hrefs[3].TextContent}"; topic.Replies = int.Parse(hrefs[4].TextContent); var last = node.GetElementsByClassName("topic_info").First().TextContent.Split('•')[1].Trim(); if (last.Length > 12) { var timeSpan = DateTime.Now - DateTime.Parse(last.Insert(10, " ")); last = $"{(int) timeSpan.TotalDays}天"; } topic.LastReply = $"时间 : {last.Trim()}"; } return(topic); }); return(new PagesBaseModel <TopicModel> { Pages = Node.Topics % 20 == 0 ? Node.Topics / 20 : Node.Topics / 20 + 1, Entity = topics }); }; _events = new List <IDisposable> { collect, click }; }