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}; | 			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,10 +352,18 @@ 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); | ||||||
| 		} | 		} | ||||||
| 		this.leaflet.fitBounds([ | 		if (this.focus) { | ||||||
| 			[this.min_lat, this.min_lon], | 			this.leaflet.fitBounds([ | ||||||
| 			[this.max_lat, this.max_lon], | 				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) { | 	activity_to_color(activity) { | ||||||
| @@ -529,29 +556,101 @@ 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>`; | 	} | ||||||
|  |  | ||||||
|  | 	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; | ||||||
| 		} | 		} | ||||||
| 		if (!this.strava?.access_token) { | 	} | ||||||
|  |  | ||||||
|  | 	render_news() { | ||||||
|  | 		return html` | ||||||
|  | 			<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}`; | 			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}`; | ||||||
| 			return html` | 			header = html` | ||||||
| 				<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%"> | ||||||
| 					<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` | ||||||
|  | 				<div> | ||||||
|  | 					<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> | ||||||
|  | 					</div> | ||||||
|  | 					<h3 ?hidden=${!this.status?.text}>${this.status?.text} <progress ?hidden=${!this.status?.max} value=${this.status?.value} max=${this.status?.max}>${this.status?.value}</progress></h3> | ||||||
|  | 				</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` | 		return html` | ||||||
| 			<div> | 			<link rel="stylesheet" href="leaflet.css"/> | ||||||
| 				<div style="display: flex; flex-direction: row; align-items: center; gap: 1em; width: 100%"> | 			<div style="width: 100%; height: 100%; display: flex; flex-direction: column"> | ||||||
| 					<h1>Welcome, ${this.user.credentials.session.name}</h1> | 				${header} | ||||||
| 					<span style="font-size: xx-small; flex: 1 1; word-break: break-all">${this.id}</span> | 				<div style="flex: 1 0; overflow: scroll">${content}</div> | ||||||
| 					<input type="button" value="📁" @click=${this.upload}></input> | 				${navigation} | ||||||
| 				</div> |  | ||||||
| 				<h3 ?hidden=${!this.status?.text}>${this.status?.text} <progress ?hidden=${!this.status?.max} value=${this.status?.value} max=${this.status?.max}>${this.status?.value}</progress></h3> |  | ||||||
| 			</div> | 			</div> | ||||||
| 		`; | 		`; | ||||||
| 	} | 	} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user