Allow logging in with Discord in horde-map

master
Nikola Forró 6 years ago
parent fc0e42dede
commit d772ffecbc

@ -5,16 +5,17 @@
min-height: 600px; min-height: 600px;
} }
.add-control { .discord-control {
top: 4em; top: 4em;
left: 0.5em; left: 0.5em;
} }
.ol-touch .add-control { .ol-touch .discord-control {
top: 5em; top: 5em;
} }
.add-control button { .discord-control button {
width: 8em; width: auto;
padding: 0.5em;
font-size: 11pt; font-size: 11pt;
} }

@ -1,7 +1,11 @@
const apiUrl = '/horde-members/api/features'; const membersApiUrl = 'https://lilia.mraveniste.cc/horde-members/api/members';
const featuresApiUrl = 'https://lilia.mraveniste.cc/horde-members/api/features';
const apiKey = 'eyJhbGciOiJIUzUxMiIsImlhdCI6MTU1Mzg5MjYwNSwiZXhwIjoxODY5MjUyNjA1fQ.ImNsaWVudCI.iX2EEEsKbMYpbeU-HB_FcqeepwZn8rkq8XdyfUmr6RJk2-64I744xLdKfrikxskF6_IlJbjBH3jNNcVfWyvswQ'; const apiKey = 'eyJhbGciOiJIUzUxMiIsImlhdCI6MTU1Mzg5MjYwNSwiZXhwIjoxODY5MjUyNjA1fQ.ImNsaWVudCI.iX2EEEsKbMYpbeU-HB_FcqeepwZn8rkq8XdyfUmr6RJk2-64I744xLdKfrikxskF6_IlJbjBH3jNNcVfWyvswQ';
var nick = ''; const discordClientId = '562565397451374593';
const cheeseHordeGuildId = '285154599038353408';
var discordUser = undefined;
function store(feature) { function store(feature) {
var writer = new ol.format.GeoJSON({ var writer = new ol.format.GeoJSON({
@ -10,7 +14,7 @@ function store(feature) {
}); });
$.ajax({ $.ajax({
url: apiUrl, url: featuresApiUrl,
type: 'post', type: 'post',
contentType: 'application/json', contentType: 'application/json',
data: writer.writeFeature(feature), data: writer.writeFeature(feature),
@ -18,6 +22,14 @@ function store(feature) {
}); });
} }
function remove(id) {
$.ajax({
url: `${membersApiUrl}/${id}`,
type: 'delete',
headers: {'X-Members-API-Key': apiKey}
});
}
function getColor(nick, alpha) { function getColor(nick, alpha) {
var r = 0; var r = 0;
var g = 0; var g = 0;
@ -53,15 +65,55 @@ function getColor(nick, alpha) {
return `rgba(${r}, ${g}, ${b}, ${alpha})`; return `rgba(${r}, ${g}, ${b}, ${alpha})`;
} }
var AddControl = (function (Control) { var discord = new LoginWithDiscord({
function AddControl(opt_options) { clientID: discordClientId,
scopes: [
Scope.Identify,
Scope.Guilds
],
cache: true
});
discord.onlogin = async () => {
var user = await discord.fetchUser();
var guilds = await discord.fetchGuilds();
var horde = guilds.find((guild) => guild.id == cheeseHordeGuildId);
if (horde !== undefined) {
discordUser = user;
var button = discordControl.element.firstElementChild;
button.innerHTML = `${discordUser.username}: Log out`;
button.style.display = null;
}
}
discord.onlogout = async () => {
discordUser = undefined;
var button = discordControl.element.firstElementChild;
button.innerHTML = 'Log in with Discord';
button.style.display = null;
};
async function discordLogin() {
await discord.login();
}
async function discordLogout() {
await discord.logout();
}
var DiscordControl = (function(Control) {
function DiscordControl(opt_options) {
var options = opt_options || {}; var options = opt_options || {};
var button = document.createElement('button'); var button = document.createElement('button');
button.innerHTML = 'Add yourself'; button.style.display = 'none';
var element = document.createElement('div'); var element = document.createElement('div');
element.className = 'add-control ol-unselectable ol-control'; element.className = 'discord-control ol-unselectable ol-control';
element.appendChild(button); element.appendChild(button);
Control.call(this, { Control.call(this, {
@ -69,24 +121,29 @@ var AddControl = (function (Control) {
target: options.target target: options.target
}); });
button.addEventListener('click', this.handleAdd.bind(this), false); button.addEventListener('click', this.handleClick.bind(this), false);
} }
if (Control) if (Control)
AddControl.__proto__ = Control; DiscordControl.__proto__ = Control;
AddControl.prototype = Object.create(Control && Control.prototype); DiscordControl.prototype = Object.create(Control && Control.prototype);
AddControl.prototype.constructor = AddControl; DiscordControl.prototype.constructor = DiscordControl;
AddControl.prototype.handleAdd = function handleAdd() { DiscordControl.prototype.handleClick = function() {
nick = prompt('Please enter your nickname', ''); if (!discordUser)
discordLogin();
else
discordLogout();
}; };
return AddControl; return DiscordControl;
}(ol.control.Control)); }(ol.control.Control));
var discordControl = new DiscordControl();
var source = new ol.source.Vector({ var source = new ol.source.Vector({
url: apiUrl, url: featuresApiUrl,
wrapX: false, wrapX: false,
format: new ol.format.GeoJSON({ format: new ol.format.GeoJSON({
dataProjection: 'EPSG:4326', dataProjection: 'EPSG:4326',
@ -136,7 +193,7 @@ var map = new ol.Map({
zoom: 2 zoom: 2
}), }),
controls: ol.control.defaults().extend([ controls: ol.control.defaults().extend([
new AddControl() discordControl
]) ])
}); });
@ -149,7 +206,7 @@ map.addInteraction(snap);
var modify = new ol.interaction.Modify({ var modify = new ol.interaction.Modify({
source: source, source: source,
condition: (evt) => { condition: (evt) => {
if (!nick) if (!discordUser)
return false; return false;
var feature = source.getClosestFeatureToCoordinate(evt.coordinate); var feature = source.getClosestFeatureToCoordinate(evt.coordinate);
@ -157,7 +214,7 @@ var modify = new ol.interaction.Modify({
if (!feature) if (!feature)
return false; return false;
return feature.get('current') !== undefined; return feature.get('id') == discordUser.id;
} }
}); });
@ -173,23 +230,20 @@ var draw = new ol.interaction.Draw({
source: source, source: source,
type: 'Point', type: 'Point',
condition: (evt) => { condition: (evt) => {
if (!nick) if (!discordUser)
return false; return false;
var feature = source.forEachFeature((feature) => { var feature = source.forEachFeature((feature) => {
if (feature.get('nick') == nick) if (feature.get('id') == discordUser.id)
return feature; return feature;
}); });
if (feature === undefined) { if (feature === undefined)
return true; return true;
}
if (feature.get('current') === undefined)
return false;
var features = []; var features = [];
// FIXME: allows duplication
map.forEachFeatureAtPixel(evt.pixel, (feature) => { map.forEachFeatureAtPixel(evt.pixel, (feature) => {
features.push(feature); features.push(feature);
}); });
@ -199,18 +253,33 @@ var draw = new ol.interaction.Draw({
}); });
draw.on('drawend', (evt) => { draw.on('drawend', (evt) => {
evt.feature.set('id', discordUser.id);
evt.feature.set('nick', discordUser.username);
store(evt.feature);
});
map.addInteraction(draw);
document.addEventListener('keydown', (evt) => {
if (evt.code != 'Delete')
return;
if (!discordUser)
return;
var feature = source.forEachFeature((feature) => { var feature = source.forEachFeature((feature) => {
if (feature.get('nick') == nick) if (feature.get('id') == discordUser.id)
return feature; return feature;
}); });
if (feature !== undefined) if (feature === undefined)
source.removeFeature(feature); return;
evt.feature.set('nick', nick); if (confirm('Are you sure you want to remove yourself?'))
evt.feature.set('current', true); {
source.removeFeature(feature);
store(evt.feature); remove(discordUser.id);
}
}); });
map.addInteraction(draw);

@ -37,7 +37,7 @@ api = flask_restful.Api(app)
member_fields = { member_fields = {
'id': flask_restful.fields.Integer(), 'id': flask_restful.fields.String(),
'nick': flask_restful.fields.String(), 'nick': flask_restful.fields.String(),
'x': flask_restful.fields.Float(), 'x': flask_restful.fields.Float(),
'y': flask_restful.fields.Float(), 'y': flask_restful.fields.Float(),
@ -49,6 +49,7 @@ geometry_fields = {
'coordinates': flask_restful.fields.List(flask_restful.fields.Float()), 'coordinates': flask_restful.fields.List(flask_restful.fields.Float()),
} }
properties_fields = { properties_fields = {
'id': flask_restful.fields.String(),
'nick': flask_restful.fields.String(), 'nick': flask_restful.fields.String(),
} }
feature_fields = { feature_fields = {
@ -63,7 +64,7 @@ features_fields = {
member_parser = flask_restful.reqparse.RequestParser() member_parser = flask_restful.reqparse.RequestParser()
member_parser.add_argument('id', type=int) member_parser.add_argument('id', type=str, required=True)
member_parser.add_argument('nick', type=str, required=True) member_parser.add_argument('nick', type=str, required=True)
member_parser.add_argument('x', type=float, required=True) member_parser.add_argument('x', type=float, required=True)
member_parser.add_argument('y', type=float, required=True) member_parser.add_argument('y', type=float, required=True)
@ -86,6 +87,7 @@ geometry_parser.add_argument('coordinates', type=float, action='append',
location=('geometry',), required=True) location=('geometry',), required=True)
properties_parser = flask_restful.reqparse.RequestParser() properties_parser = flask_restful.reqparse.RequestParser()
properties_parser.add_argument('id', type=str, location=('properties',), required=True)
properties_parser.add_argument('nick', type=str, location=('properties',), required=True) properties_parser.add_argument('nick', type=str, location=('properties',), required=True)
@ -112,6 +114,7 @@ def member2feature(member):
], ],
}, },
'properties': { 'properties': {
'id': member.id,
'nick': member.nick, 'nick': member.nick,
}, },
} }
@ -187,6 +190,8 @@ class MembersResource(flask_restful.Resource):
@flask_restful.marshal_with(member_fields) @flask_restful.marshal_with(member_fields)
def post(self): def post(self):
args = member_parser.parse_args() args = member_parser.parse_args()
if not args['id']:
flask_restful.abort(400, message='Missing required parameter id')
if not args['nick']: if not args['nick']:
flask_restful.abort(400, message='Missing required parameter nick') flask_restful.abort(400, message='Missing required parameter nick')
if not args['x']: if not args['x']:
@ -194,11 +199,12 @@ class MembersResource(flask_restful.Resource):
if not args['y']: if not args['y']:
flask_restful.abort(400, message='Missing required parameter y') flask_restful.abort(400, message='Missing required parameter y')
now = sqlalchemy.func.now() now = sqlalchemy.func.now()
q = db.session.query(Member).filter(Member.user == args['nick']) q = db.session.query(Member).filter(Member.id == args['id'])
member = q.first() member = q.first()
if not member: if not member:
member = Member(created_at=now) member = Member(created_at=now)
member.user = args['nick'] member.id = args['id']
member.nick = args['nick']
member.x, member.y = normalize(args['x'], args['y']) member.x, member.y = normalize(args['x'], args['y'])
member.updated_at = now member.updated_at = now
db.session.add(member) db.session.add(member)
@ -235,10 +241,11 @@ class FeaturesResource(flask_restful.Resource):
if geometry_args['type'] != 'Point': if geometry_args['type'] != 'Point':
flask_restful.abort(400, message='Unexpected geometry type') flask_restful.abort(400, message='Unexpected geometry type')
now = sqlalchemy.func.now() now = sqlalchemy.func.now()
q = db.session.query(Member).filter(Member.nick == properties_args['nick']) q = db.session.query(Member).filter(Member.id == properties_args['id'])
member = q.first() member = q.first()
if not member: if not member:
member = Member(created_at=now) member = Member(created_at=now)
member.id = properties_args['id']
member.nick = properties_args['nick'] member.nick = properties_args['nick']
member.x, member.y = normalize(*geometry_args['coordinates']) member.x, member.y = normalize(*geometry_args['coordinates'])
member.updated_at = now member.updated_at = now

@ -7,7 +7,7 @@ db = flask_sqlalchemy.SQLAlchemy(session_options=dict(autoflush=False))
class Member(db.Model): class Member(db.Model):
__tablename__ = 'members' __tablename__ = 'members'
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.String, primary_key=True)
nick = db.Column(db.String) nick = db.Column(db.String)
x = db.Column(db.Float) x = db.Column(db.Float)
y = db.Column(db.Float) y = db.Column(db.Float)

Loading…
Cancel
Save