diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-11-17 12:33:19 -0600 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-11-17 12:33:19 -0600 |
commit | eae5ac839c91d79fb90e7641039f7ea7656a2eac (patch) | |
tree | b25fa8c9ba31bb1b54a132ae865d9bc82e19b813 | |
parent | f6e591339dde2a0c2ef623964119be50524786e0 (diff) | |
download | voidsky-eae5ac839c91d79fb90e7641039f7ea7656a2eac.tar.zst |
Poll periodically for new posts
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/state/models/feed-view.ts | 28 | ||||
-rw-r--r-- | src/view/index.ts | 2 | ||||
-rw-r--r-- | src/view/screens/Home.tsx | 56 | ||||
-rw-r--r-- | yarn.lock | 5 |
5 files changed, 89 insertions, 3 deletions
diff --git a/package.json b/package.json index 5deef78fd..03a9a3e3c 100644 --- a/package.json +++ b/package.json @@ -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", 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, + }, +}) diff --git a/yarn.lock b/yarn.lock index 6d3d750f2..ce2d532d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" |