mrgodhani/raven-reader

View on GitHub
src/services/fever.js

Summary

Maintainability
F
3 days
Test Coverage
import db from './db.js'
import uuidstring from 'uuid-by-string'
import * as database from '../db'
import truncate from './truncate'

function getCoverImage (postContent) {
  const dom = new DOMParser()
  const doc = dom.parseFromString(postContent, 'text/html')
  const image = doc.querySelector('img')
  if (image !== null && typeof image.getAttribute('src') !== 'undefined' && image.getAttribute('src').startsWith('https://')) {
    return doc.querySelector('img').getAttribute('src')
  }
  return null
}

export default {
  async getSubscriptions (credsData) {
    try {
      const subscriptions = await window.fever.post(`${credsData.endpoint}?api&feeds`, credsData.hash)
      return subscriptions.feeds
    } catch (e) {
      window.log.info(e)
    }
  },
  async getUnreadIds (credsData) {
    try {
      const unread = await window.fever.post(`${credsData.endpoint}?api&unread_item_ids`, credsData.hash)
      return unread.unread_item_ids
    } catch (e) {
      window.log.info(e)
    }
  },
  async getStarredIds (credsData) {
    try {
      const starred = await window.fever.post(`${credsData.endpoint}?api&saved_item_ids`, credsData.hash)
      return starred.saved_item_ids
    } catch (e) {
      window.log.info(e)
    }
  },
  async getEntries (credsData, ids) {
    const perChunk = 50 // items per chunk
    const entries = []
    const chunks = ids.reduce((resultArray, item, index) => {
      const chunkIndex = Math.floor(index / perChunk)

      if (!resultArray[chunkIndex]) {
        resultArray[chunkIndex] = [] // start a new chunk
      }

      resultArray[chunkIndex].push(item)

      return resultArray
    }, [])
    try {
      for (let i = 0; i < chunks.length; i++) {
        const data = await window.fever.post(`${credsData.endpoint}?api&items&with_ids=${chunks[i].join(',')}&since_id`, credsData.hash)
        entries.push(...data.items)
      }
      return entries
    } catch (e) {
      window.log.info(e)
    }
  },
  async markItem (credsData, type, id) {
    let endpoint
    switch (type) {
      case 'READ':
        endpoint = `${credsData.endpoint}?api&mark=item&as=read&id=${id}`
        break
      case 'UNREAD':
        endpoint = `${credsData.endpoint}?api&mark=item&as=unread&id=${id}`
        break
      case 'FAVOURITE':
        endpoint = `${credsData.endpoint}?api&mark=item&as=saved&id=${id}`
        break
      case 'UNFAVOURITE':
        endpoint = `${credsData.endpoint}?api&mark=item&as=unsaved&id=${id}`
        break
    }
    return await window.fever.post(endpoint, credsData.hash)
  },
  async getGroups (credsData) {
    const groups = await window.fever.post(`${credsData.endpoint}?api&groups`, credsData.hash)
    return {
      groups: groups.groups,
      feed_groups: groups.feeds_groups.map((item) => {
        item.feed_ids = item.feed_ids.split(',').map(item => parseInt(item))
        return item
      })
    }
  },
  async syncTags (categories) {
    const categoryItems = new Set([...categories.map(item => item.title)])
    const currentCategories = await db.fetchCategoriesBySource('fever')
    const dbFormat = Array.from(categoryItems).map((item) => {
      return {
        id: uuidstring(item),
        type: 'category',
        source: 'fever',
        title: item
      }
    })
    const diff = currentCategories.filter(item => !categoryItems.has(item.title))
    if (diff.length > 0) {
      db.deleteCategoryMulti(diff.map(item => item.title))
    }
    const tobeAdded = dbFormat.filter(x => !new Set(currentCategories.map(item => item.title)).has(x.title))
    if (dbFormat.length > 0) {
      db.addCategory(tobeAdded.map(item => database.categoryTable.createRow(item)))
    }
  },
  async syncItems (credsData) {
    let subscriptions = await this.getSubscriptions(credsData)
    const unreadList = await this.getUnreadIds(credsData)
    const starredList = await this.getStarredIds(credsData)
    const entriesId = new Set([...unreadList.split(','), ...starredList.split(',')])
    const entries = await this.getEntries(credsData, Array.from(entriesId))
    const groupsData = await this.getGroups(credsData)
    const groups = groupsData.groups
    const feedGroups = groupsData.feed_groups
    this.syncTags(groups)
    if (subscriptions) {
      const currentSubscriptions = await db.fetchServicesFeeds('greader')
      const currentFeedUrls = JSON.parse(JSON.stringify(currentSubscriptions)).map((item) => {
        return item.xmlurl
      })
      const greaderSubscriptions = new Set(subscriptions.map((item) => {
        return item.url
      }))
      const diff = currentFeedUrls.filter(item => !greaderSubscriptions.has(item))
      if (diff.length > 0) {
        const deleteFeed = currentSubscriptions.filter((x) => diff.includes(x.xmlurl))
        deleteFeed.forEach(async (item) => {
          await db.deleteArticles(item.uuid)
          await db.deleteFeed(item.uuid)
        })
      }
      const transformedSubscriptions = subscriptions.map((item) => {
        const getGroup = feedGroups.filter((feedGroup) => {
          return feedGroup.feed_ids.includes(item.id)
        })
        const category = groups.filter((group) => {
          if (getGroup.length > 0) {
            return group.id === getGroup[0].group_id
          }
          return false
        })
        return {
          id: uuidstring(item.url),
          uuid: uuidstring(item.url),
          link: item.site_url,
          xmlurl: item.url,
          title: item.title,
          favicon: `https://www.google.com/s2/favicons?domain=${item.site_url}`,
          description: null,
          category: category.length > 0 ? category[0].title : null,
          source: 'fever',
          source_id: item.id
        }
      })
      const addedFeeds = db.addFeed(transformedSubscriptions.map(item => database.feedTable.createRow(item)))
      return addedFeeds.then((res) => {
        const subscriptAdded = res
        subscriptions = subscriptions.map((item) => {
          const addedSubscription = subscriptAdded.filter(feed => feed.source_id === item.id)
          db.updateArticleCategoryFeed(addedSubscription[0].uuid, addedSubscription[0].category)
          item.feed_uuid = addedSubscription[0].uuid
          item.source_id = item.id
          item.category = addedSubscription[0].category
          return item
        })
        const transformedEntries = JSON.parse(JSON.stringify(entries)).map((item) => {
          const feed = subscriptions.filter(feed => feed.source_id === item.feed_id)[0]
          return {
            id: uuidstring(item.url),
            uuid: uuidstring(item.url),
            title: item.title,
            author: item.author,
            link: item.url,
            cover: getCoverImage(item.html),
            content: item.html,
            contentSnippet: truncate(item.html.replace(/(<([^>]+)>)/gi, ''), 100),
            favourite: Boolean(item.is_saved),
            read: Boolean(item.is_read),
            keep_read: null,
            pubDate: null,
            offline: false,
            media: null,
            podcast: false,
            itunes: null,
            played: false,
            publishUnix: item.created_on_time,
            feed_uuid: feed.feed_uuid,
            category: feed.category,
            source: 'fever',
            source_id: item.id
          }
        })
        const currentArticles = db.fetchServicesArticles('fever')
        const articles = db.addArticles(transformedEntries.map(item => database.articleTable.createRow(item)))
        currentArticles.then((res) => {
          const markRead = res.filter(item => !unreadList.split(',').includes(item.articles.source_id.toString())).map(item => item.articles.uuid)
          const markUnFavourite = res.filter(item => !starredList.split(',').includes(item.articles.source_id.toString())).map(item => item.articles.uuid)
          const markFavourite = res.filter(item => starredList.split(',').includes(item.articles.source_id.toString())).map(item => item.articles.uuid)
          db.markAllRead(markRead)
          db.markBulkFavourite(markFavourite)
          db.markBulkUnFavourite(markUnFavourite)
        })
        return articles
      })
    }
  }
}