dark theme hyperlink color, more reformatting, add navbar + insightsClient to AIS, basic Vuex, VueClipboard import, Router beforeEach guard (currently unused)

This commit is contained in:
Xevion
2020-09-07 23:46:06 -05:00
parent a0d7a78f73
commit c0305ef414
10 changed files with 463 additions and 265 deletions

View File

@@ -3417,6 +3417,16 @@
"integrity": "sha1-ovSEN6LKqaIkNueUvwceyeYc7fY=", "integrity": "sha1-ovSEN6LKqaIkNueUvwceyeYc7fY=",
"dev": true "dev": true
}, },
"clipboard": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
"integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"clipboardy": { "clipboardy": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npm.taobao.org/clipboardy/download/clipboardy-2.3.0.tgz?cache=0&sync_timestamp=1584603949505&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipboardy%2Fdownload%2Fclipboardy-2.3.0.tgz", "resolved": "https://registry.npm.taobao.org/clipboardy/download/clipboardy-2.3.0.tgz?cache=0&sync_timestamp=1584603949505&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipboardy%2Fdownload%2Fclipboardy-2.3.0.tgz",
@@ -4436,6 +4446,11 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true "dev": true
}, },
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
},
"depd": { "depd": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz",
@@ -5990,6 +6005,14 @@
"slash": "^2.0.0" "slash": "^2.0.0"
} }
}, },
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"requires": {
"delegate": "^3.1.2"
}
},
"graceful-fs": { "graceful-fs": {
"version": "4.2.4", "version": "4.2.4",
"resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz?cache=0&sync_timestamp=1588087063358&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fgraceful-fs%2Fdownload%2Fgraceful-fs-4.2.4.tgz", "resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz?cache=0&sync_timestamp=1588087063358&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fgraceful-fs%2Fdownload%2Fgraceful-fs-4.2.4.tgz",
@@ -9938,6 +9961,11 @@
"ajv-keywords": "^3.4.1" "ajv-keywords": "^3.4.1"
} }
}, },
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
},
"select-hose": { "select-hose": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/select-hose/download/select-hose-2.0.0.tgz", "resolved": "https://registry.npm.taobao.org/select-hose/download/select-hose-2.0.0.tgz",
@@ -10964,6 +10992,11 @@
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
"dev": true "dev": true
}, },
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
"tmp": { "tmp": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz", "resolved": "https://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz",
@@ -11474,6 +11507,14 @@
"resolved": "https://registry.npmjs.org/vue-autosuggest/-/vue-autosuggest-2.2.0.tgz", "resolved": "https://registry.npmjs.org/vue-autosuggest/-/vue-autosuggest-2.2.0.tgz",
"integrity": "sha512-cHgEakpoRUOaqXXEo8RcRrbSTM3eAaCu9b55ZXiKbaS6IUD8ewqffQrMy/A1DXqHSQbyEEGui4oAsCbRge29Jg==" "integrity": "sha512-cHgEakpoRUOaqXXEo8RcRrbSTM3eAaCu9b55ZXiKbaS6IUD8ewqffQrMy/A1DXqHSQbyEEGui4oAsCbRge29Jg=="
}, },
"vue-clipboard2": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/vue-clipboard2/-/vue-clipboard2-0.3.1.tgz",
"integrity": "sha512-H5S/agEDj0kXjUb5GP2c0hCzIXWRBygaWLN3NEFsaI9I3uWin778SFEMt8QRXiPG+7anyjqWiw2lqcxWUSfkYg==",
"requires": {
"clipboard": "^2.0.0"
}
},
"vue-eslint-parser": { "vue-eslint-parser": {
"version": "7.1.0", "version": "7.1.0",
"resolved": "https://registry.npm.taobao.org/vue-eslint-parser/download/vue-eslint-parser-7.1.0.tgz", "resolved": "https://registry.npm.taobao.org/vue-eslint-parser/download/vue-eslint-parser-7.1.0.tgz",

View File

@@ -19,6 +19,7 @@
"instantsearch.css": "7.1.0", "instantsearch.css": "7.1.0",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-autosuggest": "^2.2.0", "vue-autosuggest": "^2.2.0",
"vue-clipboard2": "^0.3.1",
"vue-instantsearch": "^3.1.0", "vue-instantsearch": "^3.1.0",
"vue-loading-skeleton": "^1.1.9", "vue-loading-skeleton": "^1.1.9",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",

View File

@@ -1,10 +1,39 @@
<template> <template>
<div id="app"> <div id="app">
<ais-instant-search index-name="prod_THEOFFICEQUOTES" :search-client="searchClient"> <b-navbar>
<b-container :fluid="true" class="py-3 px-lg-5 px-md-4"> <b-navbar-brand>
<router-link :to="{ name: 'Home' }" class="no-link">
The Office Quotes
</router-link>
</b-navbar-brand>
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav>
<b-nav-item href="#">
<router-link :to="{ name: 'Home' }" class="no-link">
Home
</router-link>
</b-nav-item>
<b-nav-item href="#">
<router-link :to="{ name: 'Home' }" class="no-link">
About
</router-link>
</b-nav-item>
</b-navbar-nav>
</b-collapse>
</b-navbar>
<ais-instant-search
index-name="prod_THEOFFICEQUOTES"
:search-client="searchClient"
:insights-client="insightsClient"
>
<b-container :fluid="true" class="py-2 px-lg-5 px-md-4">
<b-row class="my-3 pl-1"> <b-row class="my-3 pl-1">
<b-col lg="3" xl="2" md="12"> <b-col lg="3" xl="2" md="12">
<ais-search-box @keydown.native="showResults" ref="searchbox" placeholder="Search here…"/> <ais-search-box
@keydown.native="showResults"
ref="searchbox"
placeholder="Search here…"
/>
</b-col> </b-col>
</b-row> </b-row>
<b-row> <b-row>
@@ -17,62 +46,87 @@
<b-col md="0" lg="0" xl="2"></b-col> <b-col md="0" lg="0" xl="2"></b-col>
</b-row> </b-row>
</b-container> </b-container>
<ais-configure :clickAnalytics="true" />
</ais-instant-search> </ais-instant-search>
</div> </div>
</template> </template>
<style lang="scss"> <style lang="scss">
@import "assets/scss/_variables"; @import "assets/scss/_variables";
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap'); @import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap");
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@500&display=swap");
body { background-color: $grey-0; font-family: 'Roboto', sans-serif; } body {
background-color: $grey-0;
font-family: "Roboto", sans-serif;
}
.ais-SearchBox-form { .navbar {
border: none; background-color: $grey-2;
} }
.ais-SearchBox-input { .navbar-brand {
color: $grey-8; font-family: "Poppins", sans-serif;
background-color: $grey-6; font-size: 1.4em;
border-color: transparent; color: $grey-11 !important;
border-radius: 1px; }
}
.nav-link {
.ais-SearchBox-submitIcon, .ais-SearchBox-resetIcon { color: $grey-9 !important;
> path { fill: $grey-9; } }
}
.ais-SearchBox-form {
.ais-SearchBox-input::placeholder { border: none;
color: white; }
.ais-SearchBox-input {
color: $grey-8;
background-color: $grey-6;
border-color: transparent;
border-radius: 1px;
}
.ais-SearchBox-submitIcon,
.ais-SearchBox-resetIcon {
> path {
fill: $grey-9;
} }
}
.ais-SearchBox-input::placeholder {
color: white;
}
</style> </style>
<script> <script>
import algoliasearch from 'algoliasearch/lite'; import algoliasearch from "algoliasearch/lite";
import SeasonList from './components/SeasonList.vue'; import SeasonList from "./components/SeasonList.vue";
import 'instantsearch.css/themes/algolia-min.css'; import "instantsearch.css/themes/algolia-min.css";
export default { export default {
name: 'App', name: "App",
components: { components: {
SeasonList, SeasonList,
}, },
data() { data() {
return { return {
searchClient: algoliasearch( searchClient: algoliasearch(
process.env.VUE_APP_ALGOLIA_APP_ID, process.env.VUE_APP_ALGOLIA_APP_ID,
process.env.VUE_APP_ALGOLIA_API_KEY, process.env.VUE_APP_ALGOLIA_API_KEY
), ),
}; insightsClient: window.aa,
}, };
methods: { },
showResults() { methods: {
if (this.$refs.searchbox.currentRefinement !== '' && this.$route.path !== '/search_results') { showResults() {
this.$router.push({ name: 'SearchResults' }); if (
} this.$refs.searchbox.currentRefinement !== "" &&
this.$route.path !== "/search_results"
) {
this.$router.push({name: "SearchResults"});
}
},
}, },
},
}; };
</script> </script>

View File

@@ -3,20 +3,28 @@
<b-breadcrumb :items="breadcrumbs"></b-breadcrumb> <b-breadcrumb :items="breadcrumbs"></b-breadcrumb>
<b-card> <b-card>
<h4 v-if="character">{{ this.$route.params.character }}</h4> <h4 v-if="character">{{ this.$route.params.character }}</h4>
<Skeleton v-else style="max-width: 30%;"></Skeleton> <Skeleton v-else style="max-width: 30%"></Skeleton>
</b-card> </b-card>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../assets/scss/_variables"; @import "../assets/scss/_variables";
.card-body h4, .breadcrumb-item.active {
.card-body h4,
.breadcrumb-item.active {
text-transform: capitalize; text-transform: capitalize;
} }
.skeleton { .skeleton {
min-height: 24px; min-height: 24px;
} }
a {
color: #1296ff;
&:hover {
color: #007fe0;
}
}
.breadcrumb-item + .breadcrumb-item::before { .breadcrumb-item + .breadcrumb-item::before {
color: $grey-10; color: $grey-10;
@@ -25,6 +33,7 @@
.breadcrumb { .breadcrumb {
background-color: $grey-3; background-color: $grey-3;
border-radius: 0; border-radius: 0;
.breadcrumb-item { .breadcrumb-item {
color: $grey-10; color: $grey-10;
} }
@@ -35,32 +44,35 @@
import Skeleton from './Skeleton.vue'; import Skeleton from './Skeleton.vue';
export default { export default {
data() { data() {
return { return {
character: null, character: null,
}; };
},
components: {
Skeleton,
},
computed: {
breadcrumbs() {
return [
{
text: 'Home',
to: { name: 'Home' },
},
{
text: 'Characters',
to: { name: 'Home' },
},
{
text: this.character !== null ? this.character.name : this.$route.params.character,
active: true,
},
];
}, },
}, components: {
methods: {}, Skeleton,
},
computed: {
breadcrumbs() {
return [
{
text: 'Home',
to: {name: 'Home'},
},
{
text: 'Characters',
to: {name: 'Home'},
},
{
text:
this.character !== null
? this.character.name
: this.$route.params.character,
active: true,
},
];
},
},
methods: {},
}; };
</script> </script>

View File

@@ -1,72 +1,85 @@
<template> <template>
<div class="pt-2" v-if="characters" :fluid=true> <div class="pt-2" v-if="characters" :fluid="true">
<b-button squared class="mx-2 my-1 character-button" size="sm" <b-button
v-for="character in characters" :key="character.name" :id="`character-${character.id}`" squared
:title="`${character.appearances} Quote${character.appearances > 1 ? 's' : ''}`" class="mx-2 my-1 character-button"
:to="{ name: 'Character', params: { character: character.id } }" size="sm"
v-for="character in characters"
:key="character.name"
:id="`character-${character.id}`"
:title="`${character.appearances} Quote${
character.appearances > 1 ? 's' : ''
}`"
:to="{ name: 'Character', params: { character: character.id } }"
> >
{{ character.name }} {{ character.name }}
<b-badge class="ml-1">{{ character.appearances}}</b-badge> <b-badge class="ml-1">{{ character.appearances }}</b-badge>
</b-button> </b-button>
</div> </div>
</template> </template>
<style lang="scss"> <style lang="scss">
@import "../assets/scss/_variables"; @import "../assets/scss/_variables";
.btn { .btn {
box-shadow: none;
&:focus {
box-shadow: none; box-shadow: none;
&:focus { box-shadow: none; }
} }
}
.character-button { .character-button {
color: $grey-10; color: $grey-10;
background-color: $grey-4; background-color: $grey-4;
border-color: $grey-3; border-color: $grey-3;
.badge {
color: lighten($grey-11, 8%); .badge {
} color: lighten($grey-11, 8%);
} }
}
.character-button { .character-button {
&:focus { &:focus {
background-color: $grey-6 !important; background-color: $grey-6 !important;
border-color: $grey-4 !important; border-color: $grey-4 !important;
&:active { box-shadow: none !important;}
}
&:hover {
background-color: $grey-4 !important;
border-color: $grey-3 !important;
}
&:active { &:active {
background-color: $grey-3 !important; box-shadow: none !important;
border-color: $grey-3 !important;
} }
} }
.character-button > .badge { &:hover {
background-color: $grey-7; background-color: $grey-4 !important;
border-color: $grey-3 !important;
} }
/*.btn-dark {*/ &:active {
/* &:not(:disabled), &:not {*/ background-color: $grey-3 !important;
/* */ border-color: $grey-3 !important;
/* }*/ }
/*}*/ }
/*.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, {*/ .character-button > .badge {
/* color: #ffffff;*/ background-color: $grey-7;
/* background-color: #1d2124;*/ }
/* border-color: #171a1d;*/
/*}*/ /*.btn-dark {*/
/* &:not(:disabled), &:not {*/
/* */
/* }*/
/*}*/
/*.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, {*/
/* color: #ffffff;*/
/* background-color: #1d2124;*/
/* border-color: #171a1d;*/
/*}*/
</style> </style>
<script> <script>
export default { export default {
name: 'CharacterList', name: "CharacterList",
props: ['characters'], props: ["characters"],
}; };
</script> </script>

View File

@@ -1,21 +1,39 @@
<template> <template>
<div> <div>
<b-card :title="`Season ${this.$route.params.season} Episode ${this.$route.params.episode} \ <b-card
- ${episode != null ? episode.title : ''}`" class="mb-4"> :title="`Season ${this.$route.params.season} Episode ${
<span v-if="episode"> this.$route.params.episode
{{ episode.description }} } \
</span> - ${episode != null ? episode.title : ''}`"
<CharacterList v-if="episode && episode.characters" :characters="episode.characters"></CharacterList> class="mb-4"
>
<span v-if="episode">
{{ episode.description }}
</span>
<CharacterList
v-if="episode && episode.characters"
:characters="episode.characters"
></CharacterList>
</b-card> </b-card>
<div v-if="episode != null"> <div v-if="episode != null">
<b-card v-for="(scene, sceneIndex) in episode.scenes" :key="`scene-${sceneIndex}`" <b-card
class="mb-1" body-class="p-0"> v-for="(scene, sceneIndex) in episode.scenes"
:key="`scene-${sceneIndex}`"
class="mb-1"
body-class="p-0"
>
<b-card-text class="my-2"> <b-card-text class="my-2">
<QuoteList :quotes="scene.quotes" :sceneIndex="sceneIndex"></QuoteList> <QuoteList
<span v-if="scene.deleted" class="mt-n2 mb-4 text-muted deleted-scene pl-2" :quotes="scene.quotes"
:footer="`Deleted Scene ${scene.deleted}`"> :sceneIndex="sceneIndex"
Deleted Scene {{ scene.deleted }} ></QuoteList>
</span> <span
v-if="scene.deleted"
class="mt-n2 mb-4 text-muted deleted-scene pl-2"
:footer="`Deleted Scene ${scene.deleted}`"
>
Deleted Scene {{ scene.deleted }}
</span>
</b-card-text> </b-card-text>
</b-card> </b-card>
</div> </div>
@@ -24,7 +42,7 @@
<style lang="scss"> <style lang="scss">
.card-title { .card-title {
font-family: 'Montserrat', sans-serif; font-family: "Montserrat", sans-serif;
font-weight: 600; font-weight: 600;
} }
@@ -35,49 +53,50 @@
</style> </style>
<script> <script>
import axios from 'axios'; import axios from "axios";
import QuoteList from './QuoteList.vue'; import QuoteList from "./QuoteList.vue";
import CharacterList from './CharacterList.vue'; import CharacterList from "./CharacterList.vue";
export default { export default {
name: 'Episode', name: "Episode",
components: { components: {
QuoteList, QuoteList,
CharacterList, CharacterList,
}, },
data() { data() {
return { return {
episode: null, episode: null,
}; };
}, },
methods: { methods: {
getEpisode() { getEpisode() {
const path = `${process.env.VUE_APP_BASE_APP_URL}/api/episode/\ const path = `${process.env.VUE_APP_API_URL}/api/episode/\
${this.$route.params.season}/${this.$route.params.episode}/`; ${this.$route.params.season}/${this.$route.params.episode}/`;
axios.get(path) axios
.then((res) => { .get(path)
this.episode = res.data; .then((res) => {
// Scroll this.episode = res.data;
if (this.$route.hash) { // Scroll
this.$nextTick(() => { if (this.$route.hash) {
const section = document.getElementById(this.$route.hash.substring(1)); this.$nextTick(() => {
this.$scrollTo(section, 500, { easing: 'ease-in' }); const section = document.getElementById(this.$route.hash.substring(1));
}); this.$scrollTo(section, 500, {easing: "ease-in"});
} });
}) }
.catch((error) => { })
// eslint-disable-next-line no-console .catch((error) => {
console.error(error); // eslint-disable-next-line no-console
}); console.error(error);
});
},
}, },
}, created() {
created() { this.getEpisode();
this.getEpisode(); },
}, watch: {
watch: { $route() {
$route() { this.getEpisode();
this.getEpisode(); },
}, },
},
}; };
</script> </script>

View File

@@ -1,8 +1,9 @@
<template> <template>
<b-card title="The Office Quotes"> <b-card title="The Office Quotes">
<b-card-text> <b-card-text>
A Vue.js application serving you {{ stats.totals.quote }} quotes from your favorite show - The Office. A Vue.js application serving you {{ stats.totals.quote }} quotes from your
<br> favorite show - The Office.
<br/>
Click on a Season and Episode on the left-hand sidebar to view quotes. Click on a Season and Episode on the left-hand sidebar to view quotes.
Search for quotes with the instant searchbox. Search for quotes with the instant searchbox.
</b-card-text> </b-card-text>
@@ -10,41 +11,42 @@
</template> </template>
<style lang="scss"> <style lang="scss">
@import "../assets/scss/_variables"; @import "../assets/scss/_variables";
.card { .card {
color: $grey-9; color: $grey-9;
background-color: $grey-2; background-color: $grey-2;
border-bottom: 1px solid $grey-1; border-bottom: 1px solid $grey-1;
border-radius: 0; border-radius: 0;
} }
</style> </style>
<script> <script>
import axios from 'axios'; import axios from "axios";
export default { export default {
name: 'Home', name: "Home",
data() { data() {
return { return {
stats: null, stats: null,
}; };
}, },
methods: { methods: {
getStats() { getStats() {
const path = `${process.env.VUE_APP_BASE_APP_URL}/api/stats/`; const path = `${process.env.VUE_APP_API_URL}/api/stats/`;
axios.get(path) axios
.then((res) => { .get(path)
this.stats = res.data; .then((res) => {
}) this.stats = res.data;
.catch((error) => { })
// eslint-disable-next-line no-console .catch((error) => {
console.error(error); // eslint-disable-next-line no-console
}); console.error(error);
});
},
},
created() {
this.getStats();
}, },
},
created() {
this.getStats();
},
}; };
</script> </script>

View File

@@ -1,9 +1,19 @@
<template> <template>
<b-card class="mb-1" body-class="p-0 expandable-result" footer-class="my-1" <b-card
v-on:mouseover="hover" v-on:click="toggleExpansion" :class="[expanded ? 'expanded' : '']"> class="mb-1"
body-class="p-0 expandable-result"
footer-class="my-1"
v-on:mouseover="hover"
v-on:click="toggleExpansion"
:class="[expanded ? 'expanded' : '']"
>
<b-card-text class="mu-2 py-1 mb-1"> <b-card-text class="mu-2 py-1 mb-1">
<table v-if="expanded" class="quote-list px-3 py-1 w-100"> <table v-if="expanded" class="quote-list px-3 py-1 w-100">
<tr v-for="(quote, index) in above" class="secondary" :key="`quote-a-${index}`"> <tr
v-for="(quote, index) in above"
class="secondary"
:key="`quote-a-${index}`"
>
<td class="quote-speaker my-3 pl-3"> <td class="quote-speaker my-3 pl-3">
<div>{{ quote.speaker }}</div> <div>{{ quote.speaker }}</div>
</td> </td>
@@ -12,10 +22,20 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="quote-speaker my-3 pl-3" v-html="item._highlightResult.speaker.value"></td> <td
<td class="quote-text w-100 pr-3" v-html="item._highlightResult.text.value"></td> class="quote-speaker my-3 pl-3"
v-html="item._highlightResult.speaker.value"
></td>
<td
class="quote-text w-100 pr-3"
v-html="item._highlightResult.text.value"
></td>
</tr> </tr>
<tr v-for="(quote, index) in below" class="secondary" :key="`quote-b-${index}`"> <tr
v-for="(quote, index) in below"
class="secondary"
:key="`quote-b-${index}`"
>
<td class="quote-speaker my-3 pl-3"> <td class="quote-speaker my-3 pl-3">
<div>{{ quote.speaker }}</div> <div>{{ quote.speaker }}</div>
</td> </td>
@@ -26,14 +46,27 @@
</table> </table>
<table v-else class="quote-list px-3 py-1 w-100"> <table v-else class="quote-list px-3 py-1 w-100">
<tr> <tr>
<td class="quote-speaker my-3 pl-3" v-html="item._highlightResult.speaker.value"></td> <td
<td class="quote-text w-100 pr-3" v-html="item._highlightResult.text.value"></td> class="quote-speaker my-3 pl-3"
v-html="item._highlightResult.speaker.value"
></td>
<td
class="quote-text w-100 pr-3"
v-html="item._highlightResult.text.value"
></td>
</tr> </tr>
</table> </table>
<router-link v-if="expanded" class="no-link search-result-link w-100 text-muted mb-2 ml-2" <router-link
:to="{ name: 'Episode', params: { season: item.season, episode: item.episode_rel }, v-if="expanded"
hash: `#${item.section_rel - 1}-${item.quote_rel - 1}` }"> class="no-link search-result-link w-100 text-muted mb-2 ml-2"
Season {{ item.season }} Episode {{ item.episode_rel }} Scene {{ item.section_rel }} :to="{
name: 'Episode',
params: { season: item.season, episode: item.episode_rel },
hash: `#${item.section_rel - 1}-${item.quote_rel - 1}`,
}"
>
Season {{ item.season }} Episode {{ item.episode_rel }} Scene
{{ item.section_rel }}
</router-link> </router-link>
</b-card-text> </b-card-text>
</b-card> </b-card>
@@ -73,57 +106,58 @@
font-weight: 600; font-weight: 600;
vertical-align: text-top; vertical-align: text-top;
text-align: right; text-align: right;
font-family: 'Montserrat', sans-serif; font-family: "Montserrat", sans-serif;
} }
</style> </style>
<script> <script>
import axios from 'axios'; import axios from "axios";
export default { export default {
props: ['item'], props: ["item"],
data() { data() {
return { return {
expanded: false, expanded: false,
fetching: false, fetching: false,
above: null, above: null,
below: null, below: null,
}; };
},
computed: {
fetched() {
return this.above !== null || this.below !== null;
}, },
}, computed: {
methods: { fetched() {
toggleExpansion() { return this.above !== null || this.below !== null;
this.expanded = !this.expanded; },
// if first time expanding, fetch quotes
if (!this.fetchQuotes()) {
this.hasExpanded = true;
this.fetchQuotes();
}
}, },
hover() { methods: {
if (!this.fetched && !this.fetching) { toggleExpansion() {
this.fetching = true; this.expanded = !this.expanded;
this.fetchQuotes(); // if first time expanding, fetch quotes
this.fetching = false; if (!this.fetchQuotes()) {
} this.hasExpanded = true;
}, this.fetchQuotes();
fetchQuotes() { }
const path = `${process.env.VUE_APP_BASE_APP_URL}/api/quote_surround?season=\ },
hover() {
if (!this.fetched && !this.fetching) {
this.fetching = true;
this.fetchQuotes();
this.fetching = false;
}
},
fetchQuotes() {
const path = `${process.env.VUE_APP_API_URL}/api/quote_surround?season=\
${this.item.season}&episode=${this.item.episode_rel}&scene=${this.item.section_rel}&quote=${this.item.quote_rel}`; ${this.item.season}&episode=${this.item.episode_rel}&scene=${this.item.section_rel}&quote=${this.item.quote_rel}`;
axios.get(path) axios
.then((res) => { .get(path)
this.above = res.data.above; .then((res) => {
this.below = res.data.below; this.above = res.data.above;
}) this.below = res.data.below;
.catch((error) => { })
// eslint-disable-next-line no-console .catch((error) => {
console.error(error); // eslint-disable-next-line no-console
}); console.error(error);
});
},
}, },
},
}; };
</script> </script>

View File

@@ -1,19 +1,29 @@
import 'bootstrap/dist/css/bootstrap.css'; import "bootstrap/dist/css/bootstrap.css";
import Vue from 'vue'; import Vue from "vue";
import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue'; import {BootstrapVue, BootstrapVueIcons} from "bootstrap-vue";
import InstantSearch from 'vue-instantsearch'; import InstantSearch from "vue-instantsearch";
import VueScrollTo from 'vue-scrollto'; import VueClipboard from 'vue-clipboard2'
import App from './App.vue'; import VueScrollTo from "vue-scrollto";
import router from './router'; import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.use(VueScrollTo); Vue.use(VueScrollTo);
Vue.use(BootstrapVue); Vue.use(BootstrapVue);
Vue.use(BootstrapVueIcons); Vue.use(BootstrapVueIcons);
Vue.use(InstantSearch); Vue.use(InstantSearch);
Vue.use(VueClipboard)
Vue.config.productionTip = false; Vue.config.productionTip = false;
router.beforeEach((to, from, next) => {
// eslint-disable-next-line no-constant-condition
if (from.name !== null && to.name === "Character" && false) next(false);
else next();
});
new Vue({ new Vue({
router, router,
render: (h) => h(App), store,
}).$mount('#app'); render: (h) => h(App),
}).$mount("#app");

12
client/src/store.js Normal file
View File

@@ -0,0 +1,12 @@
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
episodeCount: [6, 22, 23, 14, 26, 24, 24, 24, 23]
},
mutations: {},
actions: {},
});