Skip to content
Snippets Groups Projects
DictionaryView.vue 14.9 KiB
Newer Older
Ole Voldsæter's avatar
Ole Voldsæter committed
<template>
Ole Voldsæter's avatar
Ole Voldsæter committed
    <div class="search_container">
      <div class="lang_select_container">
        <v-radio-group row v-model="lang" @change="update_lang_form">
          <template v-slot:label tabindex="1">
            <span aria-label="Vis resultat i begge ordbøkene eller bruk radioknapp for bokmålsordboka eller radioknapp for nynorskordboka">VIS</span>
Ole Voldsæter's avatar
Ole Voldsæter committed
          </template>
          <v-radio value="bob,nob" color="primary">
Ole Voldsæter's avatar
Ole Voldsæter committed
            <template v-slot:label>
                begge{{$vuetify.breakpoint.smAndDown ? '' : ' ordbøkene'}}
Ole Voldsæter's avatar
Ole Voldsæter committed
            </template>
          </v-radio>
          <v-radio value="bob" color="primary">
Ole Voldsæter's avatar
Ole Voldsæter committed
            <template v-slot:label>
                {{$vuetify.breakpoint.xs ? 'bm' :  'bokmål (bm)'}}
Ole Voldsæter's avatar
Ole Voldsæter committed
            </template>
          </v-radio>
          <v-radio value="nob" color="primary">
Ole Voldsæter's avatar
Ole Voldsæter committed
            <template v-slot:label>
                {{$vuetify.breakpoint.xs ? 'nn' :  'nynorsk (nn)'}}
Ole Voldsæter's avatar
Ole Voldsæter committed
            </template>
          </v-radio>
        </v-radio-group>
      </div>
      <Autocomplete v-on:submit="select_result" :api="get_search_endpoint">
Ole Voldsæter's avatar
Ole Voldsæter committed
      </Autocomplete>
    </div>
Ole Voldsæter's avatar
Ole Voldsæter committed
    <div id="spinner" v-if="waiting">
      <v-progress-circular indeterminate color="secondary" size="120"></v-progress-circular>
Ole Voldsæter's avatar
Ole Voldsæter committed
    </div>
    <SearchResults :hits="search_results"
                    :lang="lang"
                    @article-click="article_link_click"
                    @details-click="details_click"
                    v-if="search_results.length && ! waiting && ! article" />
Ole Voldsæter's avatar
Ole Voldsæter committed
    <div id="single_article_container" v-if="article">
        <div class="return_to_results" v-if="search_results && search_results.length">
          <router-link :to="article.source" @click.native="return_to_results()">
            <v-icon class="nav_arrow">arrow_left</v-icon>Tilbake til {{article.dictionary == 'bob' ? 'søkeresultater' : 'søkjeresultat'}}
Ole Voldsæter's avatar
Ole Voldsæter committed
      <Article :key="article_key" :article="article" @article-click="article_link_click" />
    </div>
Ole Voldsæter's avatar
Ole Voldsæter committed
    <div class="welcome" v-if="! (article || error || search_results.length || waiting)">
      <div class="monthly" :class="$vuetify.breakpoint.name">
Ole Voldsæter's avatar
Ole Voldsæter committed
        <div>
          <Article :article="monthly_bm" @article-click="article_link_click" />
        </div>
        <div>
          <Article :article="monthly_nn" @article-click="article_link_click" />
        </div>
      </div>
    </div>
Ole Voldsæter's avatar
Ole Voldsæter committed
    <div class="error" v-if="error">
      <h1>Ingen treff</h1>
      <p v-if="error[0]">{{error[0]}}</p>
      <p v-if="error[1]">{{error[1]}}</p>
Ole Voldsæter's avatar
Ole Voldsæter committed
    </div>
Ole Voldsæter's avatar
Ole Voldsæter committed
  </main>
</template>

<script>
import axios from "axios"
import entities from '../utils/entities.js'
import Article from './Article.vue'
import SearchResults from './SearchResults.vue'
import Autocomplete from './Autocomplete.vue'

Henrik Askjer's avatar
Henrik Askjer committed
import { setup } from 'axios-cache-adapter'

const SEARCH_ENDPOINT = process.env.VUE_APP_SEARCH_ENDPOINT
const ARTICLE_ENDPOINT= process.env.VUE_APP_ARTICLE_ENDPOINT
const API_STAGE = process.env.VUE_APP_API_STAGE

const dicts = {'nob': 'Nynorskorboka',
               'bob': 'Bokmålsordboka',
               'bob,nob': 'ordbøkene'}

const api = setup({
  baseURL: SEARCH_ENDPOINT,
Henrik Askjer's avatar
Henrik Askjer committed
  cache: {
    maxAge: 15 * 60 * 1000,
    exclude: {
Henrik Askjer's avatar
Henrik Askjer committed
      query: false,
      paths: ["articles"] // Disable caching for articles
Henrik Askjer's avatar
Henrik Askjer committed

Ole Voldsæter's avatar
Ole Voldsæter committed
function navigate_to_article(self, source) {
Henrik Askjer's avatar
Henrik Askjer committed
  self.article = null
  self.waiting_for_articles = true
Henrik Askjer's avatar
Henrik Askjer committed
  /*
  if ((self.$route.query.nocache || '').toLowerCase() == 'true') {
    config.headers.cachebuster = Date.now()
Henrik Askjer's avatar
Henrik Askjer committed
  }*/
  axios.get(ARTICLE_ENDPOINT + self.$route.params.lang + '/article/' + self.$route.params.id + ".json")
Ole Voldsæter's avatar
Ole Voldsæter committed
  .then(function(response){
    self.article = Object.assign(response.data, {'dictionary': self.$route.params.lang, results: self.search_results})
Ole Voldsæter's avatar
Ole Voldsæter committed
    self.search_results = []
Ole Voldsæter's avatar
Ole Voldsæter committed
    self.error = null
Ole Voldsæter's avatar
Ole Voldsæter committed
  })
  .catch(function(error){
    if (error.response && error.response.status == 404) {
      self.error = ["Vi har ingen artikkel med id " + self.$route.params.id]
Ole Voldsæter's avatar
Ole Voldsæter committed
    } else {
      self.error = []
Henrik Askjer's avatar
Henrik Askjer committed
      if (self.lang !== 'bob') self.error.push(`Noko gjekk gale...`)
      if (self.lang !== 'nob') self.error.push(`Noe gikk galt...`)
Ole Voldsæter's avatar
Ole Voldsæter committed
    }
  })
  .then(function(response){
    self.waiting_for_articles = false
Ole Voldsæter's avatar
Ole Voldsæter committed
    history.replaceState({article: self.article, search_results: [], lang: self.lang, error: self.error}, '')
Ole Voldsæter's avatar
Ole Voldsæter committed
    if (source) {
      self.$plausible.trackEvent('internal link incoming', {props: {origin: source}})
    }
  })
}

Ole Voldsæter's avatar
Ole Voldsæter committed
function navigate_to_search(self, query) {
  axios.get(self.api_pref + 'search?q=' + query, { headers: {"x-api-key":"ZkYiyRVXxH86ijsvhx3cH4SY5Iik2ijI3BKVJGMm"}}) // API-key to be removed or reimplemented with a ci-variable with the new API
Ole Voldsæter's avatar
Ole Voldsæter committed
  .then(function(response){
    self.search_results = response.data
    if (! self.search_results.length) {
Henrik Askjer's avatar
Henrik Askjer committed
      self.error = `Søk på «${query}» gir ingen treff i ${dicts[self.lang]}. Søk med * inne i ordet dersom du er usikker på skrivemåten. Bruk knappen «begge ordbøkene» om du har søkt i feil ordbok.`
Ole Voldsæter's avatar
Ole Voldsæter committed
    } else {
      self.error = null
Ole Voldsæter's avatar
Ole Voldsæter committed
    }
  })
  .catch(function(error){
Henrik Askjer's avatar
Henrik Askjer committed
    self.articles = null
Ole Voldsæter's avatar
Ole Voldsæter committed
    if (error.response && error.response.status == 400) {
Ole Voldsæter's avatar
Ole Voldsæter committed
      self.error = "Søkeuttrykket inneholder feil"
Ole Voldsæter's avatar
Ole Voldsæter committed
    } else if (error.response) {
Ole Voldsæter's avatar
Ole Voldsæter committed
      self.error = "Noe gikk galt på serversiden"
Ole Voldsæter's avatar
Ole Voldsæter committed
    } else {
Ole Voldsæter's avatar
Ole Voldsæter committed
      self.error = "Nettverksproblemer, prøv igjen"
Ole Voldsæter's avatar
Ole Voldsæter committed
    }
  })
  .then(function(_){
    self.waiting_for_articles = false
Ole Voldsæter's avatar
Ole Voldsæter committed
    history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang, error: self.error}, '')
function navigate_to_query(self, word) {
Henrik Askjer's avatar
Henrik Askjer committed
  self.waiting_for_articles = true
  let  query = self.event ? self.event : {q: word} 
  api.get('articles?', {params: {w: query.match || query.q, dict: self.lang, scope: "w", stage: self.stage}}).then((response) => {
            let article_ids = response.data
            for (const d in article_ids) {
                article_ids[d].forEach(i => unwrapped.push({
Henrik Askjer's avatar
Henrik Askjer committed
                dictionary: d,
                id: i
              }))
            }
          
          // Get individual articles
Henrik Askjer's avatar
Henrik Askjer committed
          Promise.all(unwrapped.map((article) => {
              return axios.get(`${ARTICLE_ENDPOINT}${article.dictionary}/article/${article.id}.json`)
Henrik Askjer's avatar
Henrik Askjer committed

            }))
            .then((response) => {
              self.search_results = response.map((element, index) => {
                return Object.assign(element.data, {
                  dictionary: unwrapped[index].dictionary
                })
              })
              self.article = null
              if (! self.search_results.length) {
                  self.error = []
                  if (self.lang !== 'bob') {
                    self.error.push(`Ordet «${query.match}» finst ikkje i Nynorskordboka. Bruk knappen «bokmål (bm)» om du har søkt i feil ordbok.`)
                  }
                  if (self.lang !== 'nob') self.error.push(`Ordet «${query.match}» finnes ikke i Bokmålsordboka. Bruk knappen «nynorsk (nn)» om du har søkt i feil ordbok.`)
                  self.error = [`Søk på «${query.q}» gir ingen treff i ${dicts[self.lang]}.`]
Henrik Askjer's avatar
Henrik Askjer committed

                  // If not advanced search
                  if (query.search != 2) self.error[0] += " Søk med * eller % dersom du er usikker på skrivemåten. Søketips kan du finne i «OM ORDBØKENE»."

                  if (self.lang == "bob") self.error[0] += ` Bruk knappen «nynorsk (nn)» om du har søkt i feil ordbok.`
                  if (self.lang == "nob") self.error[0] += ` Bruk knappen «bokmål (nn)» om du har søkt i feil ordbok.`
Henrik Askjer's avatar
Henrik Askjer committed
            })
            .catch(error => {
              self.connection_error(error)
Henrik Askjer's avatar
Henrik Askjer committed
            })
            .then(() => {
              self.waiting_for_articles = false
              history.replaceState({
                article: self.article,
                search_results: self.search_results,
                lang: self.lang,
                error: self.error
              }, '')
            })
        }).catch(error =>{
          self.connection_error(error)

        })
}

export default {
  name: 'DictionaryView',
  data: function() {
    return {
      article_key: 0,
      search_results: [],
      lang: 'bob,nob',
      waiting_for_articles: true,
      waiting_for_metadata: true,
      article: null,
      error: null,
      monthly_bm: null,
      monthly_nn: null,
      event: null,
      stage: API_STAGE
    }
  },
  computed: {
    waiting: function() {
      return (this.waiting_for_articles || this.waiting_for_metadata) && this.$route.name != 'root'
    },
    get_search_endpoint: function() {
      return api
    },
  },
  components: {
    Article,
    Autocomplete,
    SearchResults
  },
  methods: {
    connection_error: function(error) {
      this.search_results = []
      if (error.response) {
        this.error = []
        if (this.lang !== 'bob') this.error.push(`Noko gjekk gale på serversida`)
        if (this.lang !== 'nob') this.error.push(`Noe gikk galt på serversiden"`)
      } else {
        this.error = []
        if (this.lang !== 'bob') this.error.push(`Nettverksproblem, prøv igjen`)
        if (this.lang !== 'nob') this.error.push(`Nettverksproblemer, prøv igjen`)
      }
    },
    select_result: function (event) {
        this.event = event
        let name = event.search ? 'search' : 'w'
        let route = `/${this.lang}/${name}/${event.match || event.q}`
        this.$router.push(route)
Henrik Askjer's avatar
Henrik Askjer committed
        let track_props = {query: event.q}
        if (event.match) track_props.match = event.match
        this.$plausible.trackEvent(event.update_lang ? "language" : 'dropdown selection', {
Henrik Askjer's avatar
Henrik Askjer committed
          props: track_props
Henrik Askjer's avatar
Henrik Askjer committed
      },
      update_lang_form: function (lang) {
        this.lang = lang
Henrik Askjer's avatar
Henrik Askjer committed
        let name = null
        let query = null
        if(this.$route.name == 'word') {
            name = "w"
            query = this.$route.params.word
Henrik Askjer's avatar
Henrik Askjer committed
            this.$router.push(`/${this.lang}/${name}/${query}`)
            navigate_to_query(this, query)
        }
        else if (this.$route.name == 'search') {
           name = "search"
           query = this.$route.params.query
Henrik Askjer's avatar
Henrik Askjer committed
           this.$router.push(`/${this.lang}/${name}/${query}`)
           navigate_to_query(this, query)
Henrik Askjer's avatar
Henrik Askjer committed
      },
Ole Voldsæter's avatar
Ole Voldsæter committed
    article_link_click: function(item) {
Ole Voldsæter's avatar
Ole Voldsæter committed
      if (this.article && this.article.article_id == item.article_id){
Ole Voldsæter's avatar
Ole Voldsæter committed
        this.article_key++
Ole Voldsæter's avatar
Ole Voldsæter committed
        history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang, error: this.error}, '')
Ole Voldsæter's avatar
Ole Voldsæter committed
      }else{
        navigate_to_article(this, item.source)
      }
    },
    details_click: function(item) {
      item.article.source = this.$route.path
      history.replaceState({article: this.article, search_results: [], lang: this.lang, error: null}, '')
    },
    return_to_results: function() {
      this.article = null
Ole Voldsæter's avatar
Ole Voldsæter committed
    }
  },
  mounted: function(){
    let self = this
    this.lang = 'bob,nob'

    Promise.all([
Henrik Askjer's avatar
Henrik Askjer committed
      axios.get(ARTICLE_ENDPOINT + 'bob/concepts.json').then(function(response){
Ole Voldsæter's avatar
Ole Voldsæter committed
        let concepts = response.data.concepts
        entities.bob = concepts
      }),
      axios.get(ARTICLE_ENDPOINT + 'nob/concepts.json').then(function(response){
Ole Voldsæter's avatar
Ole Voldsæter committed
        let concepts = response.data.concepts
        entities.nob = concepts
      })
    ]).then(function(_) {
      self.waiting_for_metadata = false
Henrik Askjer's avatar
Henrik Askjer committed
      if(self.$route.name == 'search') {
Ole Voldsæter's avatar
Ole Voldsæter committed
        self.lang = self.$route.params.lang
        navigate_to_query(self, self.$route.params.query)
      } 
      else if(self.$route.name == 'word') {
        self.lang = self.$route.params.lang
        navigate_to_query(self, self.$route.params.word)
Ole Voldsæter's avatar
Ole Voldsæter committed
      }
      else if(self.$route.name == 'lookup'){
        navigate_to_article(self, self.$route.path)
Ole Voldsæter's avatar
Ole Voldsæter committed
      }
      else {
        self.lang = self.$route.params.lang || 'bob,nob'
        self.waiting_for_articles = false
Ole Voldsæter's avatar
Ole Voldsæter committed
        history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang, error: self.error}, '')
Ole Voldsæter's avatar
Ole Voldsæter committed
      }

      // words of the month
      axios.get(ARTICLE_ENDPOINT + 'bob/article/5607.json').then(function(response){
Ole Voldsæter's avatar
Ole Voldsæter committed
        self.monthly_bm = Object.assign(response.data, {dictionary: 'bob'})
      })

      axios.get(ARTICLE_ENDPOINT + 'nob/article/78569.json').then(function(response){
Ole Voldsæter's avatar
Ole Voldsæter committed
        self.monthly_nn = Object.assign(response.data, {dictionary: 'nob'})
      })
    }).catch(function(_){
      self.error = []
      if (self.lang !== 'bob') self.error.push(`Eit nettverksproblem hindra lasting av sida. Prøv å laste sida på nytt`)
      if (self.lang !== 'nob') self.error.push(`Et nettverksproblem hindret lasting av siden. Prøv å laste siden på nytt`)
Ole Voldsæter's avatar
Ole Voldsæter committed
      self.waiting_for_metadata = false
      self.waiting_for_articles = false
    })
  },
  created: function() {
    let self = this
    window.onpopstate = function (event) {
      if (event.state) {
        self.article = event.state.article
        self.search_results = event.state.search_results
        self.lang = event.state.lang
Ole Voldsæter's avatar
Ole Voldsæter committed
        self.error = event.state.error
Ole Voldsæter's avatar
Ole Voldsæter committed
      }
    }

  }
}
</script>

<style>
main {
  flex: 1 0 auto;
  background-color: var(--v-tertiary-base);
  display: flex;
  flex-direction: column;
Ole Voldsæter's avatar
Ole Voldsæter committed
  background-image: url('../assets/books.jpg');
  background-repeat: no-repeat;
}

div.welcome article {
  border-style: none;
}

.search_container {
  background-color: var(--v-tertiary-base);
  padding-top: 1px;
  padding-bottom: 10px;
}

Henrik Askjer's avatar
Henrik Askjer committed
#spinner {
  margin: auto;
}

#search_results, #spinner, #single_article_container, div.welcome, div.search_container, .error {
  padding-left: calc((100vw - 1200px) / 2);
  padding-right: calc((100vw - 1200px) / 2);
.error > p {
  margin-left: 15px;
}

Ole Voldsæter's avatar
Ole Voldsæter committed
#spinner {
  padding-top: 40px;
}

div.monthly {
  display: flex;
  width: 100%;
}

div.monthly > div {
  flex: 50%;
}

div.monthly.sm, div.monthly.xs {
Ole Voldsæter's avatar
Ole Voldsæter committed
div.monthly article.bob .dict-label::before {
Ole Voldsæter's avatar
Ole Voldsæter committed
}

div.monthly article.nob .dict-label::before {
div.monthly details, div.monthly h3 {
  display: none;
}

Ole Voldsæter's avatar
Ole Voldsæter committed
.v-label span {
  color: var(--v-primary-base);
}

.lang_select_container {
Ole Voldsæter's avatar
Ole Voldsæter committed
li.suggestion {
  font-weight: bold;
  padding-left: 20px;
  padding-top: 5px;
  padding-bottom: 5px;
  border: 0px;
  background-image: none;
}

::selection {
  background: var(--v-secondary-base);
  color: white;
}

Ole Voldsæter's avatar
Ole Voldsæter committed
.return_to_results {
  display: table-cell;
}

.return_to_results a {
  color: var(--v-primary-base) !important;
}

Ole Voldsæter's avatar
Ole Voldsæter committed
.nav_arrow {
  vertical-align: top !important;
  color: var(--v-primary-base) !important;
}

Ole Voldsæter's avatar
Ole Voldsæter committed
</style>