I have played around with the AlloyUI Scheduler, and it seems to be the by far best calendar out there. While poking it, a few questions have risen. I am not particularly experienced with YUI, so my questions might be caused by lack of experience with the kind of thinking that goes with YUI. Nevertheless, here are the problems I haven't managed to overcome:
First, I tried loading 500+ events into it, and the result is that every action (adding events, deleting events, switching between views, switching between weeks/months) happens with a delay. Is this expected? 500 events doesn't sound like much, even for a relatively short period.
Can I limit the hours displayed in Day or Month view? I.e., instead of 00:00 - 23:59 I'd prefer to only display 08:00 - 17:59.
I've managed to translate bits of the UI, such as "Today", "Day", "Week", "Month", "Agenda", "Delete", "Save", "Cancel", "e.g., Dinner at Brian's". But how can I translate the days (e.g., "Monday", "Tuesday" etc)?
I've found a way to replace the default buttons of the EventRecorder's toolbar. But is there a way to keep the originals ("Save", "Cancel", "Delete") and add a few custom ones?
var eventRecorder = new Y.SchedulerEventRecorder({
toolbar: {
children: [
[
{
label: 'Details',
on: {
click: function () {
alert("Yeah!");
}
}
},
]
]
}
});
Is there a way to disable some periods of time for event creation? I'd imagine this could be achieved by interrupting the event creation by some way. Something in the lines of
scheduler.on({
'scheduler-events:add': function (event) {
return isEventAllowed();
}
});
What is the correct way for accessing the calendar event data in the case of events such as "scheduler-event:change", "scheduler-event-recorder:edit" or similar? I currently use scheduler.getEvents()[event.index]._state.data and eventRecorder.getUpdatedSchedulerEvent()._state.data, respectively. But using these smells funny.
What would be a recommended way for adding a detailed editing view for events? I currently hacked it with something in the lines of
scheduler.on({
'scheduler-base:click': function(event) {
var toolbar = $(".aui-scheduler-event-recorder-overlay .yui3-widget-ft .aui-toolbar-content .aui-btn-group");
if (!toolbar.data("custom-processed")) {
var button = $('<button class="aui-btn">Details</button>');
toolbar.append(button);
button.click(function (event) {
event.preventDefault();
var eventData = (eventRecorder.get("event") || eventRecorder.clone())._state.data;
// Creating my detailed editing window here
$(".aui-scheduler-event-recorder-overlay").addClass("yui3-overlay-hidden");
});
toolbar.data("custom-processed", true);
}
}
});
Thanks,
Related
is there any easy way to get any changes of tabs, groups, windows of chrome for extension?
when one of these behavios :
create or close or moved a tab
or changed the url of the tab
or add in or out from a tabgroup
or create or close or release a tabgroup
or change the color or title of the tabgroup
or move the tabgroup
i know i could judage one by one by chrome.tabs.oncreate & chorme.tabs.onupdate etc.
but i wanna know weather is there a one action could judge all the changes one times?
thank you
No there isn't. But really I dont see why just not pick all the events you want and then call query
const listen = (events, callback) =>
events.forEach((event) =>
chrome.tabs[event].addListener(() => chrome.tabs.query({}, callback))
);
listen(
[
"onCreated",
"onAttached",
"onDetached",
"onHighlighted",
"onDetached",
"onMoved",
"onRemoved",
"onReplaced",
"onActivated",
"onUpdated",
],
(tabs) => {
console.log(tabs); // all tabs here
}
);
I want to handle my own undo/redo menu commands and at the same time still support the Electron built-in undo/redo of the webContents object when it is relevant, for example when the user is focused on a text input element.
This answer seems like a good start, but it is incomplete as it does not address the root question, which is how to mix the two things.
Here is how the ideal code would look like when defining the menu item:
{
label: 'Undo',
accelerator: 'CmdOrCtrl+Z',
click: function(menuItem, focusedWin) {
if (*** focusedWin.webContents thinks it can handle it ***) {
focusedWin.webContents.undo();
} else {
// Run my own code
}
}
}
The one dollar question is: how do I code the "if focusedWin.webContents thinks it can handle it" test?
I found a solution to my problem. It's not elegant, but it works. The idea is that the built-in Edit menus such as undo/redo/cut/copy/paste/delete/etc. should be managed by the webContents object only when the user is focused on an input or textarea element.
So in my index.html file, I send a message to the Main process when there is a focus or a blur event on input elements. This sets a global variable (yuck!) on the main process side which is used to make the decision between letting webContents do its job or doing it ourselves.
In index.html (I use jQuery and the great messaging system described in this other thread):
//-- Tell the main process if the preset Edit menus should be activated or not.
// Typically we active it only when the active item has text input
$('input').focus(() => window.api.send("menus:edit-buildin", { turnItOn: true }));
$('input').blur(() => window.api.send("menus:edit-buildin", { turnItOn: false }));
In main.js:
// This is a global :-( to track if we should enable and use
// the preset Edit menus: undo/redo/cut/copy/paste/delete
var menusEditPreset = false;
// Listen to the renderer
ipcMain.on("menus:edit-buildin", (event, { turnItOn }) => {
menusEditPreset = turnItOn;
});
// ... and in the menu definition:
{
id: 'undo',
label: 'Undo',
accelerator: 'CmdOrCtrl+Z',
click: (event, focusedWindow, focusedWebContents) => {
if (menusEditPreset) {
focusedWindow.webContents.undo();
} else {
console.log("My own undo !");
}
}
},
I need to be able to undo / redo the colors that are picked with the new SwiftUI ColorPicker ( on the iPad : it's presented as a floating window )
The thing that makes it very difficult is that there is apparently no way to know that the user has indeed chosen a color ( and therefore closed the panel )
Instead, the behavior of ColorPicker is that it will keep updating the binded color as the user is manipulating the color controls. This is very helpful to show a live preview, but you don't want to register all these color variations for undo / redo purposes : you only want the color that was finally picked.
Therefore there is no logical distinction between the colors that the user tried, and the one that was selected
And I looked everywhere : there aren't any modifiers / notifications related to that.
I know SwiftUI hasn't been there for long, but this seems like a crucial functionality that's missing?
Has anyone found a workaround?
Undo is always tricky. One size does not fit all. In this case a good solution is to throttle the number of events that come into the UndoManager. You can do so by comparing the current time to the time you last commit.
Create a view model that represents your Source of Truth, and conform to ObservableObject to publish events. It will own the UndoManager. It will have a computed property that gets/sets to the internal data, and check the current time against the time of last commit.
class EditViewModel: ObservableObject {
var commitTime = Date.now
let undoManager = UndoManager()
private var _color: Color = .red {
didSet {
objectWillChange.send()
}
}
var color: Color {
get { _color }
set {
let oldValue = color
let now = Date.now
if now.timeIntervalSince(commitTime) > 1 {
undoManager.registerUndo(withTarget: self) {
$0.color = oldValue
}
}
self.commitTime = now
_color = newValue
}
}
}
Now all that's left to do is create your view and pass in the binding. The implementation details are opaque to the View since it is managed by the ViewModel.
struct EditView: View {
#StateObject var vm = EditViewModel()
var body: some View {
Form {
ColorPicker("Color", selection: $vm.color)
}
}
}
If you need even more control, you can additionally compare the last committed color to the new value and only commit if there is a significant change.
I will also agree that a data-driven approach like SwiftUI's can make undo more tricky. For example, when you need to coalesce multiple operations together into one undo group. By its very nature an undo group is procedural-- the user did one thing after the other, and finally terminates on some condition. But I'm sure there is some way to encapsulate this step-wise operation in a transaction object of some kind.
Undo is hard!
I'm using MongoDB with NodeJS. Therefore I use mongoose.
I'm developing a multi player real time game. So I receive many requests from many players sometimes at the very same time.
I can simplify it by saying that I have a house collection, that looks like this:
{
"_id" : 1,
"items": [item1, item2, item3]
}
I have a static function, called after each request is received:
house.statics.addItem = function(id, item, callback){
var HouseModel = this;
HouseModel.findById(id, function(err, house){
if (err) throw err;
//make some calculations such as:
if (house.items.length < 4){
HouseModel.findByIdAndUpdate(id, {$push: {items: item}}, cb);
}
});
}
In this example, I coded so that the house document can never have more than 4 items. But what happens is that when I receive several request at the very same time, this function is executed twice by both requests and since it is asynchronous, they both push a new item to the items field and then my house has 5 items.
I am doing something wrong? How can I avoid that behavior in the future?
yes, you need better locking on the houseModel, to indicate that an addItem
is in progress.
The problem is that multiple requests can call findById and see the same
house.items.length, then each determine based on that (outdated) snapshot
that it is ok to add one more item. The nodejs boundary of atomicity is the
callback; between an async call and its callback, other requests can run.
One easy fix is to track not just the number of items in the house but the
number of intended addItems as well. On entry into addItem, bump the "want
to add more" count, and test that.
One possible approach since the release of Mongoose 4.10.8 is writing a plugin which makes save() fail if the document has been modified since you loaded it. A partial example is referenced in #4004:
#vkarpov15 said:
8b4870c should give you the general direction of how one would write a plugin for this
Since Mongoose 4.10.8, plugins now have access to this.$where. For documents which have been loaded from the database (i.e., are not this.isNew), the plugin can add conditions which will be evaluated by MongoDB during the update which can prevent the update from actually happening. Also, if a schema’s saveErrorIfNotFound option is enabled, the save() will return an error instead of succeeding if the document failed to save.
By writing such a plugin and changing some property (such as a version number) on every update to the document, you can implement “optimistic concurrency” (as #4004 is titled). I.e., you can write code that roughly does findOne(), do some modification logic, save(), if (ex) retry(). If all you care about is a document remaining self-consistent and ensuring that Mongoose’s validators run and your document is not highly contentious, this lets you write code that is simple (no need to use something which bypasses Mongoose’s validators like .update()) without sacrificing safety (i.e., you can reject save()s if the document was modified in the meantime and avoid overwriting committed changes).
Sorry, I do not have a code example yet nor do I know if there is a package on npm which implements this pattern as a plugin yet.
I am also building a multiplayer game and ran into the same issue. I believe I have solved it my implementing a queue-like structure:
class NpcSaveQueue {
constructor() {
this.queue = new Map();
this.runQueue();
}
addToQueue(unitId, obj) {
if (!this.queue.has(unitId)) {
this.queue.set(String(unitId), obj);
} else {
this.queue.set(String(unitId), {
...this.queue.get(unitId),
...obj,
})
}
}
emptyUnitQueue(unitId) {
this.queue.delete(unitId);
}
async executeUnitQueue(unitId) {
await NPC.findByIdAndUpdate(unitId, this.queue.get(unitId));
this.emptyUnitQueue(unitId);
}
runQueue() {
setInterval(() => {
this.queue.forEach((value, key) => {
this.executeUnitQueue(key);
})
}, 1000)
}
}
Then when I want to update an NPC, instead of interacting with Mongoose directly, I run:
npcSaveQueue.addToQueue(unit._id, {
"location.x": newLocation.x,
"location.y": newLocation.y,
});
That way, every second, the SaveQueue just executes all code for every NPC that requires updating.
This function never executes twice, because update operation is atomic on a level of single document.
More info in official manual: http://docs.mongodb.org/manual/core/write-operations-atomicity/#atomicity-and-transactions
I am trying to figure out, if a user is dragging a tab, any tab. I don't care which tab it is, I just need to know, if any tab is being dragged.
What is the best way to do this?
Please note: I asked a similar question. However, in that other question I wanted to know, when dragging stopped so I could perform my move operation. The solution given there (retrying until it works) doesn't seem to apply to this new question.
There is no way to tell whether any tab is being "dragged" (=mouse button held down on a tab).
If you want to know that a tab drag has occurred (opposed to "is about to happen"), then you could use the chrome.tabs.onMoved (moved within a tab) and/or chrome.tabs.onAttached / chrome.tabs.onDetached events.
I built a solution based on the fact that Chrome doesn't allow the moving of tabs in the window that contains a tab that is currently being dragged.
In this case, chrome.runtime.lastError.message will be Tabs cannot be edited right now (user may be dragging a tab).
I utilize this by getting the first tab of the focused window and moving it to its index. Because I use its own index, there isn't actually a visual change, when the operation succeeds.
var Chrome = {
isUserDragging: function (callback) {
chrome.windows.getAll({ populate: true }, function (windows) {
var window = windows.filter(function (x) {
return x.type === 'normal' && x.focused && x.tabs
&& x.tabs.length;
})[0];
if (window === undefined)
return;
var tab = window.tabs[0];
chrome.tabs.move(tab.id, { index: tab.index }, function () {
callback(
chrome.runtime.lastError !== undefined &&
chrome.runtime.lastError.message.indexOf('dragging') !== -1);
});
});
}
}
Usage would be:
Chrome.isUserDragging(function(userIsDragging) {
if(userIsDragging)
// do something
else
// do something else
});
Now, based on this, I built a polling mechanism using setTimeout that checks periodically if the user is still dragging and executes an action, when the user stopped dragging.
The full implementation can be seen here and it uses these two helper classes.