Aleksmorshen commited on
Commit
60ab832
·
verified ·
1 Parent(s): c8b08cf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -93
app.py CHANGED
@@ -32,8 +32,8 @@ def index():
32
  <title>TON AR Hotspots</title>
33
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
34
  <script src="https://unpkg.com/@tonconnect/ui@latest/dist/tonconnect-ui.min.js"></script>
35
- <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha256-p4NxAo9TkjVj/lQsiG9MixcUCddNmW+wFRfSDKcRFuQ=" crossorigin=""/>
36
- <script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjGwcl9PfVyaNnITeztADbcHiK/fxh8=" crossorigin=""></script>
37
  <style>
38
  body, html {
39
  margin: 0;
@@ -111,64 +111,67 @@ def index():
111
  z-index: 10;
112
  text-align: center;
113
  }
 
 
 
 
 
 
114
  .hotspot.hidden {
115
  opacity: 0;
116
  pointer-events: none;
117
  }
118
- #mini-map-container {
119
  position: fixed;
120
  bottom: 10px;
121
  left: 10px;
122
  width: 200px;
123
  height: 200px;
124
- background-color: #333;
125
- border: 2px solid #555;
126
  border-radius: 10px;
127
  overflow: hidden;
128
- z-index: 20;
129
- box-shadow: 0 0 15px rgba(0,0,0,0.7);
130
  transition: all 0.3s ease;
 
131
  display: flex;
132
  flex-direction: column;
133
  }
134
- #mini-map-container.expanded {
135
  width: 100%;
136
  height: 100%;
137
  top: 0;
138
  left: 0;
139
  border-radius: 0;
 
 
140
  }
141
  #map {
142
  flex-grow: 1;
143
  width: 100%;
 
 
 
 
 
144
  height: 100%;
 
145
  }
146
  #toggle-map-button {
147
- position: absolute;
148
- top: 5px;
149
- right: 5px;
150
- z-index: 21;
151
- background-color: rgba(0,0,0,0.6);
152
  color: white;
153
  border: none;
154
- border-radius: 50%;
155
- width: 30px;
156
- height: 30px;
157
- font-size: 18px;
158
  cursor: pointer;
159
- display: flex;
160
- align-items: center;
161
- justify-content: center;
162
  }
163
- #mini-map-container.expanded #toggle-map-button {
 
164
  top: 10px;
165
  right: 10px;
166
- }
167
- .leaflet-control-container, .leaflet-pane {
168
- z-index: auto !important;
169
- }
170
- .leaflet-control-attribution.leaflet-control a {
171
- color: #fff;
172
  }
173
  </style>
174
  </head>
@@ -186,9 +189,9 @@ def index():
186
  <video id="camera-view" playsinline autoplay muted></video>
187
  <div id="ar-container"></div>
188
 
189
- <div id="mini-map-container">
190
- <button id="toggle-map-button">⤢</button>
191
  <div id="map"></div>
 
192
  </div>
193
 
194
  <script>
@@ -201,12 +204,14 @@ def index():
201
  hotspots: [],
202
  currentUserPosition: null,
203
  deviceOrientation: { alpha: 0, beta: 0, gamma: 0 },
204
- cameraFov: 60
 
 
 
 
205
  };
206
 
207
- let map;
208
- let userMarker;
209
- let hotspotMarkers = [];
210
 
211
  async function fetchBalance(address) {
212
  try {
@@ -224,7 +229,7 @@ def index():
224
  await setupCamera();
225
  setupGPS();
226
  setupOrientationListener();
227
- setupMap();
228
  await loadHotspots();
229
  setupAddHotspotListener();
230
  requestAnimationFrame(update);
@@ -267,13 +272,11 @@ def index():
267
  lat: position.coords.latitude,
268
  lon: position.coords.longitude
269
  };
270
- if (map && userMarker) {
271
- const latLng = [position.coords.latitude, position.coords.longitude];
272
- userMarker.setLatLng(latLng);
273
- if (map.getZoom() < 15) { // Only set view if zoom is low, to avoid constant recentering
274
- map.setView(latLng, 15);
275
- } else {
276
- map.panTo(latLng);
277
  }
278
  }
279
  },
@@ -301,26 +304,25 @@ def index():
301
  }
302
  }
303
 
304
- function setupMap() {
305
- map = L.map('map', {zoomControl: false}).setView([0, 0], 2);
306
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
307
- attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
308
- }).addTo(map);
309
-
310
- userMarker = L.circleMarker([0, 0], {
311
- radius: 8,
312
- color: '#007aff',
313
- fillColor: '#007aff',
314
- fillOpacity: 0.8
315
- }).addTo(map);
316
-
317
- document.getElementById('toggle-map-button').addEventListener('click', () => {
318
- const container = document.getElementById('mini-map-container');
319
- const button = document.getElementById('toggle-map-button');
320
- container.classList.toggle('expanded');
321
- button.innerText = container.classList.contains('expanded') ? '⤡' : '⤢';
322
- setTimeout(() => { map.invalidateSize(); }, 300);
323
- });
324
  }
325
 
326
  async function loadHotspots() {
@@ -329,6 +331,7 @@ def index():
329
  const data = await response.json();
330
  state.hotspots = data;
331
  renderHotspots();
 
332
  } catch (error) {
333
  console.error('Ошибка загрузки хотспотов:', error);
334
  }
@@ -337,36 +340,26 @@ def index():
337
  function renderHotspots() {
338
  const container = document.getElementById('ar-container');
339
  container.innerHTML = '';
340
-
341
- hotspotMarkers.forEach(marker => marker.remove());
342
- hotspotMarkers = [];
343
-
344
  state.hotspots.forEach((hotspot, index) => {
345
  const el = document.createElement('div');
346
  el.className = 'hotspot';
347
  el.id = `hotspot-${index}`;
348
-
349
- const textNode = document.createElement('span');
350
- textNode.innerText = hotspot.text;
351
- el.appendChild(textNode);
352
-
353
- if (hotspot.user_address) {
354
- const creatorSpan = document.createElement('span');
355
- creatorSpan.style.fontSize = '0.7em';
356
- creatorSpan.style.display = 'block';
357
- creatorSpan.style.marginTop = '5px';
358
- creatorSpan.innerText = `by ${hotspot.user_address.slice(0, 6)}...${hotspot.user_address.slice(-4)}`;
359
- el.appendChild(creatorSpan);
360
- }
361
  container.appendChild(el);
 
 
362
 
363
- const marker = L.marker([hotspot.lat, hotspot.lon]).addTo(map);
364
- let popupContent = `<b>${hotspot.text}</b>`;
365
- if (hotspot.user_address) {
366
- popupContent += `<br>Добавил: ${hotspot.user_address.slice(0, 6)}...${hotspot.user_address.slice(-4)}`;
367
- }
368
- marker.bindPopup(popupContent);
369
- hotspotMarkers.push(marker);
 
 
 
 
370
  });
371
  }
372
 
@@ -378,7 +371,7 @@ def index():
378
  return;
379
  }
380
  if (!tonConnectUI.account || !tonConnectUI.account.address) {
381
- alert('Пожалуйста, подключите кошелек, чтобы добавить хотспот.');
382
  return;
383
  }
384
 
@@ -388,7 +381,7 @@ def index():
388
  text: text,
389
  lat: state.currentUserPosition.lat,
390
  lon: state.currentUserPosition.lon,
391
- user_address: tonConnectUI.account.address
392
  };
393
 
394
  try {
@@ -398,8 +391,10 @@ def index():
398
  body: JSON.stringify(newHotspot)
399
  });
400
  if(response.ok) {
401
- state.hotspots.push(newHotspot);
 
402
  renderHotspots();
 
403
  } else {
404
  alert('Не удалось сохранить хотспот.');
405
  }
@@ -453,7 +448,7 @@ def index():
453
 
454
  const distance = haversineDistance(state.currentUserPosition, hotspot);
455
 
456
- if (distance > 10) { // Hotspot visible only within 10 meters
457
  el.classList.add('hidden');
458
  return;
459
  }
@@ -472,7 +467,7 @@ def index():
472
  const x = screenWidth / 2 + (angleDiff / (state.cameraFov / 2)) * (screenWidth / 2);
473
  const y = screenHeight / 2;
474
 
475
- const scale = Math.max(0.5, 1 - distance / 10); // Scales from 1.0 at 0m to 0.5 at 10m
476
 
477
  el.style.left = `${x}px`;
478
  el.style.top = `${y}px`;
@@ -519,17 +514,17 @@ def handle_hotspots():
519
  text = data.get('text')
520
  lat = data.get('lat')
521
  lon = data.get('lon')
522
- user_address = data.get('user_address')
523
 
524
- if not all([text, lat, lon, user_address]):
525
- return jsonify({"error": "Missing data: text, lat, lon, or user_address"}), 400
526
 
527
  try:
528
  new_hotspot = {
529
  "text": str(text),
530
  "lat": float(lat),
531
  "lon": float(lon),
532
- "user_address": str(user_address)
533
  }
534
  save_hotspot(new_hotspot)
535
  return jsonify({"success": True, "hotspot": new_hotspot}), 201
 
32
  <title>TON AR Hotspots</title>
33
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
34
  <script src="https://unpkg.com/@tonconnect/ui@latest/dist/tonconnect-ui.min.js"></script>
35
+ <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
36
+ <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
37
  <style>
38
  body, html {
39
  margin: 0;
 
111
  z-index: 10;
112
  text-align: center;
113
  }
114
+ .hotspot small {
115
+ font-size: 0.7em;
116
+ opacity: 0.8;
117
+ display: block;
118
+ margin-top: 5px;
119
+ }
120
  .hotspot.hidden {
121
  opacity: 0;
122
  pointer-events: none;
123
  }
124
+ #map-container {
125
  position: fixed;
126
  bottom: 10px;
127
  left: 10px;
128
  width: 200px;
129
  height: 200px;
130
+ background-color: rgba(0, 0, 0, 0.7);
 
131
  border-radius: 10px;
132
  overflow: hidden;
 
 
133
  transition: all 0.3s ease;
134
+ z-index: 50;
135
  display: flex;
136
  flex-direction: column;
137
  }
138
+ #map-container.fullscreen {
139
  width: 100%;
140
  height: 100%;
141
  top: 0;
142
  left: 0;
143
  border-radius: 0;
144
+ justify-content: center;
145
+ align-items: center;
146
  }
147
  #map {
148
  flex-grow: 1;
149
  width: 100%;
150
+ height: calc(100% - 35px);
151
+ border-radius: 10px;
152
+ overflow: hidden;
153
+ }
154
+ #map-container.fullscreen #map {
155
  height: 100%;
156
+ border-radius: 0;
157
  }
158
  #toggle-map-button {
159
+ background-color: rgba(0, 0, 0, 0.5);
 
 
 
 
160
  color: white;
161
  border: none;
162
+ padding: 5px 10px;
163
+ margin: 5px;
164
+ border-radius: 5px;
 
165
  cursor: pointer;
166
+ font-size: 12px;
167
+ align-self: flex-end;
 
168
  }
169
+ #map-container.fullscreen #toggle-map-button {
170
+ position: absolute;
171
  top: 10px;
172
  right: 10px;
173
+ font-size: 16px;
174
+ margin: 0;
 
 
 
 
175
  }
176
  </style>
177
  </head>
 
189
  <video id="camera-view" playsinline autoplay muted></video>
190
  <div id="ar-container"></div>
191
 
192
+ <div id="map-container">
 
193
  <div id="map"></div>
194
+ <button id="toggle-map-button">Minimap</button>
195
  </div>
196
 
197
  <script>
 
204
  hotspots: [],
205
  currentUserPosition: null,
206
  deviceOrientation: { alpha: 0, beta: 0, gamma: 0 },
207
+ cameraFov: 60,
208
+ map: null,
209
+ userMarker: null,
210
+ hotspotMarkers: [],
211
+ initialMapSet: false
212
  };
213
 
214
+ const MAX_VISIBLE_DISTANCE = 10;
 
 
215
 
216
  async function fetchBalance(address) {
217
  try {
 
229
  await setupCamera();
230
  setupGPS();
231
  setupOrientationListener();
232
+ initMap();
233
  await loadHotspots();
234
  setupAddHotspotListener();
235
  requestAnimationFrame(update);
 
272
  lat: position.coords.latitude,
273
  lon: position.coords.longitude
274
  };
275
+ if (state.userMarker) {
276
+ state.userMarker.setLatLng([state.currentUserPosition.lat, state.currentUserPosition.lon]);
277
+ if (!state.initialMapSet) {
278
+ state.map.setView([state.currentUserPosition.lat, state.currentUserPosition.lon], 15);
279
+ state.initialMapSet = true;
 
 
280
  }
281
  }
282
  },
 
304
  }
305
  }
306
 
307
+ function initMap() {
308
+ state.map = L.map('map').setView([0, 0], 2);
309
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
310
+ attribution: '© OpenStreetMap contributors'
311
+ }).addTo(state.map);
312
+ state.userMarker = L.marker([0, 0]).addTo(state.map).bindPopup('Ваше местоположение').openPopup();
313
+
314
+ document.getElementById('toggle-map-button').addEventListener('click', toggleMap);
315
+ }
316
+
317
+ function toggleMap() {
318
+ const mapContainer = document.getElementById('map-container');
319
+ mapContainer.classList.toggle('fullscreen');
320
+ setTimeout(() => {
321
+ state.map.invalidateSize();
322
+ if (state.currentUserPosition) {
323
+ state.map.setView([state.currentUserPosition.lat, state.currentUserPosition.lon], state.map.getZoom());
324
+ }
325
+ }, 300);
 
326
  }
327
 
328
  async function loadHotspots() {
 
331
  const data = await response.json();
332
  state.hotspots = data;
333
  renderHotspots();
334
+ renderHotspotsOnMap();
335
  } catch (error) {
336
  console.error('Ошибка загрузки хотспотов:', error);
337
  }
 
340
  function renderHotspots() {
341
  const container = document.getElementById('ar-container');
342
  container.innerHTML = '';
 
 
 
 
343
  state.hotspots.forEach((hotspot, index) => {
344
  const el = document.createElement('div');
345
  el.className = 'hotspot';
346
  el.id = `hotspot-${index}`;
347
+ el.innerHTML = `${hotspot.text}<br><small>by ${hotspot.creator_address ? hotspot.creator_address.slice(0, 6) + '...' + hotspot.creator_address.slice(-4) : 'Unknown'}</small>`;
 
 
 
 
 
 
 
 
 
 
 
 
348
  container.appendChild(el);
349
+ });
350
+ }
351
 
352
+ function renderHotspotsOnMap() {
353
+ if (!state.map) return;
354
+
355
+ state.hotspotMarkers.forEach(marker => state.map.removeLayer(marker));
356
+ state.hotspotMarkers = [];
357
+
358
+ state.hotspots.forEach(hotspot => {
359
+ const marker = L.marker([hotspot.lat, hotspot.lon])
360
+ .addTo(state.map)
361
+ .bindPopup(`${hotspot.text}<br>by ${hotspot.creator_address ? hotspot.creator_address.slice(0, 6) + '...' + hotspot.creator_address.slice(-4) : 'Unknown'}`);
362
+ state.hotspotMarkers.push(marker);
363
  });
364
  }
365
 
 
371
  return;
372
  }
373
  if (!tonConnectUI.account || !tonConnectUI.account.address) {
374
+ alert('Для добавления хотспота необходимо подключить кошелек.');
375
  return;
376
  }
377
 
 
381
  text: text,
382
  lat: state.currentUserPosition.lat,
383
  lon: state.currentUserPosition.lon,
384
+ creator_address: tonConnectUI.account.address
385
  };
386
 
387
  try {
 
391
  body: JSON.stringify(newHotspot)
392
  });
393
  if(response.ok) {
394
+ const savedHotspot = await response.json();
395
+ state.hotspots.push(savedHotspot.hotspot);
396
  renderHotspots();
397
+ renderHotspotsOnMap();
398
  } else {
399
  alert('Не удалось сохранить хотспот.');
400
  }
 
448
 
449
  const distance = haversineDistance(state.currentUserPosition, hotspot);
450
 
451
+ if (distance > MAX_VISIBLE_DISTANCE) {
452
  el.classList.add('hidden');
453
  return;
454
  }
 
467
  const x = screenWidth / 2 + (angleDiff / (state.cameraFov / 2)) * (screenWidth / 2);
468
  const y = screenHeight / 2;
469
 
470
+ const scale = Math.max(0.5, 1 - distance / MAX_VISIBLE_DISTANCE);
471
 
472
  el.style.left = `${x}px`;
473
  el.style.top = `${y}px`;
 
514
  text = data.get('text')
515
  lat = data.get('lat')
516
  lon = data.get('lon')
517
+ creator_address = data.get('creator_address')
518
 
519
+ if not all([text, lat, lon, creator_address]):
520
+ return jsonify({"error": "Missing data: text, lat, lon, or creator_address"}), 400
521
 
522
  try:
523
  new_hotspot = {
524
  "text": str(text),
525
  "lat": float(lat),
526
  "lon": float(lon),
527
+ "creator_address": str(creator_address)
528
  }
529
  save_hotspot(new_hotspot)
530
  return jsonify({"success": True, "hotspot": new_hotspot}), 201