From d772ffecbc302c29838f85371abc572ff0235506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Forr=C3=B3?= Date: Tue, 2 Apr 2019 15:10:37 +0200 Subject: [PATCH] Allow logging in with Discord in horde-map --- horde-map/map.css | 9 +-- horde-map/map.js | 135 +++++++++++++++++++++++++++++---------- horde-members-api/app.py | 17 +++-- horde-members-api/db.py | 2 +- 4 files changed, 120 insertions(+), 43 deletions(-) diff --git a/horde-map/map.css b/horde-map/map.css index 66843f3..f0ecfd5 100644 --- a/horde-map/map.css +++ b/horde-map/map.css @@ -5,16 +5,17 @@ min-height: 600px; } -.add-control { +.discord-control { top: 4em; left: 0.5em; } -.ol-touch .add-control { +.ol-touch .discord-control { top: 5em; } -.add-control button { - width: 8em; +.discord-control button { + width: auto; + padding: 0.5em; font-size: 11pt; } diff --git a/horde-map/map.js b/horde-map/map.js index e6c1c7a..a37c0ca 100644 --- a/horde-map/map.js +++ b/horde-map/map.js @@ -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'; -var nick = ''; +const discordClientId = '562565397451374593'; +const cheeseHordeGuildId = '285154599038353408'; + +var discordUser = undefined; function store(feature) { var writer = new ol.format.GeoJSON({ @@ -10,7 +14,7 @@ function store(feature) { }); $.ajax({ - url: apiUrl, + url: featuresApiUrl, type: 'post', contentType: 'application/json', 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) { var r = 0; var g = 0; @@ -53,15 +65,55 @@ function getColor(nick, alpha) { return `rgba(${r}, ${g}, ${b}, ${alpha})`; } -var AddControl = (function (Control) { - function AddControl(opt_options) { +var discord = new LoginWithDiscord({ + 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 button = document.createElement('button'); - button.innerHTML = 'Add yourself'; + button.style.display = 'none'; var element = document.createElement('div'); - element.className = 'add-control ol-unselectable ol-control'; + element.className = 'discord-control ol-unselectable ol-control'; element.appendChild(button); Control.call(this, { @@ -69,24 +121,29 @@ var AddControl = (function (Control) { target: options.target }); - button.addEventListener('click', this.handleAdd.bind(this), false); + button.addEventListener('click', this.handleClick.bind(this), false); } if (Control) - AddControl.__proto__ = Control; + DiscordControl.__proto__ = Control; - AddControl.prototype = Object.create(Control && Control.prototype); - AddControl.prototype.constructor = AddControl; + DiscordControl.prototype = Object.create(Control && Control.prototype); + DiscordControl.prototype.constructor = DiscordControl; - AddControl.prototype.handleAdd = function handleAdd() { - nick = prompt('Please enter your nickname', ''); + DiscordControl.prototype.handleClick = function() { + if (!discordUser) + discordLogin(); + else + discordLogout(); }; - return AddControl; + return DiscordControl; }(ol.control.Control)); +var discordControl = new DiscordControl(); + var source = new ol.source.Vector({ - url: apiUrl, + url: featuresApiUrl, wrapX: false, format: new ol.format.GeoJSON({ dataProjection: 'EPSG:4326', @@ -136,7 +193,7 @@ var map = new ol.Map({ zoom: 2 }), controls: ol.control.defaults().extend([ - new AddControl() + discordControl ]) }); @@ -149,7 +206,7 @@ map.addInteraction(snap); var modify = new ol.interaction.Modify({ source: source, condition: (evt) => { - if (!nick) + if (!discordUser) return false; var feature = source.getClosestFeatureToCoordinate(evt.coordinate); @@ -157,7 +214,7 @@ var modify = new ol.interaction.Modify({ if (!feature) 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, type: 'Point', condition: (evt) => { - if (!nick) + if (!discordUser) return false; var feature = source.forEachFeature((feature) => { - if (feature.get('nick') == nick) + if (feature.get('id') == discordUser.id) return feature; }); - if (feature === undefined) { + if (feature === undefined) return true; - } - - if (feature.get('current') === undefined) - return false; var features = []; + // FIXME: allows duplication map.forEachFeatureAtPixel(evt.pixel, (feature) => { features.push(feature); }); @@ -199,18 +253,33 @@ var draw = new ol.interaction.Draw({ }); 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) => { - if (feature.get('nick') == nick) + if (feature.get('id') == discordUser.id) return feature; }); - if (feature !== undefined) - source.removeFeature(feature); + if (feature === undefined) + return; - evt.feature.set('nick', nick); - evt.feature.set('current', true); + if (confirm('Are you sure you want to remove yourself?')) + { + source.removeFeature(feature); - store(evt.feature); + remove(discordUser.id); + } }); - -map.addInteraction(draw); diff --git a/horde-members-api/app.py b/horde-members-api/app.py index 5e32a5b..fbe9515 100644 --- a/horde-members-api/app.py +++ b/horde-members-api/app.py @@ -37,7 +37,7 @@ api = flask_restful.Api(app) member_fields = { - 'id': flask_restful.fields.Integer(), + 'id': flask_restful.fields.String(), 'nick': flask_restful.fields.String(), 'x': flask_restful.fields.Float(), 'y': flask_restful.fields.Float(), @@ -49,6 +49,7 @@ geometry_fields = { 'coordinates': flask_restful.fields.List(flask_restful.fields.Float()), } properties_fields = { + 'id': flask_restful.fields.String(), 'nick': flask_restful.fields.String(), } feature_fields = { @@ -63,7 +64,7 @@ features_fields = { 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('x', 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) 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) @@ -112,6 +114,7 @@ def member2feature(member): ], }, 'properties': { + 'id': member.id, 'nick': member.nick, }, } @@ -187,6 +190,8 @@ class MembersResource(flask_restful.Resource): @flask_restful.marshal_with(member_fields) def post(self): args = member_parser.parse_args() + if not args['id']: + flask_restful.abort(400, message='Missing required parameter id') if not args['nick']: flask_restful.abort(400, message='Missing required parameter nick') if not args['x']: @@ -194,11 +199,12 @@ class MembersResource(flask_restful.Resource): if not args['y']: flask_restful.abort(400, message='Missing required parameter y') 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() if not member: 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.updated_at = now db.session.add(member) @@ -235,10 +241,11 @@ class FeaturesResource(flask_restful.Resource): if geometry_args['type'] != 'Point': flask_restful.abort(400, message='Unexpected geometry type') 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() if not member: member = Member(created_at=now) + member.id = properties_args['id'] member.nick = properties_args['nick'] member.x, member.y = normalize(*geometry_args['coordinates']) member.updated_at = now diff --git a/horde-members-api/db.py b/horde-members-api/db.py index 8a832ae..eda5cf6 100644 --- a/horde-members-api/db.py +++ b/horde-members-api/db.py @@ -7,7 +7,7 @@ db = flask_sqlalchemy.SQLAlchemy(session_options=dict(autoflush=False)) class Member(db.Model): __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) x = db.Column(db.Float) y = db.Column(db.Float)