mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-27 03:57:21 +00:00
Expand the logging mechanism to support alerts overlaid on the main screen
Now we get decent visual indication of sync errors, for instance. Still work to do to coalesce alerts so that the screen doesn’t fill up with them after an interval. And probably we should add a button to clear all alerts.
This commit is contained in:
parent
28212f08b2
commit
70a120d4a6
@ -219,6 +219,7 @@ exports.compileFilter = function(filterString) {
|
||||
});
|
||||
// Return a function that applies the operations to a source array/hashmap of tiddler titles
|
||||
return function(source,currTiddlerTitle) {
|
||||
source = source || self.tiddlers;
|
||||
var results = [];
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,currTiddlerTitle);
|
||||
|
@ -16,11 +16,8 @@ var widget = require("$:/core/modules/widgets/widget.js");
|
||||
|
||||
exports.startup = function() {
|
||||
var modules,n,m,f,commander;
|
||||
// Load utility modules and initialise the logger
|
||||
// Load modules
|
||||
$tw.modules.applyMethods("utils",$tw.utils);
|
||||
$tw.logger = new $tw.utils.Logger();
|
||||
$tw.log = $tw.logger.log;
|
||||
// Load other modules
|
||||
$tw.modules.applyMethods("global",$tw);
|
||||
$tw.modules.applyMethods("config",$tw.config);
|
||||
if($tw.browser) {
|
||||
@ -111,7 +108,7 @@ exports.startup = function() {
|
||||
$tw.rootWidget.addEventListener("tw-scroll",function(event) {
|
||||
$tw.pageScroller.handleEvent(event);
|
||||
});
|
||||
// Install the save action handler
|
||||
// Install the save action handlers
|
||||
$tw.rootWidget.addEventListener("tw-save-wiki",function(event) {
|
||||
$tw.syncer.saveWiki({
|
||||
template: event.param,
|
||||
@ -132,6 +129,16 @@ exports.startup = function() {
|
||||
downloadType: "text/plain"
|
||||
});
|
||||
});
|
||||
// Listen out for login/logout/refresh events in the browser
|
||||
$tw.rootWidget.addEventListener("tw-login",function() {
|
||||
$tw.syncer.handleLoginEvent();
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-logout",function() {
|
||||
$tw.syncer.handleLogoutEvent();
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tw-server-refresh",function() {
|
||||
$tw.syncer.handleRefreshEvent();
|
||||
});
|
||||
// Install the crypto event handlers
|
||||
$tw.rootWidget.addEventListener("tw-set-password",function(event) {
|
||||
$tw.passwordPrompt.createPrompt({
|
||||
|
@ -20,7 +20,7 @@ function Syncer(options) {
|
||||
var self = this;
|
||||
this.wiki = options.wiki;
|
||||
// Make a logger
|
||||
this.log = $tw.logger.makeLog("syncer");
|
||||
this.logger = new $tw.utils.Logger("syncer" + ($tw.browser ? "-browser" : "") + ($tw.node ? "-server" : ""));
|
||||
// Find a working syncadaptor
|
||||
this.syncadaptor = undefined;
|
||||
$tw.modules.forEachModuleOfType("syncadaptor",function(title,module) {
|
||||
@ -51,18 +51,6 @@ function Syncer(options) {
|
||||
self.handleLazyLoadEvent(title);
|
||||
});
|
||||
}
|
||||
// Listen out for login/logout/refresh events in the browser
|
||||
if($tw.browser) {
|
||||
document.addEventListener("tw-login",function(event) {
|
||||
self.handleLoginEvent(event);
|
||||
},false);
|
||||
document.addEventListener("tw-logout",function(event) {
|
||||
self.handleLogoutEvent(event);
|
||||
},false);
|
||||
document.addEventListener("tw-server-refresh",function(event) {
|
||||
self.handleRefreshEvent(event);
|
||||
},false);
|
||||
}
|
||||
// Get the login status
|
||||
this.getStatus(function (err,isLoggedIn) {
|
||||
// Do a sync from the server
|
||||
@ -70,13 +58,6 @@ function Syncer(options) {
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Error handling
|
||||
*/
|
||||
Syncer.prototype.showError = function(error) {
|
||||
this.log("Error: " + error);
|
||||
};
|
||||
|
||||
/*
|
||||
Constants
|
||||
*/
|
||||
@ -96,8 +77,10 @@ Syncer.prototype.readTiddlerInfo = function() {
|
||||
// Hashmap by title of {revision:,changeCount:,adaptorInfo:}
|
||||
this.tiddlerInfo = {};
|
||||
// Record information for known tiddlers
|
||||
var self = this;
|
||||
this.wiki.forEachTiddler({includeSystem: true},function(title,tiddler) {
|
||||
var self = this,
|
||||
tiddlers = this.filterFn.call(this.wiki);
|
||||
$tw.utils.each(tiddlers,function(title) {
|
||||
var tiddler = self.wiki.getTiddler(title);
|
||||
self.tiddlerInfo[title] = {
|
||||
revision: tiddler.fields["revision"],
|
||||
adaptorInfo: self.syncadaptor && self.syncadaptor.getTiddlerInfo(tiddler),
|
||||
@ -165,7 +148,7 @@ Syncer.prototype.saveWiki = function(options) {
|
||||
for(var t=this.savers.length-1; t>=0; t--) {
|
||||
var saver = this.savers[t];
|
||||
if(saver.info.capabilities.indexOf(method) !== -1 && saver.save(text,method,callback)) {
|
||||
this.log("Saving wiki with method",method,"through saver",saver.info.name);
|
||||
this.logger.log("Saving wiki with method",method,"through saver",saver.info.name);
|
||||
// Clear the task queue if we're saving (rather than downloading)
|
||||
if(method !== "download") {
|
||||
this.readTiddlerInfo();
|
||||
@ -208,7 +191,7 @@ Syncer.prototype.getStatus = function(callback) {
|
||||
// Get login status
|
||||
this.syncadaptor.getStatus(function(err,isLoggedIn,username) {
|
||||
if(err) {
|
||||
self.showError(err);
|
||||
self.logger.alert(err);
|
||||
return;
|
||||
}
|
||||
// Set the various status tiddlers
|
||||
@ -233,7 +216,7 @@ Synchronise from the server by reading the skinny tiddler list and queuing up lo
|
||||
*/
|
||||
Syncer.prototype.syncFromServer = function() {
|
||||
if(this.syncadaptor && this.syncadaptor.getSkinnyTiddlers) {
|
||||
this.log("Retrieving skinny tiddler list");
|
||||
this.logger.log("Retrieving skinny tiddler list");
|
||||
var self = this;
|
||||
if(this.pollTimerId) {
|
||||
clearTimeout(this.pollTimerId);
|
||||
@ -247,7 +230,7 @@ Syncer.prototype.syncFromServer = function() {
|
||||
},self.pollTimerInterval);
|
||||
// Check for errors
|
||||
if(err) {
|
||||
self.log("Error retrieving skinny tiddler list:",err);
|
||||
self.logger.alert("Error retrieving skinny tiddler list:",err);
|
||||
return;
|
||||
}
|
||||
// Process each incoming tiddler
|
||||
@ -285,8 +268,8 @@ Syncer.prototype.syncToServer = function(changes) {
|
||||
now = new Date(),
|
||||
filteredChanges = this.filterFn.call(this.wiki,changes);
|
||||
$tw.utils.each(changes,function(change,title,object) {
|
||||
// Ignore the change if it is a shadow tiddler
|
||||
if((change.deleted && $tw.utils.hop(self.tiddlerInfo,title)) || (!change.deleted && filteredChanges.indexOf(title) !== -1)) {
|
||||
// Process the change if it is a deletion of a tiddler we're already syncing, or is on the filtered change list
|
||||
if((change.deleted && $tw.utils.hop(self.tiddlerInfo,title)) || filteredChanges.indexOf(title) !== -1) {
|
||||
// Queue a task to sync this tiddler
|
||||
self.enqueueSyncTask({
|
||||
type: change.deleted ? "delete" : "save",
|
||||
@ -300,7 +283,6 @@ Syncer.prototype.syncToServer = function(changes) {
|
||||
Lazily load a skinny tiddler if we can
|
||||
*/
|
||||
Syncer.prototype.handleLazyLoadEvent = function(title) {
|
||||
console.log("Lazy loading",title)
|
||||
// Queue up a sync task to load this tiddler
|
||||
this.enqueueSyncTask({
|
||||
type: "load",
|
||||
@ -335,7 +317,7 @@ Attempt to login to TiddlyWeb.
|
||||
callback: invoked with arguments (err,isLoggedIn)
|
||||
*/
|
||||
Syncer.prototype.login = function(username,password,callback) {
|
||||
this.log("Attempting to login as",username);
|
||||
this.logger.log("Attempting to login as",username);
|
||||
var self = this;
|
||||
if(this.syncadaptor.login) {
|
||||
this.syncadaptor.login(username,password,function(err) {
|
||||
@ -357,12 +339,12 @@ Syncer.prototype.login = function(username,password,callback) {
|
||||
Attempt to log out of TiddlyWeb
|
||||
*/
|
||||
Syncer.prototype.handleLogoutEvent = function() {
|
||||
this.log("Attempting to logout");
|
||||
this.logger.log("Attempting to logout");
|
||||
var self = this;
|
||||
if(this.syncadaptor.logout) {
|
||||
this.syncadaptor.logout(function(err) {
|
||||
if(err) {
|
||||
self.showError(err);
|
||||
self.logger.alert(err);
|
||||
} else {
|
||||
self.getStatus();
|
||||
}
|
||||
@ -400,7 +382,7 @@ Syncer.prototype.enqueueSyncTask = function(task) {
|
||||
}
|
||||
// Check if this tiddler is already in the queue
|
||||
if($tw.utils.hop(this.taskQueue,task.title)) {
|
||||
// this.log("Re-queueing up sync task with type:",task.type,"title:",task.title);
|
||||
// this.logger.log("Re-queueing up sync task with type:",task.type,"title:",task.title);
|
||||
var existingTask = this.taskQueue[task.title];
|
||||
// If so, just update the last modification time
|
||||
existingTask.lastModificationTime = task.lastModificationTime;
|
||||
@ -409,7 +391,7 @@ Syncer.prototype.enqueueSyncTask = function(task) {
|
||||
existingTask.type = task.type;
|
||||
}
|
||||
} else {
|
||||
// this.log("Queuing up sync task with type:",task.type,"title:",task.title);
|
||||
// this.logger.log("Queuing up sync task with type:",task.type,"title:",task.title);
|
||||
// If it is not in the queue, insert it
|
||||
this.taskQueue[task.title] = task;
|
||||
}
|
||||
@ -463,7 +445,7 @@ Syncer.prototype.processTaskQueue = function() {
|
||||
// Dispatch the task
|
||||
this.dispatchTask(task,function(err) {
|
||||
if(err) {
|
||||
self.showError("Sync error while processing '" + task.title + "':\n" + err);
|
||||
self.logger.alert("Sync error while processing '" + task.title + "':\n" + err);
|
||||
}
|
||||
// Mark that this task is no longer in progress
|
||||
delete self.taskInProgress[task.title];
|
||||
@ -515,7 +497,7 @@ Syncer.prototype.dispatchTask = function(task,callback) {
|
||||
if(task.type === "save") {
|
||||
var changeCount = this.wiki.getChangeCount(task.title),
|
||||
tiddler = this.wiki.getTiddler(task.title);
|
||||
this.log("Dispatching 'save' task:",task.title);
|
||||
this.logger.log("Dispatching 'save' task:",task.title);
|
||||
if(tiddler) {
|
||||
this.syncadaptor.saveTiddler(tiddler,function(err,adaptorInfo,revision) {
|
||||
if(err) {
|
||||
@ -533,7 +515,7 @@ Syncer.prototype.dispatchTask = function(task,callback) {
|
||||
}
|
||||
} else if(task.type === "load") {
|
||||
// Load the tiddler
|
||||
this.log("Dispatching 'load' task:",task.title);
|
||||
this.logger.log("Dispatching 'load' task:",task.title);
|
||||
this.syncadaptor.loadTiddler(task.title,function(err,tiddlerFields) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
@ -547,7 +529,7 @@ Syncer.prototype.dispatchTask = function(task,callback) {
|
||||
});
|
||||
} else if(task.type === "delete") {
|
||||
// Delete the tiddler
|
||||
this.log("Dispatching 'delete' task:",task.title);
|
||||
this.logger.log("Dispatching 'delete' task:",task.title);
|
||||
this.syncadaptor.deleteTiddler(task.title,function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
|
@ -45,7 +45,7 @@ exports.httpRequest = function(options) {
|
||||
return;
|
||||
}
|
||||
// Something went wrong
|
||||
options.callback(new Error("XMLHttpRequest error: " + this.status));
|
||||
options.callback("XMLHttpRequest error code: " + this.status);
|
||||
}
|
||||
};
|
||||
// Make the request
|
||||
|
@ -15,29 +15,36 @@ A basic logging implementation
|
||||
/*
|
||||
Make a new logger
|
||||
*/
|
||||
function Logger() {
|
||||
|
||||
function Logger(componentName) {
|
||||
this.componentName = componentName || "";
|
||||
}
|
||||
|
||||
/*
|
||||
Make a log function for a particular component
|
||||
*/
|
||||
Logger.prototype.makeLog = function(componentName) {
|
||||
var self = this;
|
||||
return function(/* args */) {
|
||||
self.log.apply(self.log,[componentName + ":"].concat(Array.prototype.slice.call(arguments,0)));
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Log a message
|
||||
*/
|
||||
Logger.prototype.log = function(/* args */) {
|
||||
if(console !== undefined && console.log !== undefined) {
|
||||
return Function.apply.call(console.log, console, arguments);
|
||||
return Function.apply.call(console.log, console, [this.componentName + ":"].concat(Array.prototype.slice.call(arguments,0)));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Alert a message
|
||||
*/
|
||||
Logger.prototype.alert = function(/* args */) {
|
||||
var text = Array.prototype.join.call(arguments," "),
|
||||
fields = {
|
||||
title: $tw.wiki.generateNewTitle("$:/temp/alerts/alert",{prefix: ""}),
|
||||
text: text,
|
||||
tags: ["$:/tags/Alert"],
|
||||
component: this.componentName,
|
||||
modified: new Date()
|
||||
};
|
||||
$tw.wiki.addTiddler(new $tw.Tiddler(fields));
|
||||
// Log it too
|
||||
this.log.apply(this,Array.prototype.slice.call(arguments,0));
|
||||
};
|
||||
|
||||
exports.Logger = Logger;
|
||||
|
||||
})();
|
||||
|
@ -172,11 +172,14 @@ exports.tiddlerExists = function(title) {
|
||||
/*
|
||||
Generate an unused title from the specified base
|
||||
*/
|
||||
exports.generateNewTitle = function(baseTitle) {
|
||||
exports.generateNewTitle = function(baseTitle,options) {
|
||||
options = options || {};
|
||||
var c = 0,
|
||||
title = baseTitle;
|
||||
while(this.tiddlerExists(title)) {
|
||||
title = baseTitle + " " + (++c);
|
||||
title = baseTitle +
|
||||
(options.prefix || " ") +
|
||||
(++c);
|
||||
};
|
||||
return title;
|
||||
};
|
||||
|
3
core/ui/AlertTemplate.tid
Normal file
3
core/ui/AlertTemplate.tid
Normal file
@ -0,0 +1,3 @@
|
||||
title: $:/core/ui/AlertTemplate
|
||||
|
||||
<div class="tw-alert"><div class="tw-alert-toolbar"><$button message="tw-delete-tiddler" class="btn-invisible">{{$:/core/images/delete-button}}</$button></div><div class="tw-alert-subtitle"><$view field="component"/> - <$view field="modified" format="relativedate"/></div><div class="tw-alert-body"><$view field="text"/></div></div>
|
@ -3,6 +3,13 @@ tags: $:/tags/PageTemplate
|
||||
|
||||
<section class="story-river">
|
||||
|
||||
<!-- Alerts -->
|
||||
<div class="tw-alerts">
|
||||
|
||||
<$list filter="[is[shadow]!has[draft.of]tag[$:/tags/Alert]] [!is[shadow]!has[draft.of]tag[$:/tags/Alert]] +[sort[modified]]" template="$:/core/ui/AlertTemplate" storyview="pop"/>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- The main story -->
|
||||
<$list filter="[list[$:/StoryList]]" history="$:/HistoryList" template="$:/core/ui/ViewTemplate" editTemplate="$:/core/ui/EditTemplate" storyview={{$:/view}} />
|
||||
|
||||
|
17
editions/clientserver/tiddlers/AlertMechanism.tid
Normal file
17
editions/clientserver/tiddlers/AlertMechanism.tid
Normal file
@ -0,0 +1,17 @@
|
||||
created: 20140213224306412
|
||||
modified: 20140213224622441
|
||||
tags: mechanism
|
||||
title: AlertMechanism
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Alerts are displayed as yellow boxes overlaying the main TiddlyWiki window. Each one corresponds to a tiddler with the tag [[$:/tags/Alert]]. Clicking the delete icon on an alert deletes the corresponding tiddler.
|
||||
|
||||
Alert tiddlers should have the following fields:
|
||||
|
||||
|!Field |!Description |
|
||||
|title |By default, alert titles have the prefix `$:/temp/alerts/` |
|
||||
|text |The text of the alert message |
|
||||
|modified |Date of the alert (used for ordering the alerts on screen) |
|
||||
|component |Component name associated with the alert |
|
||||
|tags |Must include [[$:/tags/Alert]] |
|
||||
|
@ -21,12 +21,12 @@ function FileSystemAdaptor(syncer) {
|
||||
this.syncer = syncer;
|
||||
this.watchers = {};
|
||||
this.pending = {};
|
||||
this.log = $tw.logger.makeLog("FileSystem");
|
||||
this.logger = new $tw.utils.Logger("FileSystem");
|
||||
this.setwatcher = function(filename, title) {
|
||||
return undefined;
|
||||
return this.watchers[filename] = this.watchers[filename] ||
|
||||
fs.watch(filename, {persistent: false}, function(e) {
|
||||
self.log("Error:",e,filename);
|
||||
self.logger.log("Error:",e,filename);
|
||||
if(e === "change") {
|
||||
var tiddlers = $tw.loadTiddlersFromFile(filename).tiddlers;
|
||||
for(var t in tiddlers) {
|
||||
@ -157,7 +157,7 @@ FileSystemAdaptor.prototype.saveTiddler = function(tiddler,callback) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
self.log("Saved file",fileInfo.filepath);
|
||||
self.logger.log("Saved file",fileInfo.filepath);
|
||||
_finish();
|
||||
});
|
||||
});
|
||||
@ -169,7 +169,7 @@ FileSystemAdaptor.prototype.saveTiddler = function(tiddler,callback) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
self.log("Saved file",fileInfo.filepath);
|
||||
self.logger.log("Saved file",fileInfo.filepath);
|
||||
_finish();
|
||||
});
|
||||
}
|
||||
@ -203,7 +203,7 @@ FileSystemAdaptor.prototype.deleteTiddler = function(title,callback) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
self.log("Deleted file",fileInfo.filepath);
|
||||
self.logger.log("Deleted file",fileInfo.filepath);
|
||||
// Delete the metafile if present
|
||||
if(fileInfo.hasMetaFile) {
|
||||
fs.unlink(fileInfo.filepath + ".meta",function(err) {
|
||||
|
@ -19,7 +19,7 @@ function TiddlyWebAdaptor(syncer) {
|
||||
this.syncer = syncer;
|
||||
this.host = this.getHost();
|
||||
this.recipe = undefined;
|
||||
this.log = $tw.logger.makeLog("TiddlyWebAdaptor");
|
||||
this.logger = new $tw.utils.Logger("TiddlyWebAdaptor");
|
||||
}
|
||||
|
||||
TiddlyWebAdaptor.prototype.getHost = function() {
|
||||
@ -48,7 +48,7 @@ TiddlyWebAdaptor.prototype.getStatus = function(callback) {
|
||||
// Get status
|
||||
var self = this,
|
||||
wiki = self.syncer.wiki;
|
||||
this.log("Getting status");
|
||||
this.logger.log("Getting status");
|
||||
$tw.utils.httpRequest({
|
||||
url: this.host + "status",
|
||||
callback: function(err,data) {
|
||||
@ -63,7 +63,7 @@ TiddlyWebAdaptor.prototype.getStatus = function(callback) {
|
||||
} catch (e) {
|
||||
}
|
||||
if(json) {
|
||||
self.log("Status:",data);
|
||||
self.logger.log("Status:",data);
|
||||
// Record the recipe
|
||||
if(json.space) {
|
||||
self.recipe = json.space.recipe;
|
||||
@ -95,7 +95,7 @@ TiddlyWebAdaptor.prototype.login = function(username,password,callback) {
|
||||
callback(err);
|
||||
}
|
||||
};
|
||||
this.log("Logging in:",options);
|
||||
this.logger.log("Logging in:",options);
|
||||
$tw.utils.httpRequest(options);
|
||||
};
|
||||
|
||||
@ -113,7 +113,7 @@ TiddlyWebAdaptor.prototype.logout = function(callback) {
|
||||
callback(err);
|
||||
}
|
||||
};
|
||||
this.log("Logging out:",options);
|
||||
this.logger.log("Logging out:",options);
|
||||
$tw.utils.httpRequest(options);
|
||||
};
|
||||
|
||||
|
@ -922,14 +922,35 @@ canvas.tw-edit-bitmapeditor {
|
||||
** Alerts
|
||||
*/
|
||||
|
||||
.tw-alerts {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 20000;
|
||||
}
|
||||
|
||||
.tw-alert {
|
||||
position: relative;
|
||||
<<border-radius 6px>>
|
||||
margin: 7px 7px 7px 7px;
|
||||
margin: 28px;
|
||||
padding: 14px 14px 14px 14px;
|
||||
border: 3px solid #DBB727;
|
||||
background-color: rgb(250, 210, 50);
|
||||
}
|
||||
|
||||
.tw-alert-toolbar {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
}
|
||||
|
||||
.tw-alert-subtitle {
|
||||
color: #DBB727;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.tw-static-alert {
|
||||
position: relative;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user