about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2022-11-17 12:33:19 -0600
committerPaul Frazee <pfrazee@gmail.com>2022-11-17 12:33:19 -0600
commiteae5ac839c91d79fb90e7641039f7ea7656a2eac (patch)
treeb25fa8c9ba31bb1b54a132ae865d9bc82e19b813 /src
parentf6e591339dde2a0c2ef623964119be50524786e0 (diff)
downloadvoidsky-eae5ac839c91d79fb90e7641039f7ea7656a2eac.tar.zst
Poll periodically for new posts
Diffstat (limited to 'src')
-rw-r--r--src/state/models/feed-view.ts28
-rw-r--r--src/view/index.ts2
-rw-r--r--src/view/screens/Home.tsx56
3 files changed, 83 insertions, 3 deletions
diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts
index 436b47038..a746993cf 100644
--- a/src/state/models/feed-view.ts
+++ b/src/state/models/feed-view.ts
@@ -149,6 +149,7 @@ export class FeedModel {
   // state
   isLoading = false
   isRefreshing = false
+  hasNewLatest = false
   hasLoaded = false
   error = ''
   params: GetTimeline.QueryParams | GetAuthorFeed.QueryParams
@@ -195,6 +196,10 @@ export class FeedModel {
     return this.hasLoaded && !this.hasContent
   }
 
+  setHasNewLatest(v: boolean) {
+    this.hasNewLatest = v
+  }
+
   // public api
   // =
 
@@ -209,6 +214,7 @@ export class FeedModel {
       return this._loadPromise
     }
     await this._pendingWork()
+    this.setHasNewLatest(false)
     this._loadPromise = this._initialLoad(isRefreshing)
     await this._loadPromise
     this._loadPromise = undefined
@@ -242,6 +248,7 @@ export class FeedModel {
       return this._loadLatestPromise
     }
     await this._pendingWork()
+    this.setHasNewLatest(false)
     this._loadLatestPromise = this._loadLatest()
     await this._loadLatestPromise
     this._loadLatestPromise = undefined
@@ -260,6 +267,21 @@ export class FeedModel {
     this._updatePromise = undefined
   }
 
+  /**
+   * Check if new postrs are available
+   */
+  async checkForLatest() {
+    if (this.hasNewLatest) {
+      return
+    }
+    await this._pendingWork()
+    const res = await this._getFeed({limit: 1})
+    this.setHasNewLatest(
+      res.data.feed[0] &&
+        (this.feed.length === 0 || res.data.feed[0].uri !== this.feed[0]?.uri),
+    )
+  }
+
   // state transitions
   // =
 
@@ -380,10 +402,14 @@ export class FeedModel {
 
   private _prependAll(res: GetTimeline.Response | GetAuthorFeed.Response) {
     let counter = this.feed.length
+    const toPrepend = []
     for (const item of res.data.feed) {
       if (this.feed.find(item2 => item2.uri === item.uri)) {
-        return // stop here - we've hit a post we already ahve
+        return // stop here - we've hit a post we already have
       }
+      toPrepend.unshift(item) // reverse the order
+    }
+    for (const item of toPrepend) {
       this._prepend(counter++, item)
     }
   }
diff --git a/src/view/index.ts b/src/view/index.ts
index 3b9a92ebd..e38e1debf 100644
--- a/src/view/index.ts
+++ b/src/view/index.ts
@@ -6,6 +6,7 @@ import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft'
 import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight'
 import {faArrowLeft} from '@fortawesome/free-solid-svg-icons/faArrowLeft'
 import {faArrowRight} from '@fortawesome/free-solid-svg-icons/faArrowRight'
+import {faArrowUp} from '@fortawesome/free-solid-svg-icons/faArrowUp'
 import {faArrowRightFromBracket} from '@fortawesome/free-solid-svg-icons'
 import {faArrowUpFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowUpFromBracket'
 import {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare'
@@ -64,6 +65,7 @@ export function setup() {
     faAngleRight,
     faArrowLeft,
     faArrowRight,
+    faArrowUp,
     faArrowRightFromBracket,
     faArrowUpFromBracket,
     faArrowUpRightFromSquare,
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 04ce2d0cb..7abbfdbd9 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -1,13 +1,15 @@
 import React, {useState, useEffect, useMemo} from 'react'
-import {View} from 'react-native'
+import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
+import useAppState from 'react-native-appstate-hook'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {ViewHeader} from '../com/util/ViewHeader'
 import {Feed} from '../com/posts/Feed'
 import {FAB} from '../com/util/FloatingActionButton'
 import {useStores} from '../../state'
 import {FeedModel} from '../../state/models/feed-view'
 import {ScreenParams} from '../routes'
-import {s} from '../lib/styles'
+import {s, colors} from '../lib/styles'
 
 export const Home = observer(function Home({
   visible,
@@ -15,6 +17,9 @@ export const Home = observer(function Home({
 }: ScreenParams) {
   const store = useStores()
   const [hasSetup, setHasSetup] = useState<boolean>(false)
+  const {appState} = useAppState({
+    onForeground: () => doPoll(true),
+  })
   const defaultFeedView = useMemo<FeedModel>(
     () =>
       new FeedModel(store, 'home', {
@@ -23,9 +28,24 @@ export const Home = observer(function Home({
     [store],
   )
 
+  const doPoll = (knownActive = false) => {
+    if ((!knownActive && appState !== 'active') || !visible) {
+      return
+    }
+    if (defaultFeedView.isLoading) {
+      return
+    }
+    console.log('Polling home feed')
+    defaultFeedView.checkForLatest().catch(e => {
+      console.error('Failed to poll feed', e)
+    })
+  }
+
   useEffect(() => {
     let aborted = false
+    const pollInterval = setInterval(() => doPoll(), 15e3)
     if (!visible) {
+      console.log('hit')
       return
     }
     if (hasSetup) {
@@ -40,6 +60,7 @@ export const Home = observer(function Home({
       })
     }
     return () => {
+      clearInterval(pollInterval)
       aborted = true
     }
   }, [visible, store])
@@ -53,6 +74,10 @@ export const Home = observer(function Home({
   const onPressTryAgain = () => {
     defaultFeedView.refresh()
   }
+  const onPressLoadLatest = () => {
+    defaultFeedView.refresh()
+    scrollElRef?.current?.scrollToOffset({offset: 0})
+  }
 
   return (
     <View style={s.flex1}>
@@ -64,7 +89,34 @@ export const Home = observer(function Home({
         style={{flex: 1}}
         onPressTryAgain={onPressTryAgain}
       />
+      {defaultFeedView.hasNewLatest ? (
+        <TouchableOpacity style={styles.loadLatest} onPress={onPressLoadLatest}>
+          <FontAwesomeIcon icon="arrow-up" style={{color: colors.white}} />
+          <Text style={styles.loadLatestText}>Load new posts</Text>
+        </TouchableOpacity>
+      ) : undefined}
       <FAB icon="pen-nib" onPress={onComposePress} />
     </View>
   )
 })
+
+const styles = StyleSheet.create({
+  loadLatest: {
+    flexDirection: 'row',
+    position: 'absolute',
+    left: 10,
+    bottom: 15,
+    backgroundColor: colors.pink3,
+    paddingHorizontal: 10,
+    paddingVertical: 8,
+    borderRadius: 30,
+    shadowColor: '#000',
+    shadowOpacity: 0.3,
+    shadowOffset: {width: 0, height: 1},
+  },
+  loadLatestText: {
+    color: colors.white,
+    fontWeight: 'bold',
+    marginLeft: 5,
+  },
+})