forked from cory/tildefriends
Add some GPS game tabs.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4446 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
35475defb5
commit
45231c6ede
@ -6,11 +6,9 @@
|
|||||||
let g_data = ${data};
|
let g_data = ${data};
|
||||||
</script>
|
</script>
|
||||||
<script src="script.js" type="module"></script>
|
<script src="script.js" type="module"></script>
|
||||||
<link rel="stylesheet" href="leaflet.css"/>
|
|
||||||
<script src="leaflet.js"></script>
|
<script src="leaflet.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body style="color: #fff; display: flex; flex-flow: column; height: 100%; width: 100%; margin: 0; padding: 0">
|
<body style="color: #fff; display: flex; flex-flow: column; height: 100%; width: 100%; margin: 0; padding: 0">
|
||||||
<gg-app style="flex: 0 1 auto; overflow: scroll"></gg-app>
|
<gg-app style="width: 100%; height: 100%"></gg-app>
|
||||||
<div id="map" style="flex: 1 0"></div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -24,6 +24,9 @@ class GgAppElement extends LitElement {
|
|||||||
world: {type: Object},
|
world: {type: Object},
|
||||||
id: {type: String},
|
id: {type: String},
|
||||||
status: {type: Object},
|
status: {type: Object},
|
||||||
|
tab: {type: String},
|
||||||
|
url: {type: String},
|
||||||
|
currency: {type: Number},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +40,9 @@ class GgAppElement extends LitElement {
|
|||||||
this.min_lon = Number.MAX_VALUE;
|
this.min_lon = Number.MAX_VALUE;
|
||||||
this.max_lat = -Number.MAX_VALUE;
|
this.max_lat = -Number.MAX_VALUE;
|
||||||
this.max_lon = -Number.MAX_VALUE;
|
this.max_lon = -Number.MAX_VALUE;
|
||||||
|
this.focus = undefined;
|
||||||
this.status = undefined;
|
this.status = undefined;
|
||||||
|
this.tab = 'map';
|
||||||
this.load().catch(function(e) {
|
this.load().catch(function(e) {
|
||||||
console.log('load error', e);
|
console.log('load error', e);
|
||||||
});
|
});
|
||||||
@ -46,6 +51,7 @@ class GgAppElement extends LitElement {
|
|||||||
async load() {
|
async load() {
|
||||||
console.log('load');
|
console.log('load');
|
||||||
this.user = await tfrpc.rpc.getUser();
|
this.user = await tfrpc.rpc.getUser();
|
||||||
|
this.url = (await tfrpc.rpc.url()).split('?')[0];
|
||||||
try {
|
try {
|
||||||
await this.update_credentials();
|
await this.update_credentials();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -84,8 +90,8 @@ class GgAppElement extends LitElement {
|
|||||||
async get_activities_from_ssb() {
|
async get_activities_from_ssb() {
|
||||||
this.status = {text: 'loading activities'};
|
this.status = {text: 'loading activities'};
|
||||||
this.loaded_activities = [];
|
this.loaded_activities = [];
|
||||||
let blob_ids = await tfrpc.rpc.query(`
|
let rows = await tfrpc.rpc.query(`
|
||||||
SELECT json_extract(mention.value, '$.link') AS blob_id
|
SELECT messages.author, json_extract(mention.value, '$.link') AS blob_id
|
||||||
FROM messages_fts('"gg-activity"')
|
FROM messages_fts('"gg-activity"')
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid,
|
JOIN messages ON messages.rowid = messages_fts.rowid,
|
||||||
json_each(messages.content, '$.mentions') as mention
|
json_each(messages.content, '$.mentions') as mention
|
||||||
@ -94,15 +100,26 @@ class GgAppElement extends LitElement {
|
|||||||
ORDER BY messages.timestamp DESC
|
ORDER BY messages.timestamp DESC
|
||||||
`, []);
|
`, []);
|
||||||
this.status = {text: 'loading activity data'};
|
this.status = {text: 'loading activity data'};
|
||||||
let blobs = await this.promise_all(blob_ids.map(x => tfrpc.rpc.get_blob(x.blob_id)), 8);
|
let authors = rows.map(x => x.author);
|
||||||
|
let blobs = await this.promise_all(rows.map(x => tfrpc.rpc.get_blob(x.blob_id)), 8);
|
||||||
this.status = {text: 'processing activity data'};
|
this.status = {text: 'processing activity data'};
|
||||||
for (let blob of blobs) {
|
for (let [index, blob] of blobs.entries()) {
|
||||||
|
let activity;
|
||||||
try {
|
try {
|
||||||
this.loaded_activities.push(JSON.parse(blob));
|
activity = JSON.parse(blob);
|
||||||
} catch {
|
} catch {
|
||||||
this.loaded_activities.push(gpx_parse(blob));
|
activity = gpx_parse(blob);
|
||||||
|
}
|
||||||
|
if (activity) {
|
||||||
|
activity.author = authors[index];
|
||||||
|
this.loaded_activities.push(activity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.status = {text: 'calculating balance'};
|
||||||
|
rows = await tfrpc.rpc.query(`
|
||||||
|
SELECT count(*) AS currency FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'gg-activity'
|
||||||
|
`, [this.id]);
|
||||||
|
this.currency = rows[0].currency;
|
||||||
this.status = undefined;
|
this.status = undefined;
|
||||||
this.update_map();
|
this.update_map();
|
||||||
}
|
}
|
||||||
@ -278,8 +295,14 @@ class GgAppElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update_map() {
|
async update_map() {
|
||||||
|
let map = this.shadowRoot.getElementById('map');
|
||||||
|
if (!map || !this.loaded_activities.length) {
|
||||||
|
this.leaflet = undefined;
|
||||||
|
this.grid_layer = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.leaflet) {
|
if (!this.leaflet) {
|
||||||
this.leaflet = L.map('map', {attributionControl: false, maxZoom: 16, bounceAtZoomLimits: false});
|
this.leaflet = L.map(map, {attributionControl: false, maxZoom: 16, bounceAtZoomLimits: false});
|
||||||
this.leaflet.on({click: this.on_click.bind(this)});
|
this.leaflet.on({click: this.on_click.bind(this)});
|
||||||
}
|
}
|
||||||
let self = this;
|
let self = this;
|
||||||
@ -295,9 +318,6 @@ class GgAppElement extends LitElement {
|
|||||||
let degrees = 360.0 / (2 ** coords.z);
|
let degrees = 360.0 / (2 ** coords.z);
|
||||||
let ul = bounds.getNorthWest();
|
let ul = bounds.getNorthWest();
|
||||||
let lr = bounds.getSouthEast();
|
let lr = bounds.getSouthEast();
|
||||||
//context.fillText(JSON.stringify(coords), 0, 12);
|
|
||||||
//context.fillText(`${Math.round(ul.lat * 100) / 100} ${Math.round(ul.lng * 100) / 100}`, 0, 24);
|
|
||||||
//context.fillText(`${Math.round(lr.lat * 100) / 100} ${Math.round(lr.lng * 100) / 100}`, 0, 36);
|
|
||||||
|
|
||||||
let mini = document.createElement('canvas');
|
let mini = document.createElement('canvas');
|
||||||
mini.width = Math.floor(size.x / 16.0);
|
mini.width = Math.floor(size.x / 16.0);
|
||||||
@ -307,7 +327,6 @@ class GgAppElement extends LitElement {
|
|||||||
for (let activity of self.loaded_activities) {
|
for (let activity of self.loaded_activities) {
|
||||||
self.draw_activity_to_tile(image_data, mini.width, mini.height, ul, lr, activity);
|
self.draw_activity_to_tile(image_data, mini.width, mini.height, ul, lr, activity);
|
||||||
}
|
}
|
||||||
//mini_context.putImageData(image_data, 0, 0);
|
|
||||||
for (let x = 0; x < mini.width; x++) {
|
for (let x = 0; x < mini.width; x++) {
|
||||||
for (let y = 0; y < mini.height; y++) {
|
for (let y = 0; y < mini.height; y++) {
|
||||||
let start = (y * mini.width + x) * 4;
|
let start = (y * mini.width + x) * 4;
|
||||||
@ -333,11 +352,19 @@ class GgAppElement extends LitElement {
|
|||||||
this.max_lat = Math.max(this.max_lat, bounds.max.lat);
|
this.max_lat = Math.max(this.max_lat, bounds.max.lat);
|
||||||
this.max_lon = Math.max(this.max_lon, bounds.max.lng);
|
this.max_lon = Math.max(this.max_lon, bounds.max.lng);
|
||||||
}
|
}
|
||||||
|
if (this.focus) {
|
||||||
|
this.leaflet.fitBounds([
|
||||||
|
this.focus.min,
|
||||||
|
this.focus.max,
|
||||||
|
]);
|
||||||
|
this.focus = undefined;
|
||||||
|
} else {
|
||||||
this.leaflet.fitBounds([
|
this.leaflet.fitBounds([
|
||||||
[this.min_lat, this.min_lon],
|
[this.min_lat, this.min_lon],
|
||||||
[this.max_lat, this.max_lon],
|
[this.max_lat, this.max_lon],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
activity_to_color(activity) {
|
activity_to_color(activity) {
|
||||||
let color = [0, 0, 0, 255];
|
let color = [0, 0, 0, 255];
|
||||||
@ -529,24 +556,53 @@ class GgAppElement extends LitElement {
|
|||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
updated() {
|
||||||
if (!this.user?.credentials?.session?.name) {
|
this.update_map();
|
||||||
return html`<div>Please <a target="_top" href="/login">login</a> to Tilde Friends, first.</div>`;
|
|
||||||
}
|
}
|
||||||
if (!this.strava?.access_token) {
|
|
||||||
let strava_url = `https://www.strava.com/oauth/authorize?client_id=${k_client_id}&redirect_uri=${k_redirect_url}&response_type=code&approval_prompt=auto&scope=activity%3Aread&state=${g_data.state}`;
|
focus_map(activity) {
|
||||||
|
let bounds = this.activity_bounds(activity);
|
||||||
|
if (bounds.min.lat < bounds.max.lat &&
|
||||||
|
bounds.min.lng < bounds.max.lng) {
|
||||||
|
this.tab = 'map';
|
||||||
|
this.focus = bounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render_news() {
|
||||||
return html`
|
return html`
|
||||||
<div style="display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%">
|
<ul>
|
||||||
|
${this.loaded_activities.map(x => html`
|
||||||
|
<li style="cursor: pointer" @click=${() => this.focus_map(x)}>${x.author} ${x.name ?? x.time}</li>
|
||||||
|
`)}
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render_store() {
|
||||||
|
return html`
|
||||||
|
<h2>Store</h2>
|
||||||
|
<div><b>Your balance:</b> ${this.currency}</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let header;
|
||||||
|
if (!this.user?.credentials?.session?.name) {
|
||||||
|
header = html`<div style="flex: 1 0">Please <a target="_top" href="/login?return=${this.url}">login</a> to Tilde Friends, first.</div>`;
|
||||||
|
} else if (!this.strava?.access_token) {
|
||||||
|
let strava_url = `https://www.strava.com/oauth/authorize?client_id=${k_client_id}&redirect_uri=${k_redirect_url}&response_type=code&approval_prompt=auto&scope=activity%3Aread&state=${g_data.state}`;
|
||||||
|
header = html`
|
||||||
|
<div style="flex: 1 0; display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%">
|
||||||
<div style="flex: 1 1">Please <a target="_top" href=${strava_url}>login</a> to Strava.</div>
|
<div style="flex: 1 1">Please <a target="_top" href=${strava_url}>login</a> to Strava.</div>
|
||||||
<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.id}</span>
|
<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.id}</span>
|
||||||
<input type="button" value="📁" @click=${this.upload}></input>
|
<input type="button" value="📁" @click=${this.upload}></input>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
} else {
|
||||||
|
header = html`
|
||||||
return html`
|
|
||||||
<div>
|
<div>
|
||||||
<div style="display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%">
|
<div style="flex: 1 0; display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%">
|
||||||
<h1>Welcome, ${this.user.credentials.session.name}</h1>
|
<h1>Welcome, ${this.user.credentials.session.name}</h1>
|
||||||
<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.id}</span>
|
<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.id}</span>
|
||||||
<input type="button" value="📁" @click=${this.upload}></input>
|
<input type="button" value="📁" @click=${this.upload}></input>
|
||||||
@ -555,5 +611,48 @@ class GgAppElement extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let navigation = html`
|
||||||
|
<style>
|
||||||
|
#navigation input[type="button"] {
|
||||||
|
min-width: 3em;
|
||||||
|
min-height: 3em;
|
||||||
|
flex: 1 0;
|
||||||
|
font-size: large;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="navigation" style="display: flex; flex-direction: row">
|
||||||
|
<input type="button" id="button_map" @click=${() => this.tab = 'map'} value="🗺️Map"></input>
|
||||||
|
<input type="button" id="button_news" @click=${() => this.tab = 'news'} value="🏃News"></input>
|
||||||
|
<input type="button" id="button_friends" @click=${() => this.tab = 'friends'} value="👫Friends"></input>
|
||||||
|
<input type="button" id="button_store" @click=${() => this.tab = 'store'} value="🏗️Store"></input>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
switch (this.tab) {
|
||||||
|
case 'map':
|
||||||
|
content = html`<div id="map" style="width: 100%; height: 100%"></div>`;
|
||||||
|
break;
|
||||||
|
case 'news':
|
||||||
|
content = this.render_news();
|
||||||
|
break;
|
||||||
|
case 'friends':
|
||||||
|
content = html`<div>Friends</div>`;
|
||||||
|
break;
|
||||||
|
case 'store':
|
||||||
|
content = this.render_store();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<link rel="stylesheet" href="leaflet.css"/>
|
||||||
|
<div style="width: 100%; height: 100%; display: flex; flex-direction: column">
|
||||||
|
${header}
|
||||||
|
<div style="flex: 1 0; overflow: scroll">${content}</div>
|
||||||
|
${navigation}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
customElements.define('gg-app', GgAppElement);
|
customElements.define('gg-app', GgAppElement);
|
Loading…
Reference in New Issue
Block a user