Toasts – Blame information for rev 53
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
41 | office | 1 | using System; |
2 | using System.Collections.Concurrent; |
||
44 | office | 3 | using System.ComponentModel; |
53 | office | 4 | using System.Diagnostics; |
41 | office | 5 | using System.Drawing; |
44 | office | 6 | using System.Runtime.InteropServices; |
41 | office | 7 | using System.Threading; |
8 | using System.Threading.Tasks; |
||
9 | using System.Threading.Tasks.Dataflow; |
||
10 | using System.Windows.Forms; |
||
11 | using Markdig; |
||
12 | using Markdig.Renderers; |
||
13 | |||
14 | namespace Toasts |
||
15 | { |
||
44 | office | 16 | public class ToastDisplay : IDisposable |
17 | { |
||
18 | private readonly CancellationToken _cancellationToken; |
||
19 | private readonly BufferBlock<ToastDisplayData> _toastFormBufferBlock; |
||
20 | private ConcurrentBag<IDisposable> tplDataflowLinks; |
||
21 | private readonly TransformBlock<ToastDisplayData, ToastDisplayData> _toastBodyTransformBlock; |
||
22 | private readonly ActionBlock<ToastDisplayData> _toastFormActionBlock; |
||
23 | |||
24 | private ToastDisplay() |
||
25 | { |
||
26 | tplDataflowLinks = new ConcurrentBag<IDisposable>(); |
||
27 | } |
||
28 | |||
29 | public ToastDisplay(CancellationToken cancellationToken) : this() |
||
30 | { |
||
31 | _cancellationToken = cancellationToken; |
||
32 | |||
33 | _toastFormBufferBlock = new BufferBlock<ToastDisplayData>(new DataflowBlockOptions { CancellationToken = _cancellationToken }); |
||
34 | _toastBodyTransformBlock = new TransformBlock<ToastDisplayData, ToastDisplayData>(ToastBodyToHTML, |
||
35 | new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); |
||
36 | _toastFormActionBlock = new ActionBlock<ToastDisplayData>(DisplayForm, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); |
||
37 | |||
38 | tplDataflowLinks.Add(_toastFormBufferBlock.LinkTo(_toastBodyTransformBlock, new DataflowLinkOptions { PropagateCompletion = true })); |
||
39 | tplDataflowLinks.Add(_toastBodyTransformBlock.LinkTo(DataflowBlock.NullTarget<ToastDisplayData>(), new DataflowLinkOptions { PropagateCompletion = true }, result => result == null)); |
||
40 | tplDataflowLinks.Add(_toastBodyTransformBlock.LinkTo(_toastFormActionBlock, new DataflowLinkOptions { PropagateCompletion = true })); |
||
41 | } |
||
42 | |||
43 | private ToastDisplayData ToastBodyToHTML(ToastDisplayData toastDisplayData) |
||
44 | { |
||
45 | switch (toastDisplayData.Content) |
||
46 | { |
||
47 | case "text/markdown": |
||
46 | office | 48 | var pipeline = new MarkdownPipelineBuilder().UsePipeTables().Build(); |
49 | var html = Markdown.ToHtml(toastDisplayData.Body, pipeline); |
||
50 | toastDisplayData.Body = html; |
||
44 | office | 51 | break; |
52 | } |
||
53 | |||
54 | return toastDisplayData; |
||
55 | } |
||
56 | |||
57 | public void Dispose() |
||
58 | { |
||
59 | while (tplDataflowLinks.TryTake(out var disposable)) |
||
60 | { |
||
61 | disposable.Dispose(); |
||
62 | } |
||
63 | } |
||
64 | |||
65 | private static void DisplayForm(ToastDisplayData toastPayload) |
||
66 | { |
||
67 | var thread = new Thread(() => |
||
68 | { |
||
69 | try |
||
70 | { |
||
71 | var toastForm = new ToastForm(toastPayload.Title, toastPayload.Body) |
||
72 | { |
||
73 | EnableChime = toastPayload.EnableChime, |
||
74 | Chime = toastPayload.Chime ?? toastPayload.Chime, |
||
75 | LingerTime = toastPayload.LingerTime, |
||
76 | Image = toastPayload.Image, |
||
77 | ContentType = toastPayload.Content, |
||
78 | EnablePin = toastPayload.EnablePin, |
||
49 | office | 79 | PinPoint = toastPayload.PinPoint, |
80 | UserAgent = toastPayload.UserAgent |
||
44 | office | 81 | }; |
82 | |||
83 | var formClosingEvent = new ManualResetEvent(false); |
||
84 | void OnFormClosing(object sender, EventArgs args) |
||
85 | { |
||
86 | toastForm.FormClosing -= OnFormClosing; |
||
87 | formClosingEvent.Set(); |
||
88 | } |
||
89 | toastForm.FormClosing += OnFormClosing; |
||
90 | |||
91 | Application.Run(toastForm); |
||
92 | |||
93 | var timeout = toastPayload.EnablePin ? -1 : toastPayload.LingerTime; |
||
94 | formClosingEvent.WaitOne(timeout); |
||
95 | toastForm.FormClosing -= OnFormClosing; |
||
96 | toastForm.Close(); |
||
97 | if (!toastForm.IsDisposed) |
||
98 | { |
||
99 | toastForm.Dispose(); |
||
100 | } |
||
101 | toastForm = null; |
||
102 | formClosingEvent.Dispose(); |
||
103 | formClosingEvent = null; |
||
104 | } |
||
105 | catch (Win32Exception) |
||
106 | { |
||
107 | // ignore window creation errors for now |
||
108 | } |
||
109 | catch (COMException) |
||
110 | { |
||
111 | // ignore window creation errors for now |
||
112 | } |
||
51 | office | 113 | catch(InvalidOperationException) |
114 | { |
||
115 | // animation clutter |
||
116 | } |
||
53 | office | 117 | catch (ArgumentException) |
118 | { |
||
119 | // animation clutter |
||
120 | } |
||
121 | catch(Exception exception) |
||
122 | { |
||
123 | Debug.WriteLine(exception); |
||
124 | } |
||
44 | office | 125 | }) |
126 | { |
||
127 | IsBackground = true |
||
128 | }; |
||
129 | thread.SetApartmentState(ApartmentState.STA); |
||
130 | thread.Start(); |
||
131 | } |
||
132 | |||
133 | public async Task Queue(ToastDisplayData toastPayload) |
||
134 | { |
||
135 | await _toastFormBufferBlock.SendAsync(toastPayload, _cancellationToken); |
||
136 | } |
||
41 | office | 137 | } |
27 | office | 138 | } |