Poll periodically for new posts

zio/stable
Paul Frazee 2022-11-17 12:33:19 -06:00
parent f6e591339d
commit eae5ac839c
5 changed files with 89 additions and 3 deletions

View File

@ -31,6 +31,7 @@
"react-circular-progressbar": "^2.1.0",
"react-dom": "17.0.2",
"react-native": "0.68.2",
"react-native-appstate-hook": "^1.0.6",
"react-native-gesture-handler": "^2.5.0",
"react-native-inappbrowser-reborn": "^3.6.3",
"react-native-linear-gradient": "^2.6.2",

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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,
},
})

View File

@ -10148,6 +10148,11 @@ react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-native-appstate-hook@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/react-native-appstate-hook/-/react-native-appstate-hook-1.0.6.tgz#cbc16e7b89cfaea034cabd999f00e99053cabd06"
integrity sha512-0hPVyf5yLxCSVrrNEuGqN1ZnSSj3Ye2gZex0NtcK/AHYwMc0rXWFNZjBKOoZSouspqu3hXBbQ6NOUSTzrME1AQ==
react-native-codegen@^0.0.17:
version "0.0.17"
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.17.tgz#83fb814d94061cbd46667f510d2ddba35ffb50ac"