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:
		| @@ -6,11 +6,9 @@ | ||||
| 			let g_data = ${data}; | ||||
| 		</script> | ||||
| 		<script src="script.js" type="module"></script> | ||||
| 		<link rel="stylesheet" href="leaflet.css"/> | ||||
| 		<script src="leaflet.js"></script> | ||||
| 	</head> | ||||
| 	<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> | ||||
| 		<div id="map" style="flex: 1 0"></div> | ||||
| 		<gg-app style="width: 100%; height: 100%"></gg-app> | ||||
| 	</body> | ||||
| </html> | ||||
| @@ -24,6 +24,9 @@ class GgAppElement extends LitElement { | ||||
| 			world: {type: Object}, | ||||
| 			id: {type: String}, | ||||
| 			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.max_lat = -Number.MAX_VALUE; | ||||
| 		this.max_lon = -Number.MAX_VALUE; | ||||
| 		this.focus = undefined; | ||||
| 		this.status = undefined; | ||||
| 		this.tab = 'map'; | ||||
| 		this.load().catch(function(e) { | ||||
| 			console.log('load error', e); | ||||
| 		}); | ||||
| @@ -46,6 +51,7 @@ class GgAppElement extends LitElement { | ||||
| 	async load() { | ||||
| 		console.log('load'); | ||||
| 		this.user = await tfrpc.rpc.getUser(); | ||||
| 		this.url = (await tfrpc.rpc.url()).split('?')[0]; | ||||
| 		try { | ||||
| 			await this.update_credentials(); | ||||
| 		} catch (e) { | ||||
| @@ -84,8 +90,8 @@ class GgAppElement extends LitElement { | ||||
| 	async get_activities_from_ssb() { | ||||
| 		this.status = {text: 'loading activities'}; | ||||
| 		this.loaded_activities = []; | ||||
| 		let blob_ids = await tfrpc.rpc.query(` | ||||
| 			SELECT json_extract(mention.value, '$.link') AS blob_id | ||||
| 		let rows = await tfrpc.rpc.query(` | ||||
| 			SELECT messages.author, json_extract(mention.value, '$.link') AS blob_id | ||||
| 			FROM messages_fts('"gg-activity"') | ||||
| 			JOIN messages ON messages.rowid = messages_fts.rowid, | ||||
| 				json_each(messages.content, '$.mentions') as mention | ||||
| @@ -94,15 +100,26 @@ class GgAppElement extends LitElement { | ||||
| 			ORDER BY messages.timestamp DESC | ||||
| 		`, []); | ||||
| 		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'}; | ||||
| 		for (let blob of blobs) { | ||||
| 		for (let [index, blob] of blobs.entries()) { | ||||
| 			let activity; | ||||
| 			try { | ||||
| 				this.loaded_activities.push(JSON.parse(blob)); | ||||
| 				activity = JSON.parse(blob); | ||||
| 			} 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.update_map(); | ||||
| 	} | ||||
| @@ -278,8 +295,14 @@ class GgAppElement extends LitElement { | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
| 			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)}); | ||||
| 		} | ||||
| 		let self = this; | ||||
| @@ -295,9 +318,6 @@ class GgAppElement extends LitElement { | ||||
| 				let degrees = 360.0 / (2 ** coords.z); | ||||
| 				let ul = bounds.getNorthWest(); | ||||
| 				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'); | ||||
| 				mini.width = Math.floor(size.x / 16.0); | ||||
| @@ -307,7 +327,6 @@ class GgAppElement extends LitElement { | ||||
| 				for (let activity of self.loaded_activities) { | ||||
| 					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 y = 0; y < mini.height; y++) { | ||||
| 						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_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.min_lat, this.min_lon], | ||||
| 				[this.max_lat, this.max_lon], | ||||
| 			]); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	activity_to_color(activity) { | ||||
| 		let color = [0, 0, 0, 255]; | ||||
| @@ -529,24 +556,53 @@ class GgAppElement extends LitElement { | ||||
| 		input.click(); | ||||
| 	} | ||||
|  | ||||
| 	render() { | ||||
| 		if (!this.user?.credentials?.session?.name) { | ||||
| 			return html`<div>Please <a target="_top" href="/login">login</a> to Tilde Friends, first.</div>`; | ||||
| 	updated() { | ||||
| 		this.update_map(); | ||||
| 	} | ||||
| 		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` | ||||
| 				<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> | ||||
| 					<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.id}</span> | ||||
| 					<input type="button" value="📁" @click=${this.upload}></input> | ||||
| 				</div> | ||||
| 			`; | ||||
| 		} | ||||
|  | ||||
| 		return html` | ||||
| 		} else { | ||||
| 			header = html` | ||||
| 				<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> | ||||
| 						<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.id}</span> | ||||
| 						<input type="button" value="📁" @click=${this.upload}></input> | ||||
| @@ -555,5 +611,48 @@ class GgAppElement extends LitElement { | ||||
| 				</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); | ||||
		Reference in New Issue
	
	Block a user