WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 61239db

Browse files
committed
tloc
1 parent f642e15 commit 61239db

File tree

5 files changed

+253
-9
lines changed

5 files changed

+253
-9
lines changed

ui/package-lock.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"googleapis": "^105.0.0",
3434
"jquery": "^3.6.0",
3535
"jwt-decode": "^3.1.2",
36+
"leaflet": "^1.9.4",
3637
"lodash": "^4.17.21",
3738
"material-icons": "^1.11.2",
3839
"net": "^1.0.2",

ui/src/router/index.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import BWbrucosi from "../views/BruciWebViews/BWbrucosiView.vue";
4545

4646

4747
import SponsorsPage from "../views/BruciWebViews/SponsorsPageView.vue";
48+
import TlocrtView2 from "@/views/BruciWebViews/TlocrtView2.vue";
4849

4950
const routes = [
5051
{
@@ -224,17 +225,25 @@ const routes = [
224225
path: "/tlocrt",
225226
name: "Tlocrt",
226227
component: Tlocrt,
227-
meta: {
228-
visibilityCheck: "TLOCRT_VISIBILITY",
229-
},
228+
// meta: {
229+
// visibilityCheck: "TLOCRT_VISIBILITY",
230+
// },
231+
},
232+
{
233+
path: "/t2",
234+
name: "Tlocrt2",
235+
component: TlocrtView2,
236+
// meta: {
237+
// visibilityCheck: "TLOCRT_VISIBILITY",
238+
// },
230239
},
231240
{
232241
path: "/Satnica",
233242
name: "satnica",
234243
component: Satnica,
235-
meta: {
236-
visibilityCheck: "SATNICA_VISIBILITY",
237-
},
244+
// meta: {
245+
// visibilityCheck: "SATNICA_VISIBILITY",
246+
// },
238247
},
239248
{
240249
path: "/igrica",

ui/src/store/visibilityStore.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createStore } from "vuex";
22
import createPersistedState from "vuex-persistedstate";
3-
import axios from "axios";
3+
import { api } from "@/plugins/api";
44

55
export default createStore({
66
state: {
@@ -55,8 +55,8 @@ export default createStore({
5555
actions: {
5656
async fetchVisibilityData({ commit }) {
5757
try {
58-
const response = await axios.get(
59-
`${process.env.VUE_APP_BASE_URL}/visibility/`
58+
const response = await api.get(
59+
`/visibility/`
6060
);
6161
const visibilityResp = response.data.reduce((result, obj) => {
6262
result[obj.name] = obj.visible;
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
<template>
2+
<div class="bw-page-container">
3+
<!-- Map section -->
4+
<div class="map-wrapper">
5+
<div ref="mapEl" class="map-container" aria-label="Location map"></div>
6+
</div>
7+
8+
<!-- Your existing content -->
9+
<div class="tlocrt">
10+
<div class="image">
11+
<img class="tlocrt-desktop" src="../../assets/tlocrt/tlocrt-desktop.svg" alt="Brucifer Tlocrt" />
12+
<img class="tlocrt-tablet" src="../../assets/tlocrt/tlocrt-tablet.svg" alt="Brucifer Tlocrt" />
13+
<img class="tlocrt-mobile" src="../../assets/tlocrt/tlocrt-mobile.svg" alt="Brucifer Tlocrt" />
14+
<div class="tlocrt-grid">
15+
<img class="tlocrt-desktop" src="../../assets/tlocrt/menza-desktop.svg" alt="Brucifer Tlocrt" />
16+
<img class="tlocrt-desktop" src="../../assets/tlocrt/galerija-desktop.svg" alt="Brucifer Tlocrt" />
17+
<img class="tlocrt-desktop" src="../../assets/tlocrt/mm-desktop.svg" alt="Brucifer Tlocrt" />
18+
<img class="tlocrt-desktop" src="../../assets/tlocrt/teatar-desktop.svg" alt="Brucifer Tlocrt" />
19+
</div>
20+
<img class="tlocrt-tablet" src="../../assets/tlocrt/menza-desktop.svg" alt="Brucifer Tlocrt" />
21+
<img class="tlocrt-tablet" src="../../assets/tlocrt/galerija-desktop.svg" alt="Brucifer Tlocrt" />
22+
<img class="tlocrt-tablet" src="../../assets/tlocrt/mm-desktop.svg" alt="Brucifer Tlocrt" />
23+
<img class="tlocrt-tablet" src="../../assets/tlocrt/teatar-desktop.svg" alt="Brucifer Tlocrt" />
24+
25+
<img class="tlocrt-mobile" src="../../assets/tlocrt/menza-desktop.svg" alt="Brucifer Tlocrt" />
26+
<img class="tlocrt-mobile" src="../../assets/tlocrt/galerija-desktop.svg" alt="Brucifer Tlocrt" />
27+
<img class="tlocrt-mobile" src="../../assets/tlocrt/mm-desktop.svg" alt="Brucifer Tlocrt" />
28+
<img class="tlocrt-mobile" src="../../assets/tlocrt/teatar-desktop.svg" alt="Brucifer Tlocrt" />
29+
</div>
30+
</div>
31+
32+
<Footer />
33+
</div>
34+
</template>
35+
36+
<script>
37+
import Footer from '@/components/NavbarAndFooter/Footer.vue'
38+
import NavbarBweb from '@/components/NavbarAndFooter/NavbarBweb.vue'
39+
40+
// Leaflet imports
41+
import L from 'leaflet'
42+
import 'leaflet/dist/leaflet.css'
43+
44+
// Fix marker icons in bundlers (Vite/Webpack)
45+
// These imports ensure the default icon files are bundled correctly.
46+
import marker2x from 'leaflet/dist/images/marker-icon-2x.png'
47+
import marker1x from 'leaflet/dist/images/marker-icon.png'
48+
import markerShadow from 'leaflet/dist/images/marker-shadow.png'
49+
50+
// Set default icon globally
51+
const DefaultIcon = L.icon({
52+
iconRetinaUrl: marker2x,
53+
iconUrl: marker1x,
54+
shadowUrl: markerShadow,
55+
iconSize: [25, 41],
56+
iconAnchor: [12, 41],
57+
popupAnchor: [1, -34],
58+
tooltipAnchor: [16, -28],
59+
shadowSize: [41, 41]
60+
})
61+
L.Marker.prototype.options.icon = DefaultIcon
62+
63+
export default {
64+
name: 'Naslovnica',
65+
components: { Footer, NavbarBweb },
66+
props: { msg: String },
67+
data() {
68+
return {
69+
map: null
70+
}
71+
},
72+
// Leaflet setup stays the same … then inside mounted():
73+
mounted() {
74+
this.$nextTick(() => {
75+
// Initialize map
76+
this.map = L.map(this.$refs.mapEl, {
77+
scrollWheelZoom: false,
78+
zoomControl: true,
79+
zoomAnimation: false,
80+
markerZoomAnimation: false
81+
})
82+
83+
// Base layer
84+
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
85+
maxZoom: 19,
86+
attribution:
87+
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
88+
}).addTo(this.map)
89+
90+
// SC center and bounds
91+
const scCenter = L.latLng(45.8035519, 15.9672823)
92+
let scBounds = scCenter.toBounds(1000)
93+
this.map.fitBounds(scBounds, { padding: [30, 30] })
94+
95+
const clampedBounds = scBounds.pad(0.15)
96+
this.map.setMaxBounds(clampedBounds)
97+
this.map.options.maxBoundsViscosity = 1.0
98+
const minZoomForSC = this.map.getBoundsZoom(clampedBounds, true)
99+
this.map.setMinZoom(minZoomForSC)
100+
101+
// Overlay image
102+
const scOverlayBounds = L.latLngBounds(
103+
[45.8027, 15.9647],
104+
[45.8053, 15.9699]
105+
)
106+
107+
const tlocrtOverlay = L.imageOverlay(
108+
require('@/assets/tlocrt/tlocrt-desktop.svg'),
109+
scOverlayBounds,
110+
{ opacity: 1, interactive: false }
111+
)
112+
tlocrtOverlay.addTo(this.map)
113+
114+
// Optional guides
115+
L.rectangle(scBounds, { weight: 1, fillOpacity: 0.05 }).addTo(this.map)
116+
L.marker(scCenter).addTo(this.map).bindPopup('Studentski centar')
117+
118+
// ---- Geolocation (robust) ----
119+
let userMarker = null
120+
let retryMs = 1500 // start small
121+
const retryMax = 60000
122+
123+
const updateUserMarker = (latlng) => {
124+
if (!userMarker) {
125+
userMarker = L.marker(latlng, { icon: DefaultIcon, zIndexOffset: 1000 })
126+
.addTo(this.map)
127+
.bindPopup('Vaša lokacija')
128+
} else {
129+
userMarker.setLatLng(latlng)
130+
}
131+
this.map.panInsideBounds(clampedBounds, { animate: true })
132+
}
133+
134+
const startLocate = () => {
135+
this.map.locate({
136+
setView: false,
137+
watch: false, // one fix first
138+
enableHighAccuracy: true,
139+
timeout: 10000,
140+
maximumAge: 1000
141+
})
142+
}
143+
144+
const scheduleRetry = () => {
145+
const delay = Math.min(retryMs, retryMax)
146+
console.warn(`Retrying geolocation in ${delay}ms…`)
147+
setTimeout(startLocate, delay)
148+
retryMs = Math.min(retryMs * 2, retryMax)
149+
}
150+
151+
this.map.on('locationfound', (e) => {
152+
if (!clampedBounds.contains(e.latlng)) return
153+
updateUserMarker(e.latlng)
154+
155+
// After first fix, switch to continuous watch
156+
this.map.stopLocate()
157+
this.map.locate({
158+
setView: false,
159+
watch: true,
160+
enableHighAccuracy: true,
161+
timeout: 20000,
162+
maximumAge: 2000
163+
})
164+
165+
retryMs = 1500 // reset backoff after success
166+
})
167+
168+
this.map.on('locationerror', (err) => {
169+
const code = err.code
170+
const msg = (err && err.message) || ''
171+
console.warn('Geolocation failed:', code, msg)
172+
173+
// Show only once
174+
if (!this._geoToastShown) {
175+
this._geoToastShown = true
176+
alert(
177+
'Ne možemo trenutno odrediti lokaciju. Provjerite dozvole i pokušat ćemo ponovno.'
178+
)
179+
}
180+
181+
if (code === 1) {
182+
// Permission denied: stop retries, offer manual
183+
console.warn('Permission denied — stopping retries.')
184+
addManualPinHelper()
185+
return
186+
}
187+
188+
// Transient/timeout → retry
189+
scheduleRetry()
190+
})
191+
192+
// Manual fallback
193+
const addManualPinHelper = () => {
194+
if (this._manualPinMsg) return
195+
this._manualPinMsg = true
196+
const onClick = (e) => {
197+
if (!clampedBounds.contains(e.latlng)) return
198+
updateUserMarker(e.latlng)
199+
}
200+
this.map.on('click', onClick)
201+
console.info('Manual pin mode enabled: click the map to set your location.')
202+
}
203+
204+
// Kick off first locate
205+
startLocate()
206+
207+
// Final resize correction
208+
setTimeout(() => this.map && this.map.invalidateSize(), 200)
209+
})
210+
}
211+
,
212+
beforeUnmount() {
213+
if (this.map) {
214+
this.map.stopLocate()
215+
this.map.off()
216+
this.map.remove()
217+
this.map = null
218+
}
219+
}
220+
,
221+
}
222+
</script>

0 commit comments

Comments
 (0)