Proof of concept of building emojis.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4464 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2023-09-17 13:22:29 +00:00
parent e921b4a86a
commit 3c4959433a
3 changed files with 124 additions and 15 deletions

View File

@ -8,6 +8,7 @@ tfrpc.register(async function createIdentity() {
return ssb.createIdentity(); return ssb.createIdentity();
}); });
tfrpc.register(async function appendMessage(id, message) { tfrpc.register(async function appendMessage(id, message) {
print('APPEND', JSON.stringify(message));
return ssb.appendMessageWithIdentity(id, message); return ssb.appendMessageWithIdentity(id, message);
}); });
tfrpc.register(function url() { tfrpc.register(function url() {

View File

@ -9,6 +9,6 @@
<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="width: 100%; height: 100%"></gg-app> <gg-app style="width: 100%; height: 100%" id="ggapp"></gg-app>
</body> </body>
</html> </html>

View File

@ -14,6 +14,12 @@ const k_color_pavement = [32, 32, 32, 255];
const k_color_grass = [0, 255, 0, 255]; const k_color_grass = [0, 255, 0, 255];
const k_color_default = [128, 128, 128, 255]; const k_color_default = [128, 128, 128, 255];
const k_store = {
'🦞': 15,
'🛶': 10,
'🏠': 10,
};
class GgAppElement extends LitElement { class GgAppElement extends LitElement {
static get properties() { static get properties() {
return { return {
@ -22,11 +28,12 @@ class GgAppElement extends LitElement {
activities: {type: Array}, activities: {type: Array},
activity: {type: Object}, activity: {type: Object},
world: {type: Object}, world: {type: Object},
id: {type: String}, whoami: {type: String},
status: {type: Object}, status: {type: Object},
tab: {type: String}, tab: {type: String},
url: {type: String}, url: {type: String},
currency: {type: Number}, currency: {type: Number},
to_build: {type: String},
}; };
} }
@ -35,6 +42,7 @@ class GgAppElement extends LitElement {
this.activities = []; this.activities = [];
this.activity = {}; this.activity = {};
this.loaded_activities = []; this.loaded_activities = [];
this.placed_emojis = [];
this.strava = {}; this.strava = {};
this.min_lat = Number.MAX_VALUE; this.min_lat = Number.MAX_VALUE;
this.min_lon = Number.MAX_VALUE; this.min_lon = Number.MAX_VALUE;
@ -63,7 +71,7 @@ class GgAppElement extends LitElement {
console.log('update_activities failed', e); console.log('update_activities failed', e);
} }
await this.acquire_ssb_identity(); await this.acquire_ssb_identity();
if (this.id && this.activities?.length) { if (this.whoami && this.activities?.length) {
await this.sync_activities(); await this.sync_activities();
} }
await this.get_activities_from_ssb(); await this.get_activities_from_ssb();
@ -118,8 +126,30 @@ class GgAppElement extends LitElement {
this.status = {text: 'calculating balance'}; this.status = {text: 'calculating balance'};
rows = await tfrpc.rpc.query(` rows = await tfrpc.rpc.query(`
SELECT count(*) AS currency FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'gg-activity' SELECT count(*) AS currency FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'gg-activity'
`, [this.id]); `, [this.whoami]);
this.currency = rows[0].currency; let currency = rows[0].currency;
rows = await tfrpc.rpc.query(`
SELECT SUM(json_extract(content, '$.cost')) AS cost FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'gg-place'
`, [this.whoami]);
let spent = rows[0].cost;
this.currency = currency - spent;
this.status = {text: 'getting placed emojis'};
rows = await tfrpc.rpc.query(`
SELECT messages.content
FROM messages_fts('"gg-place"')
JOIN messages ON messages.rowid = messages_fts.rowid
WHERE json_extract(messages.content, '$.type') = 'gg-place'
ORDER BY messages.timestamp
`);
for (let row of rows) {
console.log(row.content);
let content = JSON.parse(row.content);
this.placed_emojis.push({
position: content.position,
emoji: content.emoji,
});
}
console.log(this.placed_emojis);
this.status = undefined; this.status = undefined;
this.update_map(); this.update_map();
} }
@ -137,7 +167,7 @@ class GgAppElement extends LitElement {
SELECT from_strava.value FROM json_each(?) AS from_strava SELECT from_strava.value FROM json_each(?) AS from_strava
LEFT OUTER JOIN my_activities ON from_strava.value = my_activities.url LEFT OUTER JOIN my_activities ON from_strava.value = my_activities.url
WHERE my_activities.url IS NULL WHERE my_activities.url IS NULL
`, [this.id, JSON.stringify(ids)]); `, [this.whoami, JSON.stringify(ids)]);
console.log('missing = ', missing); console.log('missing = ', missing);
for (let [index, row] of missing.entries()) { for (let [index, row] of missing.entries()) {
this.status = {text: 'syncing from strava', value: index, max: missing.length}; this.status = {text: 'syncing from strava', value: index, max: missing.length};
@ -163,7 +193,7 @@ class GgAppElement extends LitElement {
} }
], ],
}; };
await tfrpc.rpc.appendMessage(this.id, message); await tfrpc.rpc.appendMessage(this.whoami, message);
} }
this.status = undefined; this.status = undefined;
} }
@ -182,16 +212,16 @@ class GgAppElement extends LitElement {
ORDER BY timestamp DESC limit 1 ORDER BY timestamp DESC limit 1
`, [JSON.stringify(ids)])).map(row => row.author) : []; `, [JSON.stringify(ids)])).map(row => row.author) : [];
if (!players.length) { if (!players.length) {
this.id = await tfrpc.rpc.createIdentity(); this.whoami = await tfrpc.rpc.createIdentity();
if (this.id) { if (this.whoami) {
await tfrpc.rpc.appendMessage(this.id, { await tfrpc.rpc.appendMessage(this.whoami, {
type: 'gg-player', type: 'gg-player',
active: true, active: true,
}); });
} }
} else { } else {
players.sort(); players.sort();
this.id = players[0]; this.whoami = players[0];
} }
} }
@ -294,6 +324,57 @@ class GgAppElement extends LitElement {
.openOn(this.leaflet); .openOn(this.leaflet);
} }
async build() {
if (this.popup) {
this.popup.remove();
}
if (!this.marker) {
return;
}
let latlng = this.marker.getLatLng();
let cost = k_store[this.to_build];
if (cost > this.currency) {
alert('Insufficient funds.');
return;
}
let message = {
type: 'gg-place',
position: {lat: latlng.lat, lng: latlng.lng},
emoji: this.to_build,
cost: cost,
};
let id = await tfrpc.rpc.appendMessage(this.whoami, message);
this.marker.remove();
this.placed_emojis.push({
position: {lat: latlng.lat, lng: latlng.lng},
emoji: this.to_build,
});
this.currency -= cost;
return this.update_map();
}
on_marker_click(event) {
this.popup = L.popup()
.setLatLng(event.latlng)
.setContent(`
${this.to_build} (-${k_store[this.to_build]}) <input type="button" value="Build" onclick="document.getElementById('ggapp').build()"></input>
`)
.openOn(this.leaflet);
}
on_mouse_down(event) {
if (this.marker) {
this.marker.remove();
this.marker = undefined;
}
if (this.to_build) {
this.marker = L.marker(event.latlng, {icon: L.divIcon({className: 'build-icon'}), draggable: true}).addTo(this.leaflet);
this.marker.on({click: this.on_marker_click.bind(this)});
}
}
async update_map() { async update_map() {
let map = this.shadowRoot.getElementById('map'); let map = this.shadowRoot.getElementById('map');
if (!map || !this.loaded_activities.length) { if (!map || !this.loaded_activities.length) {
@ -304,6 +385,7 @@ class GgAppElement extends LitElement {
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({contextmenu: this.on_click.bind(this)}); this.leaflet.on({contextmenu: this.on_click.bind(this)});
this.leaflet.on({click: this.on_mouse_down.bind(this)});
} }
let self = this; let self = this;
let grid_layer = L.GridLayer.extend({ let grid_layer = L.GridLayer.extend({
@ -336,6 +418,16 @@ class GgAppElement extends LitElement {
} }
} }
} }
for (let placed of self.placed_emojis) {
let position = self.leaflet.options.crs.latLngToPoint(placed.position, coords.z);
let tile_x = Math.floor(position.x / size.x);
let tile_y = Math.floor(position.y / size.y);
position.x = position.x - tile_x * size.x;
position.y = position.y - tile_y * size.y;
if (tile_x == coords.x && tile_y == coords.y) {
context.fillText(placed.emoji, position.x, position.y + 10);
}
}
return tile; return tile;
} }
}); });
@ -539,8 +631,8 @@ class GgAppElement extends LitElement {
} }
], ],
}; };
console.log('id =', this.id, 'message = ', message); console.log('id =', this.whoami, 'message = ', message);
let id = await tfrpc.rpc.appendMessage(this.id, message); let id = await tfrpc.rpc.appendMessage(this.whoami, message);
console.log('appended message', id); console.log('appended message', id);
alert('Activity uploaded.'); alert('Activity uploaded.');
await this.get_activities_from_ssb(); await this.get_activities_from_ssb();
@ -579,10 +671,20 @@ class GgAppElement extends LitElement {
`; `;
} }
render_store_item(item) {
let [emoji, cost] = item;
return html`
<div>
<input type="button" value="${emoji}" @click=${() => this.to_build = emoji}></input> ${cost} ${emoji == this.to_build ? '<-- Will be built next' : undefined}
</div>
`;
}
render_store() { render_store() {
return html` return html`
<h2>Store</h2> <h2>Store</h2>
<div><b>Your balance:</b> ${this.currency}</div> <div><b>Your balance:</b> ${this.currency}</div>
${Object.entries(k_store).map(this.render_store_item.bind(this))}
`; `;
} }
@ -595,7 +697,7 @@ class GgAppElement extends LitElement {
header = html` header = html`
<div style="flex: 1 0; 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.whoami}</span>
<input type="button" value="📁" @click=${this.upload}></input> <input type="button" value="📁" @click=${this.upload}></input>
</div> </div>
`; `;
@ -604,7 +706,7 @@ class GgAppElement extends LitElement {
<div> <div>
<div style="flex: 1 0; 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.whoami}</span>
<input type="button" value="📁" @click=${this.upload}></input> <input type="button" value="📁" @click=${this.upload}></input>
</div> </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> <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>
@ -646,6 +748,12 @@ class GgAppElement extends LitElement {
} }
return html` return html`
<style>
.build-icon::before {
content: '📍';
border: 2px solid red;
}
</style>
<link rel="stylesheet" href="leaflet.css"/> <link rel="stylesheet" href="leaflet.css"/>
<div style="width: 100%; height: 100%; display: flex; flex-direction: column"> <div style="width: 100%; height: 100%; display: flex; flex-direction: column">
${header} ${header}