Permalink
Browse files

Fix the color picker when used from JavaScript

Fixes #417

In short, the listener would be overridden each time a new player
created a color picker. This would normally work fine, unless multiple
players were using the color picker at the same time.

Fix this by introducing a ColorPickerManager that coordinates between
the active color pickers for multiple players, making sure that only a
single event listener exists at a given time.
  • Loading branch information...
RussellLVP committed Jan 8, 2017
1 parent 7cfbcaa commit e78b4589e376ce00245ae3d7409a4f087372368f
Showing with 59 additions and 32 deletions.
  1. +15 −32 javascript/components/dialogs/color_picker.js
  2. +44 −0 javascript/components/dialogs/color_picker_manager.js
@@ -2,7 +2,7 @@
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.
const ScopedCallbacks = require('base/scoped_callbacks.js');
const ColorPickerManager = require('components/dialogs/color_picker_manager.js');
// Private symbol ensuring that the ColorPicker constructor won't be used.
const PrivateSymbol = Symbol('Please use the static methods.');
@@ -15,11 +15,12 @@ class ColorPicker {
// either selected a color, or dismissed the dialog.
static show(player) {
const colorPicker = new ColorPicker(PrivateSymbol, player);
ColorPickerManager.register(player, colorPicker);
// Immediately show the color picker to the user.
colorPicker.showPicker();
return colorPicker.finished;
return colorPicker.showPicker().then(color => {
ColorPickerManager.unregister(player);
return color;
});
}
constructor(privateSymbol, player) {
@@ -29,46 +30,28 @@ class ColorPicker {
this.player_ = player;
this.resolve_ = null;
this.reject_ = null;
this.finished_ = new Promise((resolve, reject) => {
this.resolve_ = resolve;
this.reject_ = reject;
});
this.callbacks_ = new ScopedCallbacks();
this.callbacks_.addEventListener(
'colorpickerresponse', ColorPicker.prototype.onColorPicked.bind(this));
}
// Returns the promise that is to be resolved or rejected when the color has been picked.
get finished() { return this.finished_; }
// Displays the color picker to the user. Sends a request to Pawn to display it immediately,
// which will respond to JavaScript through an event that will be listened to.
showPicker() {
if (server.isTest()) {
this.onColorPicked({ playerid: this.player_.id, color: Color.RED.toNumberRGBA() });
return;
}
Promise.resolve().then(() => {
if (server.isTest())
this.didSelectColor(Color.RED);
else
pawnInvoke('OnColorPickerRequest', 'i', this.player_.id);
});
Promise.resolve().then(() =>
pawnInvoke('OnColorPickerRequest', 'i', this.player_.id));
return this.finished_;
}
// Called when a color has been picked for a player. Will resolve the promise if that player
// happens to be the one this instance exists for.
onColorPicked(event) {
const player = server.playerManager.getById(event.playerid);
if (!player) {
console.log('[ColorPicker] Received a `colorpicked` event for an invalid player: ' +
event.playerid);
return;
}
this.callbacks_.dispose();
this.resolve_(event.color != 0 ? Color.fromNumberRGBA(event.color)
: null /* dismissed */);
didSelectColor(color) {
this.resolve_(color);
}
}
@@ -0,0 +1,44 @@
// Copyright 2017 Las Venturas Playground. All rights reserved.
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.
// Registry of active color pickers on Las Venturas Playground.
const registry = new WeakMap();
// Manages the color pickers, displayed through Pawn, that players may have requested from features
// which are implemented in JavaScript. A central registry is required as multiple people may be
// using the color pickers at the same time.
class ColorPickerManager {
// Registers the |colorPicker| as being active for |player|.
static register(player, colorPicker) {
if (registry.has(player))
ColorPickerManager.sendResult(player, null /* dismissed */);
registry.set(player, colorPicker);
}
// Shares that the |player| has selected the |value|, which may be NULL, with the color picker
// that has been created on their behalf.
static sendResult(player, value) {
const colorPicker = registry.get(player);
if (!colorPicker)
return;
colorPicker.didSelectColor(value);
}
// Unregisters any color picker that may be active for the |player|.
static unregister(player) {
registry.delete(player);
}
}
// Listens to `colorpickerresponse` events triggered by the Pawn code.
global.addEventListener('colorpickerresponse', event => {
const player = server.playerManager.getById(event.playerid);
const color = event.color ? Color.fromNumberRGBA(event.color) : null /* dismissed */;
ColorPickerManager.sendResult(player, color);
});
exports = ColorPickerManager;

0 comments on commit e78b458

Please sign in to comment.