mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-05-02 02:34:08 +02:00
Add a WIP notification system (buggy startup, missing navigation, bad UI and UX)
This commit is contained in:
parent
b9c93bfe20
commit
2834a9a443
@ -18,6 +18,7 @@ private static UserConfig Config{
|
|||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
private readonly TweetDeckBridge bridge;
|
private readonly TweetDeckBridge bridge;
|
||||||
|
private readonly FormNotification notification;
|
||||||
|
|
||||||
public FormBrowser(){
|
public FormBrowser(){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -29,6 +30,9 @@ public FormBrowser(){
|
|||||||
browser.RegisterJsObject("$TD",bridge);
|
browser.RegisterJsObject("$TD",bridge);
|
||||||
|
|
||||||
Controls.Add(browser);
|
Controls.Add(browser);
|
||||||
|
|
||||||
|
notification = new FormNotification(this);
|
||||||
|
notification.Show(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void WndProc(ref Message m){
|
protected override void WndProc(ref Message m){
|
||||||
@ -97,5 +101,9 @@ public void OpenSettings(){
|
|||||||
public void OpenAbout(){
|
public void OpenAbout(){
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnTweetPopup(string tweetHtml, string tweetColumn){
|
||||||
|
notification.ShowNotification(new TweetNotification(tweetHtml));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
54
Core/FormNotification.Designer.cs
generated
Normal file
54
Core/FormNotification.Designer.cs
generated
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
namespace TweetDick.Core {
|
||||||
|
partial class FormNotification {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.timer = new System.Windows.Forms.Timer(this.components);
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// timer
|
||||||
|
//
|
||||||
|
this.timer.Tick += new System.EventHandler(this.timer_Tick);
|
||||||
|
//
|
||||||
|
// FormNotification
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(284, 118);
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
|
||||||
|
this.Location = new System.Drawing.Point(32000, 32000);
|
||||||
|
this.Name = "FormNotification";
|
||||||
|
this.ShowInTaskbar = false;
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||||
|
this.Text = "TweetDick";
|
||||||
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotification_FormClosing);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Timer timer;
|
||||||
|
}
|
||||||
|
}
|
86
Core/FormNotification.cs
Normal file
86
Core/FormNotification.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
|
using System.Drawing;
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TweetDick.Core.Handling;
|
||||||
|
|
||||||
|
namespace TweetDick.Core{
|
||||||
|
partial class FormNotification : Form{
|
||||||
|
private readonly FormBrowser owner;
|
||||||
|
private readonly ChromiumWebBrowser browser;
|
||||||
|
|
||||||
|
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
|
||||||
|
|
||||||
|
public FormNotification(FormBrowser owner){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.owner = owner;
|
||||||
|
|
||||||
|
browser = new ChromiumWebBrowser(""){ MenuHandler = new MenuHandlerEmpty() };
|
||||||
|
Controls.Add(browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowNotification(TweetNotification notification){
|
||||||
|
Screen screen = Screen.FromControl(owner);
|
||||||
|
Location = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-16-Width,screen.WorkingArea.Y+16);
|
||||||
|
|
||||||
|
tweetQueue.Enqueue(notification);
|
||||||
|
|
||||||
|
if (!timer.Enabled){
|
||||||
|
LoadNextNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HideNotification(){
|
||||||
|
browser.Load("about:blank");
|
||||||
|
Location = new Point(32000,32000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadNextNotification(){
|
||||||
|
TweetNotification tweet = tweetQueue.Dequeue();
|
||||||
|
|
||||||
|
browser.Load("about:blank");
|
||||||
|
browser.LoadHtml(tweet.GenerateHtml(),"http://tweetdeck.twitter.com/");
|
||||||
|
|
||||||
|
timer.Stop();
|
||||||
|
timer.Interval = 5000;
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timer_Tick(object sender, EventArgs e){
|
||||||
|
if (tweetQueue.Count > 0){
|
||||||
|
LoadNextNotification();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
HideNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
|
if (e.CloseReason == CloseReason.UserClosing){
|
||||||
|
HideNotification();
|
||||||
|
tweetQueue.Clear();
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MenuHandlerEmpty : IContextMenuHandler{
|
||||||
|
public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||||
|
model.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){}
|
||||||
|
|
||||||
|
public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,12 +6,30 @@ public TweetDeckBridge(FormBrowser form){
|
|||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadFontSizeClass(string fsClass){
|
||||||
|
form.InvokeSafe(() => {
|
||||||
|
TweetNotification.SetFontSizeClass(fsClass);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadNotificationHeadContents(string headContents){
|
||||||
|
form.InvokeSafe(() => {
|
||||||
|
TweetNotification.SetHeadTag(headContents);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void OpenSettingsMenu(){
|
public void OpenSettingsMenu(){
|
||||||
form.InvokeSafe(() => {
|
form.InvokeSafe(() => {
|
||||||
form.OpenSettings();
|
form.OpenSettings();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnTweetPopup(string tweetHtml, string tweetColumn){
|
||||||
|
form.InvokeSafe(() => {
|
||||||
|
form.OnTweetPopup(tweetHtml,tweetColumn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void Log(string data){
|
public void Log(string data){
|
||||||
System.Diagnostics.Debug.WriteLine(data);
|
System.Diagnostics.Debug.WriteLine(data);
|
||||||
}
|
}
|
||||||
|
34
Core/Handling/TweetNotification.cs
Normal file
34
Core/Handling/TweetNotification.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace TweetDick.Core.Handling{
|
||||||
|
sealed class TweetNotification{
|
||||||
|
private static string FontSizeClass { get; set; }
|
||||||
|
private static string HeadTag { get; set; }
|
||||||
|
|
||||||
|
public static void SetFontSizeClass(string newFSClass){
|
||||||
|
FontSizeClass = newFSClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetHeadTag(string headContents){
|
||||||
|
HeadTag = headContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly string html;
|
||||||
|
|
||||||
|
public TweetNotification(string html){
|
||||||
|
this.html = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateHtml(){
|
||||||
|
StringBuilder build = new StringBuilder();
|
||||||
|
build.Append("<!DOCTYPE html>");
|
||||||
|
build.Append("<html class='os-windows ").Append(FontSizeClass).Append("'>");
|
||||||
|
build.Append("<head>").Append(HeadTag).Append("</head>");
|
||||||
|
build.Append("<body class='hearty'><div class='app-columns-container'><div class='column' style='width:100%'>");
|
||||||
|
build.Append(html);
|
||||||
|
build.Append("</div></div></body>");
|
||||||
|
build.Append("</html>");
|
||||||
|
return build.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,19 @@
|
|||||||
(function($,$TD){
|
(function($,$TD){
|
||||||
|
//
|
||||||
|
// Constant: List of valid font size classes.
|
||||||
|
//
|
||||||
|
var fontSizeClasses = [ "txt-base-smallest", "txt-base-small", "txt-base-medium", "txt-base-large", "txt-base-largest" ];
|
||||||
|
|
||||||
//
|
//
|
||||||
// Variable: Says whether TweetDick events was initialized.
|
// Variable: Says whether TweetDick events was initialized.
|
||||||
//
|
//
|
||||||
var isInitialized = false;
|
var isInitialized = false;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Variable: Previous font size class in the <html> tag.
|
||||||
|
//
|
||||||
|
var prevFontSizeClass;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Initializes TweetDick events. Called after the website app is loaded.
|
// Function: Initializes TweetDick events. Called after the website app is loaded.
|
||||||
//
|
//
|
||||||
@ -29,9 +39,94 @@
|
|||||||
},0);
|
},0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Tweet notifications
|
||||||
|
(function(){
|
||||||
|
var columns = $(".js-app-columns").first();
|
||||||
|
|
||||||
|
var refreshColumnObservers = function(){
|
||||||
|
columns.children().each(function(){
|
||||||
|
registerTweetObserverForColumn($(this));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
new MutationObserver(refreshColumnObservers).observe(columns[0],{
|
||||||
|
childList: true
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshColumnObservers();
|
||||||
|
})();
|
||||||
|
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Registers an observer to a TweetDeck column, which reports new tweets.
|
||||||
|
//
|
||||||
|
var registerTweetObserverForColumn = function(column){
|
||||||
|
if (column[0].hasAttribute("data-tweetdick-observed"))return;
|
||||||
|
|
||||||
|
var mid = column;
|
||||||
|
mid = mid.children().first();
|
||||||
|
mid = mid.children().first();
|
||||||
|
mid = mid.children(".js-column-content").first();
|
||||||
|
mid = mid.children(".js-column-scroller").first();
|
||||||
|
|
||||||
|
var container = mid.children(".js-chirp-container").first();
|
||||||
|
if (container.length == 0)return;
|
||||||
|
|
||||||
|
new MutationObserver(function(mutations){
|
||||||
|
// TODO check if popups are enabled first
|
||||||
|
|
||||||
|
Array.prototype.forEach.call(mutations,function(mutation){
|
||||||
|
Array.prototype.forEach.call(mutation.addedNodes,function(node){
|
||||||
|
if (node.tagName != "ARTICLE")return;
|
||||||
|
|
||||||
|
onNewTweet(column,node);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).observe(container[0],{
|
||||||
|
childList: true
|
||||||
|
});
|
||||||
|
|
||||||
|
column[0].setAttribute("data-tweetdick-observed","");
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Event callback for a new tweet.
|
||||||
|
//
|
||||||
|
var onNewTweet = function(column, tweet){
|
||||||
|
var html = $(tweet.outerHTML);
|
||||||
|
html.find("footer:first").remove();
|
||||||
|
|
||||||
|
$TD.onTweetPopup(html.html(),""); // TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Retrieves the font size using <html> class attribute.
|
||||||
|
//
|
||||||
|
var getFontSizeClass = function(){
|
||||||
|
for(var index = 0; index < fontSizeClasses.length; index++){
|
||||||
|
if (document.documentElement.classList.contains(fontSizeClasses[index])){
|
||||||
|
return fontSizeClasses[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fontSizeClasses[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Retrieves the tags to be put into <head> for notification HTML code.
|
||||||
|
//
|
||||||
|
var getNotificationHeadContents = function(){
|
||||||
|
var tags = [];
|
||||||
|
|
||||||
|
$(document.head).children("link[rel='stylesheet'],meta[charset],meta[http-equiv]").each(function(){
|
||||||
|
tags.push($(this)[0].outerHTML);
|
||||||
|
});
|
||||||
|
|
||||||
|
return tags.join("");
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Observe the app <div> element and initialize TweetDick whenever possible.
|
// Block: Observe the app <div> element and initialize TweetDick whenever possible.
|
||||||
//
|
//
|
||||||
@ -48,4 +143,31 @@
|
|||||||
attributes: true,
|
attributes: true,
|
||||||
attributeFilter: [ "class" ]
|
attributeFilter: [ "class" ]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Observe changes in <html> class to update font size.
|
||||||
|
//
|
||||||
|
new MutationObserver(function(mutations){
|
||||||
|
var fsClass = getFontSizeClass();
|
||||||
|
|
||||||
|
if (fsClass != prevFontSizeClass){
|
||||||
|
prevFontSizeClass = fsClass;
|
||||||
|
$TD.loadFontSizeClass(fsClass);
|
||||||
|
}
|
||||||
|
}).observe(document.documentElement,{
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: [ "class" ]
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Observe stylesheet swapping.
|
||||||
|
//
|
||||||
|
new MutationObserver(function(mutations){
|
||||||
|
$TD.loadNotificationHeadContents(getNotificationHeadContents());
|
||||||
|
}).observe(document.head.querySelector("[http-equiv='Default-Style']"),{
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: [ "content" ]
|
||||||
|
});
|
||||||
|
|
||||||
|
$TD.loadNotificationHeadContents(getNotificationHeadContents());
|
||||||
})($,$TD);
|
})($,$TD);
|
||||||
|
@ -86,6 +86,13 @@
|
|||||||
<Compile Include="Core\FormBrowser.Designer.cs">
|
<Compile Include="Core\FormBrowser.Designer.cs">
|
||||||
<DependentUpon>FormBrowser.cs</DependentUpon>
|
<DependentUpon>FormBrowser.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Core\FormNotification.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\FormNotification.Designer.cs">
|
||||||
|
<DependentUpon>FormNotification.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\Handling\TweetNotification.cs" />
|
||||||
<Compile Include="Core\RichTextLabel.cs">
|
<Compile Include="Core\RichTextLabel.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
Loading…
Reference in New Issue
Block a user