Skip to content

Commit

Permalink
Merge pull request #28 from jsdelivr/map-theme
Browse files Browse the repository at this point in the history
feat: persist dark theme in localstorage
  • Loading branch information
MartinKolarik authored Sep 24, 2024
2 parents 37e908b + 64f00cc commit c808737
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 39 deletions.
10 changes: 5 additions & 5 deletions assets/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@
--bluegray-800: #37404c;
--bluegray-900: #282e38;

--dark-50: #f8fafc; /**/
--dark-100: #f1f5f9; /**/
--dark-200: #e2e8f0; /**/
--dark-300: #cbd5e1; /**/
--dark-50: #f8fafc;
--dark-100: #f1f5f9;
--dark-200: #e2e8f0;
--dark-300: #cbd5e1;
--dark-400: #4f6180;
--dark-500: #33415a;
--dark-600: #2d3c59;
--dark-700: #1a2842;
--dark-800: #17233a;
--dark-900: #162034;
--dark-950: #131728; /**/
--dark-950: #131728;

--sidebar-border: #4f6180;
--table-border: #2d3c59;
Expand Down
21 changes: 14 additions & 7 deletions pages/probes/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@
const emit = defineEmits([ 'save', 'hide', 'delete' ]);
const auth = useAuth();
const user = auth.getUser as User;
// ROOT
const probeDetailsDialog = ref(true);
Expand All @@ -239,18 +242,22 @@
// GOOGLE MAP
watch(() => props.gmapsLoaded, () => {
initGoogleMap(probe.value);
let removeWatcher: (() => void) | undefined;
watch(() => props.gmapsLoaded, async () => {
removeWatcher = await initGoogleMap(probe.value);
});
onMounted(() => {
props.gmapsLoaded && initGoogleMap(probe.value);
onMounted(async () => {
if (props.gmapsLoaded) {
removeWatcher = await initGoogleMap(probe.value);
}
});
// TAGS
onUnmounted(() => {
removeWatcher && removeWatcher();
});
const auth = useAuth();
const user = auth.getUser as User;
// TAGS
const isEditingTags = ref<boolean>(false);
const tagStrings = computed(() => probe.value.tags.map(({ prefix, value }) => `u-${prefix}-${value}`));
Expand Down
2 changes: 1 addition & 1 deletion plugins/02.refreshToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useAuth } from '~/store/auth';
export default defineNuxtPlugin(async () => {
setInterval(async () => {
const auth = useAuth();
const expiresAt = auth.getExpiresAt;
const expiresAt = auth.expiresAt;

if (expiresAt && expiresAt - new Date().getTime() < 3 * 60 * 1000) {
auth.refresh();
Expand Down
33 changes: 24 additions & 9 deletions plugins/04.theme.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
import { useAppearance } from '~/store/appearance';
import { useAuth } from '~/store/auth';

export default defineNuxtPlugin(() => {
const auth = useAuth();
const appearance = useAppearance();
const userTheme = computed(() => auth.user.appearance);

const userAppearance = computed(() => auth.user.appearance);
const updateTheme = () => {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
const prevUserTheme = localStorage.getItem('theme:prevUserTheme') as 'dark' | 'light' | null;

const updateAppearance = () => {
const systemAppearance = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
const appearance = userAppearance.value ?? systemAppearance;
document.documentElement.className = appearance;
};
let theme: 'light' | 'dark' = systemTheme;

if (auth.isLoggedIn && userTheme.value) {
theme = userTheme.value;
} else if (!auth.isLoggedIn && prevUserTheme) {
theme = prevUserTheme;
}

updateAppearance();
document.documentElement.className = theme;
appearance.setTheme(theme);

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateAppearance);
if (auth.isLoggedIn && userTheme.value) {
localStorage.setItem('theme:prevUserTheme', userTheme.value);
} else if (auth.isLoggedIn && userTheme.value === null) {
localStorage.removeItem('theme:prevUserTheme');
}
};

watch(userAppearance, updateAppearance);
updateTheme();
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme);
watch(userTheme, updateTheme);
});
16 changes: 16 additions & 0 deletions store/appearance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineStore } from 'pinia';

interface AppearanceState {
theme: 'light' | 'dark',
}

export const useAppearance = defineStore('appearance', {
state: (): AppearanceState => ({
theme: 'light',
}),
actions: {
setTheme (theme: AppearanceState['theme']) {
this.theme = theme;
},
},
});
2 changes: 0 additions & 2 deletions store/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ export const useAuth = defineStore('auth', {
}),

getters: {
getExpiresAt: state => state.expiresAt,
getUser: state => state.user,
getGithubUsername: state => state.user.github_username,
},

actions: {
Expand Down
41 changes: 41 additions & 0 deletions utils/dark-map-styles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[
{
"featureType": "water",
"elementType": "geometry",
"stylers": [
{
"color": "#131728"
},
{
"lightness": 0
}
]
},
{
"featureType": "landscape",
"elementType": "geometry",
"stylers": [
{
"color": "#2d3c59"
},
{
"lightness": 0
}
]
},
{
"featureType": "administrative",
"elementType": "geometry.stroke",
"stylers": [
{
"color": "#17233a"
},
{
"lightness": 0
},
{
"weight": 1.2
}
]
}
]
65 changes: 50 additions & 15 deletions utils/init-google-map.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import darkMapStyles from './dark-map-styles.json';
import mapStyles from './map-styles.json';
import { useAppearance } from '~/store/appearance.js';

const INITIAL_MAP_STYLES = mapStyles;
const MAP_MIN_ZOOM = 1;
const MAP_MAX_ZOOM = 22;
const MAP_ZOOM_REG = 3.74;
const DEFAULT_MARKER_COLOR = '#17d4a7';

const INITIAL_MAP_STYLES = mapStyles;
const MODERATE_MAP_STYLES = [
...INITIAL_MAP_STYLES,
{
elementType: 'labels.text.stroke',
stylers: [{ visibility: 'on' }],
Expand All @@ -20,12 +22,34 @@ const MODERATE_MAP_STYLES = [
},
];
const DETAILED_MAP_STYLES = [
...MODERATE_MAP_STYLES,
{
featureType: 'road',
stylers: [{ visibility: 'on' }],
},
];
const INITIAL_MAP_STYLES_DARK = darkMapStyles;
const MODERATE_MAP_STYLES_DARK = [{
elementType: 'labels.text.stroke',
stylers: [
{ visibility: 'on' },
{ color: '#131728' },
],
}];

const stylesByTheme = {
light: {
background: '#ffffff',
initial: INITIAL_MAP_STYLES,
moderate: [ ...INITIAL_MAP_STYLES, ...MODERATE_MAP_STYLES ],
detailed: [ ...INITIAL_MAP_STYLES, ...MODERATE_MAP_STYLES, ...DETAILED_MAP_STYLES ],
},
dark: {
background: '#131728',
initial: [ ...INITIAL_MAP_STYLES, ...INITIAL_MAP_STYLES_DARK ],
moderate: [ ...INITIAL_MAP_STYLES, ...MODERATE_MAP_STYLES, ...INITIAL_MAP_STYLES_DARK, ...MODERATE_MAP_STYLES_DARK ],
detailed: [ ...INITIAL_MAP_STYLES, ...MODERATE_MAP_STYLES, ...DETAILED_MAP_STYLES, ...INITIAL_MAP_STYLES_DARK, ...MODERATE_MAP_STYLES_DARK ],
},
};

let map: google.maps.Map, marker: google.maps.Marker, infoWindow: google.maps.InfoWindow;

Expand All @@ -41,9 +65,12 @@ export const initGoogleMap = async (probe: Probe) => {
return;
}

const appearance = useAppearance();
const style = stylesByTheme[appearance.theme];

map = new Map(element, {
backgroundColor: '#fafafa',
styles: INITIAL_MAP_STYLES,
backgroundColor: style.background,
styles: style.initial,
zoom: MAP_ZOOM_REG,
center: { lat: probe.latitude, lng: probe.longitude },
mapTypeId: 'roadmap',
Expand All @@ -60,18 +87,26 @@ export const initGoogleMap = async (probe: Probe) => {

map.addListener('zoom_changed', () => {
infoWindow && infoWindow.close();
updateStyles(map, appearance.theme);
});

const currZoom = map.getZoom();
const removeWatcher = appearance.$subscribe(() => updateStyles(map, appearance.theme));

// handle map detalization on zoom
if (currZoom && currZoom >= 14) {
map.setOptions({ styles: DETAILED_MAP_STYLES });
} else if (currZoom && currZoom >= 5) {
map.setOptions({ styles: MODERATE_MAP_STYLES });
} else {
map.setOptions({ styles: INITIAL_MAP_STYLES });
}
});
return removeWatcher;
};

const updateStyles = (map: google.maps.Map, theme: 'light' | 'dark') => {
const style = stylesByTheme[theme];
const currZoom = map.getZoom();

// handle map detalization on zoom
if (currZoom && currZoom >= 14) {
map.setOptions({ styles: style.detailed });
} else if (currZoom && currZoom >= 5) {
map.setOptions({ styles: style.moderate });
} else {
map.setOptions({ styles: style.initial });
}
};

function createMapMarker (probe: Probe) {
Expand Down

0 comments on commit c808737

Please sign in to comment.