Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Bookmarking Sets of Layers #1185

Merged
merged 9 commits into from
May 8, 2024
11 changes: 10 additions & 1 deletion app/components/main-header.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class MainHeaderComponent extends Component {
@service('print') printSvc;

@service() media;

bookmarks;
@tracked bookmarks;

@tracked savedLayerSets;

@computed('bookmarks.length', 'savedLayerSets.length')
get totalBookmarks() {
return this.bookmarks.length + this.savedLayerSets.length;
}
}
124 changes: 124 additions & 0 deletions app/controllers/bookmarks.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
/* eslint-disable no-unused-expressions */
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { computed as computedProp } from '@ember/object';
import { Promise } from 'rsvp';

const QUERY_PARAMS_LIST = [
'selectedZoning',
'selectedOverlays',
'selectedFirm',
'selectedPfirm',
'selectedCouncilDistricts',
'selectedLayerGroup',
];

export default Controller.extend({
mainMap: service(),
metrics: service(),
router: service(),

savedLayerSets: window.localStorage['saved-layer-sets']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be more trouble than it's worth but did you consider making an Ember model for these bookmarkable layer sets, similar to how we have a bookmark model already? This approach isn't bad but using a model might be more Ember-y

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered it, but abandoned the idea after looking at the way the bookmarks were set up and finding the abstraction was a little much for me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about you spend another day or two on that approach? We already use LocalStorageSerializer in the serializer for bookmarks and you may be able to take advantage of the existing layer group model for a property on your new model.

Keeping all of this in a controller feels like breaking Ember's conventions which tends to lead to tech debt. I'm more comfortable taking on debt in this repo than I would be in a newer thing, but I think this situation warrants spending a little more time trying to do it the "ideal" way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhochbaum-dcp Have you spent any more time on the approach I described here? If you have and are still hitting a wall, I won't hold up the PR over it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent another day on it and got nowhere.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I'll take another look at the PR as is and leave a review.

? JSON.parse(window.localStorage['saved-layer-sets'])
: [],

editMode: false,

track(act) {
gtag('event', 'saved_layer_sets', {
event_category: 'Saved Layer Sets',
event_action: act,
});
this.metrics.trackEvent('MatomoTagManager', {
category: 'Saved Layer Sets',
action: act,
name: act,
});
},
// because we must compute the record types based on multiple
// promises, the model uses Promise.all
// this gets us in trouble when we need to do
Expand All @@ -26,5 +55,100 @@ export default Controller.extend({
zoom: 15,
});
},

bookmarkCurrentLayerSet() {
let allLayers = [];
const visibleLayers = [];
const visibleLayerGroups = [];
this.router.currentRoute.parent.attributes.layerGroups.forEach((lg) => {
allLayers = allLayers.concat(lg.layers);
lg.visible ? visibleLayerGroups.push(lg.id) : null;
});
allLayers.forEach((layer) => {
layer.visibility ? visibleLayers.push(layer.id) : null;
});

const queryParams = {};
['layer-groups', ...QUERY_PARAMS_LIST].forEach((selected) => {
queryParams[selected] = this.router.currentRoute.queryParams[selected]
? JSON.parse(this.router.currentRoute.queryParams[selected])
: undefined;
});

const layerSet = {
id: crypto.randomUUID(),
name: 'New Saved Layer Set',
visibleLayers,
visibleLayerGroups,
queryParams,
};
this.set('savedLayerSets', [...this.savedLayerSets, layerSet]);
window.localStorage['saved-layer-sets'] = JSON.stringify(
this.savedLayerSets
);
this.track('bookmarkCurrentLayerSet');
// Hack to update the # which doesn't update automatically
document.querySelector('.badge.sup').innerText =
parseInt(document.querySelector('.badge.sup').innerText, 10) + 1;
},

deleteBookmarkedLayerSettings(id) {
this.set(
'savedLayerSets',
[...this.savedLayerSets].filter((lg) => lg.id !== id)
);
window.localStorage['saved-layer-sets'] = JSON.stringify(
this.savedLayerSets
);
this.track('deleteBookmarkedLayerSettings');
// Hack to update the # which doesn't update automatically
document.querySelector('.badge.sup').innerText =
parseInt(document.querySelector('.badge.sup').innerText, 10) - 1;
},

updateBookmarkedLayerSettings(id) {
const newLayerSets = [...this.savedLayerSets];
const updatedLayerSetIndex = newLayerSets.findIndex((lg) => lg.id === id);
newLayerSets[updatedLayerSetIndex].name =
document.getElementById('name').value;
this.set('savedLayerSets', newLayerSets);
window.localStorage['saved-layer-sets'] = JSON.stringify(
this.savedLayerSets
);
this.set('editMode', false);
// without the below, the name won't update in the dom
setTimeout(function () {
document.getElementById(id).innerText =
newLayerSets[updatedLayerSetIndex].name;
}, 1);
this.track('finishUpdateBookmarkedLayerSettings');
},

turnOnEditMode(id) {
this.set('editMode', id);
this.track('beginUpdateBookmarkedLayerSettings');
},

loadBookmarkedLayerSettings(bookmarkId) {
const layerToLoad = this.savedLayerSets.find(
(lg) => bookmarkId === lg.id
);
const layerGroups = [
...this.router.currentRoute.parent.attributes.layerGroups,
];
layerGroups.forEach((lg) => {
lg.visible = !!layerToLoad.visibleLayerGroups.includes(lg.id);
lg.layers.forEach((layer) => {
layer.visibility = !!layerToLoad.visibleLayers.includes(layer.id);
});
});

QUERY_PARAMS_LIST.forEach((selected) => {
this.router.currentRoute.queryParams[selected] =
layerToLoad.queryParams[selected];
});

this.track('loadBookmarkedLayerSettings');
},
},
});
5 changes: 5 additions & 0 deletions app/routes/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,16 @@ export default Route.extend({

await bookmarks.invoke('get', 'bookmark');

const savedLayerSets = window.localStorage['saved-layer-sets']
? JSON.parse(window.localStorage['saved-layer-sets'])
: [];

return {
layerGroups,
layerGroupsObject,
meta,
bookmarks,
savedLayerSets,
};
},
});
2 changes: 1 addition & 1 deletion app/templates/application.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<MainHeader @bookmarks={{this.model.bookmarks}} />
<MainHeader @bookmarks={{this.model.bookmarks}} @savedLayerSets={{this.model.savedLayerSets}} />
<div class="{{if this.printSvc.enabled "print-view"}}">
<LinkTo @route="index" class="index-active-detector sr-only">Homepage</LinkTo>
{{#if this.printSvc.enabled}}
Expand Down
58 changes: 57 additions & 1 deletion app/templates/bookmarks.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
{{get (promise-rejected-reason this.bookmarksSettled) 'message'}}.
{{/if}}

{{#unless this.model.length}}
{{#unless (or this.model.length this.savedLayerSets.length)}}
<div class="no-bookmarks">
<div class="align-self-middle large-text-center">
<h1 class="header-large">
Expand All @@ -47,9 +47,65 @@
<p>
From this page you can quickly navigate to all of your bookmarked&nbsp;information.
</p>
<p>
If you would like to bookmark the current selected set of layers, use the button below.
</p>
<a
class="button gray tiny"
onclick={{action 'bookmarkCurrentLayerSet'}}
>
Bookmark Current Layers
</a>
</div>
</div>
{{/unless}}

<div style="margin-top: 2rem">
<h3>
Layer Sets
<a
class="button gray tiny float-right"
onclick={{action 'bookmarkCurrentLayerSet'}}
>
Bookmark Current Layers
</a>
</h3>

{{#unless this.savedLayerSets.length}}
<p>No current saved layer sets.</p>
{{/unless}}
<ul class="no-bullet">
{{#each this.savedLayerSets as |bookmark|}}
<li class="lot-bookmark">
{{#if (eq this.editMode bookmark.id)}}
<form onsubmit={{action 'updateBookmarkedLayerSettings' bookmark.id}} style="display: flex; flex-direction: row; flex-wrap:nowrap;">
<input type="text" id="name" value={{bookmark.name}} style="max-width: 100%">
<input type="submit" value="Save" class="button gray" style="margin-right: 0.5rem;
margin-left: 0.5rem;">
<button
class="float-right lu-red delete-bookmark-button" type="button" {{action 'deleteBookmarkedLayerSettings' bookmark.id}}
>
&times;
</button>
</form>
{{else}}
<a onclick={{action 'turnOnEditMode' bookmark.id}}>
<FaIcon @icon="pen" color="#ae561f" cursor="pointer" />
</a>
<LinkTo @route="bookmarks" @query={{bookmark.queryParams}} id={{bookmark.id}} onclick={{action 'loadBookmarkedLayerSettings' bookmark.id}}>
{{bookmark.name}}
</LinkTo>
<button
class="float-right lu-red delete-bookmark-button" type="button" {{action 'deleteBookmarkedLayerSettings' bookmark.id}}
>
&times;
</button>
{{/if}}
</li>
{{/each}}
</ul>
</div>


{{outlet}}
</div>
4 changes: 2 additions & 2 deletions app/templates/components/main-header.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
<LinkTo @route="bookmarks">
<FaIcon @icon="bookmark" />
Saved
{{#if this.bookmarks.length}}
{{#if this.totalBookmarks}}
<span class="badge sup">
{{this.bookmarks.length}}
{{this.totalBookmarks}}
</span>
{{/if}}
</LinkTo>
Expand Down
Loading