From f3f0ea6c1d9087429d8f066f1c51db646839b52c Mon Sep 17 00:00:00 2001
From: Henrik Askjer <henrik.askjer@uib.no>
Date: Tue, 16 Nov 2021 13:33:36 +0100
Subject: [PATCH] lazy loading of articles

---
 src/components/DictionaryView.vue | 147 ++++++++++++++++++------------
 src/components/SearchResults.vue  |  27 +++---
 2 files changed, 101 insertions(+), 73 deletions(-)

diff --git a/src/components/DictionaryView.vue b/src/components/DictionaryView.vue
index e871e9ee..8093fec9 100644
--- a/src/components/DictionaryView.vue
+++ b/src/components/DictionaryView.vue
@@ -36,22 +36,23 @@
     <div id="spinner" v-if="waiting">
       <v-progress-circular indeterminate color="secondary" size="120"></v-progress-circular>
     </div>
-    <SearchResults :hits="search_results"
+    <SearchResults  :results_bm="search_results.bm"
+                    :results_nn="search_results.nn"
                     :lang="lang"
                     :key="lang"
                     :meta="this.article_info.meta"
                     @article-click="article_link_click"
                     @details-click="details_click"
-                    v-if="search_results.length && ! waiting && ! article" />
+                    v-if="total_results() && ! waiting && ! article" />
     <div id="single_article_container" v-if="article">
-        <div class="return_to_results" v-if="search_results && search_results.length">
+        <div class="return_to_results" v-if="total_results">
           <router-link :to="article.source" @click.native="return_to_results()">
             <v-icon class="nav_arrow">arrow_left</v-icon>Tilbake til {{article.dictionary == 'bm' ? 'søkeresultater' : 'søkjeresultat'}}
           </router-link>
         </div>
       <Article :key="article_key" :article="article" @article-click="article_link_click" />
     </div>
-    <div class="welcome" v-if="! (article || error || search_results.length || waiting)">
+    <div class="welcome" v-if="! (article || error || total_results() || waiting)">
       <div class="monthly" :class="$vuetify.breakpoint.name">
         <div>
           <Article :article="monthly_bm" @article-click="article_link_click" />
@@ -106,7 +107,7 @@ function navigate_to_article(self, source) {
   axios.get(ARTICLE_ENDPOINT + lang + '/article/' + self.$route.params.id + ".json")
   .then(function(response){
     self.article = Object.assign(response.data, {'dictionary': lang, results: self.search_results})
-    self.search_results = []
+    self.search_results = {}
     self.error = null
   })
   .catch(function(error){
@@ -120,7 +121,7 @@ function navigate_to_article(self, source) {
   })
   .then(function(response){
     self.waiting_for_articles = false
-    history.replaceState({article: self.article, search_results: [], lang: self.lang, error: self.error, article_info: self.article_info}, '')
+    history.replaceState({article: self.article, search_results: {}, lang: self.lang, error: self.error, article_info: self.article_info}, '')
     if (source) {
       self.$plausible.trackEvent('internal link incoming', {props: {origin: source}})
     }
@@ -155,9 +156,62 @@ function navigate_to_search(self, query) {
 }
 */
 
-function load_articles(self, response, query, offset, n, dict) {
-  
+function load_articles(self, query, offset, n, dict) {
+
+  let article_IDs = self.article_info.articles[dict]
+  if (offset > article_IDs.length) {
+    n = 0
+  }
+  else if (offset + n > article_IDs.length) {
+    n = n + (article_IDs.length - n)
+  }
 
+  if (n > 0) {
+    article_IDs = article_IDs.slice(offset, n)
+    console.log("ARTICLES", dict,article_IDs)
+    return Promise.all(article_IDs.map((article_id) => {
+      return axios.get(`${ARTICLE_ENDPOINT}${dict}/article/${article_id}.json`)
+
+    }))
+    .then((response) => {
+      let results = response.map((element, index) => {
+        return Object.assign(element.data, {
+          dictionary: dict
+        })
+      })
+      self.article = null
+      if (! results.length) {
+        if (query.match) {
+          self.error = []
+          if (self.lang !== 'bm') {
+            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 !== 'nn') self.error.push(`Ordet «${query.match}» finnes ikke i Bokmålsordboka. Bruk knappen «nynorsk (nn)» om du har søkt i feil ordbok.`)
+        } else {
+          self.error = [`Søk på «${query.q}» gir ingen treff i ${dicts[self.lang]}.`]
+
+          // 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 == "bm") self.error[0] += ` Bruk knappen «nynorsk (nn)» om du har søkt i feil ordbok.`
+          if (self.lang == "nn") self.error[0] += ` Bruk knappen «bokmål (bm)» om du har søkt i feil ordbok.`
+        }
+      }
+      else {
+        self.error = null
+      }
+      if (offset == 0) {
+        self.search_results[dict] = results
+      }
+      else {
+      self.search_results[dict] = self.search_results[dict].concat(results)
+      }
+    })
+    .catch(error => {
+      self.waiting_for_articles = false
+      self.connection_error(error)
+    })
+  }
 }
 
 
@@ -168,62 +222,21 @@ function navigate_to_query(self, word) {
   // Get article IDs
   api.get('articles?', {params: {w: query.match || query.q, dict: self.lang, scope: "w"}}).then((response) => {
             self.article_info = response.data
-            let article_ids = response.data["articles"]
-            let unwrapped = []
-            for (const d in article_ids) {
-                article_ids[d].forEach(i => unwrapped.push({
-                dictionary: d,
-                id: i
-              }))
-            }
-          
-          // Get individual articles
-          Promise.all(unwrapped.map((article) => {
-              return axios.get(`${ARTICLE_ENDPOINT}${article.dictionary}/article/${article.id}.json`)
-
-            }))
-            .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) {
-                if (query.match) {
-                  self.error = []
-                  if (self.lang !== 'bm') {
-                    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 !== 'nn') self.error.push(`Ordet «${query.match}» finnes ikke i Bokmålsordboka. Bruk knappen «nynorsk (nn)» om du har søkt i feil ordbok.`)
-                } else {
-                  self.error = [`Søk på «${query.q}» gir ingen treff i ${dicts[self.lang]}.`]
-
-                  // 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 == "bm") self.error[0] += ` Bruk knappen «nynorsk (nn)» om du har søkt i feil ordbok.`
-                  if (self.lang == "nn") self.error[0] += ` Bruk knappen «bokmål (bm)» om du har søkt i feil ordbok.`
-                }
-
-              }
-              else {
-                self.error = null
-              }
-            })
-            .catch(error => {
-              self.connection_error(error)
-            })
+            self.search_results = {}
+            Promise.all([load_articles(self, query, 0, 10, "bm"),
+                        load_articles(self, query, 0, 10, "nn")])
             .then(() => {
               self.waiting_for_articles = false
               history.replaceState({
                 article: self.article,
-                search_results: self.search_results,
                 lang: self.lang,
                 error: self.error,
-                article_info: self.article_info
-              }, '')
+                article_info: self.article_info,
+                search_results: self.search_results
+                }, '')
+              
             })
+          
         }).catch(error =>{
           self.connection_error(error)
 
@@ -262,8 +275,24 @@ export default {
     SearchResults
   },
   methods: {
+    total_results: function() {
+      
+      if (this.search_results) {
+              let total = 0
+      if (this.search_results.bm) {
+        return total += this.search_results.bm.length
+      }
+      if (this.search_results.nn) {
+        return this.search_results.nn.length
+      }
+      console.log("TOTAL", total)
+      return total
+
+      }
+
+    },
     connection_error: function(error) {
-      this.search_results = []
+      this.search_results = {}
       if (error.response) {
         this.error = []
         if (this.lang !== 'bm') this.error.push(`Noko gjekk gale på serversida`)
@@ -316,7 +345,7 @@ export default {
     details_click: function(item) {
       item.article.source = this.previous
       this.article = item.article
-      history.replaceState({article: this.article, search_results: [], lang: this.lang, error: null, article_info: this.article_info}, '')
+      history.replaceState({article: this.article, search_results: {}, lang: this.lang, error: null, article_info: this.article_info}, '')
     },
     return_to_results: function() {
       this.article = null
diff --git a/src/components/SearchResults.vue b/src/components/SearchResults.vue
index a3330519..f560eed8 100644
--- a/src/components/SearchResults.vue
+++ b/src/components/SearchResults.vue
@@ -1,9 +1,9 @@
 <template>
   <section id="search_results">
     <div class="flex-container" :class="$vuetify.breakpoint.name">
-      <ul class="hits" v-if="results_bm.length">
+      <ul class="hits" :v-if="results_bm">
       <li class="results-count">Treff i Bokmålsordboka: {{count_bm}}</li>
-        <li  class="article_container" v-for="(result, index) in results_bm" :key="index + results_hash" tabindex="-1">
+        <li  class="article_container" v-for="(result, index) in results_bm" :key="index + bm_hash" tabindex="-1">
           <Article
               :article="result"
               @article-click="article_link_click"
@@ -11,9 +11,9 @@
           </Article>
         </li>
       </ul>
-      <ul class="hits" v-if="results_nn.length">
+      <ul class="hits" :v-if="results_nn">
         <li class="results-count">Treff i Nynorskorboka: {{count_nn}}</li>
-        <li class="article_container" v-for="(result, index) in results_nn" :key="index + results_hash" tabindex="-1">
+        <li class="article_container" v-for="(result, index) in results_nn" :key="index + nn_hash" tabindex="-1">
           <Article
               :article="result"
               @article-click="article_link_click"
@@ -28,31 +28,30 @@
 
 <script>
 import Article from './Article.vue'
-
 export default {
   name: 'SearchResults',
   props: {
-    hits: Array,
+    results_bm: Array,
+    results_nn: Array,
     lang: String,
     meta: Object
   },
   computed: {
     right_col_class_name: function() {
       if (this.$vuetify.breakpoint.mdAndUp) {
-        if (this.results_bm.length) {
+        if (this.results_bm) {
           return 'righ_hand_column'
         }
       }
       return ''
     },
-    results_bm: function(){
-      return this.hits.filter(hit => hit.dictionary == 'bm')
-    },
-    results_nn: function(){
-      return this.hits.filter(hit => hit.dictionary == 'nn')
+    bm_hash: function(){
+      console.log("HASH_B;")
+      return this.results_bm.reduce((hash, hit) => (hash + hit.article_id) % 10000, 0)
     },
-    results_hash: function(){
-      return this.hits.reduce((hash, hit) => (hash + hit.article_id) % 10000, 0)
+    nn_hash: function(){
+      console.log("HASH_NN")
+      return this.results_nn.reduce((hash, hit) => (hash + hit.article_id) % 10000, 0)
     },
     count_bm: function(){
       console.log(this.meta)
-- 
GitLab