Skip to content

Commit

Permalink
app: add global search store
Browse files Browse the repository at this point in the history
  • Loading branch information
verdie-g committed Jun 1, 2018
1 parent 5741926 commit b89ec6f
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 103 deletions.
6 changes: 1 addition & 5 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@
<Search id="search" class="column" />
<Credits id="credits" class="column is-narrow" />
</div>
<WKMap id="map" v-on:mapMove="updateJobs" />
<WKMap id="map" />
</div>
</div>
</template>

<script>
import { mapActions } from 'vuex';
import Credits from './components/Credits';
import JobList from './components/JobList';
import Search from './components/Search';
Expand All @@ -32,9 +31,6 @@ export default {
computed: {
},
methods: {
...mapActions([
'updateJobs',
]),
},
};
</script>
Expand Down
9 changes: 4 additions & 5 deletions src/api.js → src/algolia/algolia.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ function createSearchStore(indexName) {
return searchStore;
}

export const jobsIndex = client.initIndex(jobsIndexName);

export function createJobsSearchStore() {
return createSearchStore(jobsIndexName);
}
export {
jobsIndexName,
createSearchStore,
};
72 changes: 72 additions & 0 deletions src/algolia/jobsSearchStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { jobsIndexName, createSearchStore } from './algolia';

class JobsSearchStore {
constructor(queryParameters = {}) {
this._searchStore = createSearchStore(jobsIndexName);
this._jobsBox = null;
this._searchStore.queryParameters = queryParameters;
}

get searchStore() {
return this._searchStore;
}

get jobs() {
return this._searchStore.results;
}

getFilteredJobs(filter) {
return this.jobs.filter(filter);
}

setJobsBox(jobsBox) {
if (this._jobsBox !== null && this._jobsBox.contains(jobsBox)) {
return;
}

const grownBox = jobsBox.pad(0.5);

const boundingBox = [
grownBox.getNorthEast().lat,
grownBox.getNorthEast().lng,
grownBox.getSouthWest().lat,
grownBox.getSouthWest().lng,
];

this._searchStore._helper.setQueryParameter('insideBoundingBox', [boundingBox]);

if (this._jobsBox === null) {
this.start();
this.refresh();
}

this._jobsBox = grownBox;
}

start() {
this._searchStore.start();
}

stop() {
this._searchStore.stop();
}

refresh() {
this._searchStore.refresh();
}
}

export default new JobsSearchStore({
hitsPerPage: 1000,
attributesToRetrieve: [
'_geoloc',
'company_logo_url',
'company_name',
'contract_type.fr',
'name',
'office_city',
'published_at',
'websites_urls',
],
analytics: false,
});
25 changes: 22 additions & 3 deletions src/components/JobList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,36 @@
</template>

<script>
import { mapGetters } from 'vuex';
import { mapGetters, mapState } from 'vuex';
import jobsSearchStore from '../algolia/jobsSearchStore';
import { sortByDistance } from '../helpers/GeoHelper';
export default {
name: 'JobList',
nbJobs: 30,
data() {
return {
jobsSearchStore,
searchStore: jobsSearchStore.searchStore,
};
},
computed: {
...mapState([
'mapViewport',
]),
...mapGetters([
'jobsSortedByDistance',
'mapCenter',
]),
sortedJobs() {
return this.jobsSortedByDistance.slice(0, this.$options.nbJobs);
const viewportJobs = this.jobsSearchStore.getFilteredJobs(j =>
this.mapViewport.contains(j._geoloc));
if (viewportJobs.length === 0) {
return viewportJobs;
}
return sortByDistance(this.mapCenter, viewportJobs, j => j._geoloc)
.slice(0, this.$options.nbJobs);
},
},
methods: {
Expand Down
8 changes: 2 additions & 6 deletions src/components/Search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,9 @@
</template>

<script>
import { createJobsSearchStore } from '../api';
import RefinementListDropdown from './ais/RefinementListDropdown';
import RefinementListDropdownList from './ais/RefinementListDropdownList';
const searchStore = createJobsSearchStore();
import jobsSearchStore from '../algolia/jobsSearchStore';
const professions = {
name: 'profession',
Expand All @@ -71,13 +69,11 @@ export default {
},
data() {
return {
searchStore,
searchStore: jobsSearchStore.searchStore,
professions,
};
},
mounted() {
this.searchStore.start();
this.searchStore.refresh();
},
};
</script>
Expand Down
35 changes: 16 additions & 19 deletions src/components/WKMap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@
<l-map ref="map" :zoom="zoom" :center="center" :options="mapOptions" v-on:moveend="onMoveEnd">
<l-tile-layer :url="url" :attribution="attribution" />
<l-control-zoom position="bottomleft"></l-control-zoom>
<l-marker-cluster :options="clusterOptions" v-if="jobs.length !== 0">
<l-marker
v-for="job in jobs"
v-if="job._geoloc !== null"
:key="job.objectID"
:lat-lng="{ lat: job._geoloc.lat, lng: job._geoloc.lng }">
<l-popup :content="job.company_name + ': ' + job.name"></l-popup>
</l-marker>
<l-marker-cluster :options="clusterOptions" v-if="searchStore.results.length !== 0">
<ais-results :search-store="searchStore">
<template slot-scope="{ result }">
<l-marker
v-if="result._geoloc !== null"
:key="result.objectID"
:lat-lng="{ lat: result._geoloc.lat, lng: result._geoloc.lng }">
<l-popup :content="result.company_name + ': ' + result.name"></l-popup>
</l-marker>
</template>
</ais-results>
</l-marker-cluster>
</l-map>
</template>

<script>
import L from 'leaflet';
import { LMap, LTileLayer, LMarker, LPopup, LControlZoom } from 'vue2-leaflet';
import { mapState, mapActions } from 'vuex';
import { mapActions } from 'vuex';
import LMarkerCluster from 'vue2-leaflet-markercluster';
import jobsSearchStore from '../algolia/jobsSearchStore';
import iconRetinaUrl from '../../node_modules/leaflet/dist/images/marker-icon-2x.png';
import iconUrl from '../../node_modules/leaflet/dist/images/marker-icon.png';
import shadowUrl from '../../node_modules/leaflet/dist/images/marker-shadow.png';
Expand Down Expand Up @@ -53,25 +57,18 @@ export default {
clusterOptions: {
maxClusterRadius: 25,
},
searchStore: jobsSearchStore.searchStore,
};
},
computed: {
...mapState([
'jobs',
]),
},
methods: {
...mapActions([
'updateMapViewport',
]),
onMoveEnd() {
this.emitMapMove();
},
emitMapMove() {
const map = this.$refs.map.mapObject;
const viewport = map.getBounds();
this.updateMapViewport(viewport);
this.$emit('mapMove');
jobsSearchStore.setJobsBox(viewport);
},
zoomOnJob(job) {
const map = this.$refs.map.mapObject;
Expand All @@ -91,7 +88,7 @@ export default {
this.$root.$on('select-job', this.zoomOnJob);
},
mounted() {
this.emitMapMove();
this.onMoveEnd();
},
beforeDestroy() {
this.$root.$off('select-job', this.zoomOnJob);
Expand Down
3 changes: 2 additions & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Buefy from 'buefy';
import Vue from 'vue';
import VueMoment from 'vue-moment';
import Vuex from 'vuex';
import { RefinementList } from 'vue-instantsearch';
import { RefinementList, Results } from 'vue-instantsearch';

import App from './App';
import store from './store/index';
Expand All @@ -14,6 +14,7 @@ Vue.use(VueMoment);
Vue.use(Vuex);

Vue.component('ais-refinement-list', RefinementList);
Vue.component('ais-results', Results);

Vue.config.productionTip = false;

Expand Down
36 changes: 0 additions & 36 deletions src/store/actions.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,3 @@
import { jobsIndex } from '../api';

export async function updateJobs({ commit, state }) {
if (state.jobsBox !== null && state.jobsBox.contains(state.mapViewport)) {
return;
}

commit('updateJobsBox', state.mapViewport.pad(0.5));

const boundingBox = [
state.jobsBox.getNorthEast().lat,
state.jobsBox.getNorthEast().lng,
state.jobsBox.getSouthWest().lat,
state.jobsBox.getSouthWest().lng,
];

const res = await jobsIndex.search({
query: state.query,
hitsPerPage: 1000,
insideBoundingBox: [boundingBox],
attributesToRetrieve: [
'_geoloc',
'company_logo_url',
'company_name',
'contract_type.fr',
'name',
'office_city',
'published_at',
'websites_urls',
],
analytics: false,
});

commit('getJobs', res.hits);
}

export function updateMapViewport({ commit }, viewport) {
commit('updateMapViewport', viewport);
}
17 changes: 0 additions & 17 deletions src/store/getters.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
import { sortByDistance } from '../helpers/GeoHelper';

export function jobsInsideViewport(state) {
if (state.mapViewport === null) {
return [];
}
return state.jobs.filter(j => state.mapViewport.contains(j._geoloc));
}

export function jobsSortedByDistance(state, getters) {
const viewportJobs = getters.jobsInsideViewport;
if (viewportJobs.length === 0) {
return viewportJobs;
}
return sortByDistance(getters.mapCenter, viewportJobs, j => j._geoloc);
}

export function mapCenter(state) {
return state.mapViewport.getCenter();
}
3 changes: 0 additions & 3 deletions src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ Vue.use(Vuex);
export default new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
state: {
jobs: [],
jobsBox: null,
mapViewport: null,
query: '',
},
actions,
getters,
Expand Down
8 changes: 0 additions & 8 deletions src/store/mutations.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
export function updateJobsBox(state, jobsBox) {
state.jobsBox = jobsBox;
}

export async function getJobs(state, jobs) {
state.jobs = jobs;
}

export function updateMapViewport(state, viewport) {
state.mapViewport = viewport;
}

0 comments on commit b89ec6f

Please sign in to comment.