diff --git a/eslint-problems b/eslint-problems
index b0598afe68..535ca222ce 100644
--- a/eslint-problems
+++ b/eslint-problems
@@ -134,23 +134,6 @@
./source/views/menus/types.ts
35:29 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
-./source/views/news/news-container.tsx
- 28:2 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
- 34:14 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
- 59:2 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
-
-./source/views/news/news-list.tsx
- 22:19 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
- 29:16 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
- 33:20 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
- 39:15 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
- 47:17 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
- 49:2 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
-
-./source/views/news/news-row.tsx
- 14:13 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
- 22:2 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
-
./source/views/settings/components/logo.tsx
26:2 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
32:2 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
@@ -307,5 +290,5 @@
./source/views/student-orgs/util.ts
3:8 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
-✖ 192 problems (0 errors, 192 warnings)
+✖ 181 problems (0 errors, 181 warnings)
diff --git a/images/news-sources/index.ts b/images/news-sources/index.ts
index 93f5561f91..4c9df5f034 100644
--- a/images/news-sources/index.ts
+++ b/images/news-sources/index.ts
@@ -1 +1,5 @@
export {ksto} from '../streaming'
+
+export * as mess from './mess.png'
+export * as oleville from './oleville.png'
+export * as stolaf from './stolaf.png'
diff --git a/source/navigation/routes.tsx b/source/navigation/routes.tsx
index 8654c69ba9..bd128ec43a 100644
--- a/source/navigation/routes.tsx
+++ b/source/navigation/routes.tsx
@@ -29,7 +29,7 @@ import {
MenuItemDetailView,
DetailNavigationOptions,
} from '@frogpond/food-menu/food-item-detail'
-// import NewsView from '../views/news'
+import * as news from '../views/news'
import * as settings from '../views/settings/'
// import SISView from '../views/sis'
import * as streaming from '../views/streaming'
@@ -214,10 +214,16 @@ export function RootStack(): JSX.Element {
options={orgs.DetailNavigationOptions}
/>
+
+
+
{/*
-
(
- ()
+
+const StOlafNewsView = () => (
+
+)
+const MessNewsView = () => (
+
+)
+const OlevilleNewsView = () => (
+
+)
+
+const NewsView = (): JSX.Element => {
+ return (
+
+
- ),
- navigationOptions: {
- tabBarLabel: 'St. Olaf',
- tabBarIcon: TabBarIcon('school'),
- },
- },
-
- OlevilleNewsView: {
- screen: ({navigation}) => (
-
- ),
- navigationOptions: {
- tabBarLabel: 'Oleville',
- tabBarIcon: TabBarIcon('happy'),
- },
- },
-
- MessNewsView: {
- screen: ({navigation}) => (
-
- ),
- navigationOptions: {
- tabBarLabel: 'The Mess',
- tabBarIcon: TabBarIcon('paper'),
- },
- },
-})
-NewsView.navigationOptions = {
- title: 'News',
+
+ )
}
-export default NewsView
+export {NewsView as View}
+
+export const NavigationOptions: NativeStackNavigationOptions = {
+ title: 'News',
+}
diff --git a/source/views/news/news-container.tsx b/source/views/news/news-container.tsx
deleted file mode 100644
index 6f08562b86..0000000000
--- a/source/views/news/news-container.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import * as React from 'react'
-import type {StoryType} from './types'
-import {LoadingView} from '@frogpond/notice'
-import type {TopLevelViewPropsType} from '../types'
-import {NewsList} from './news-list'
-import {fetch} from '@frogpond/fetch'
-import {API} from '@frogpond/api'
-
-type Props = TopLevelViewPropsType & {
- source: string | {url: string; type: 'rss' | 'wp-json'}
- thumbnail: false | number
- title: string
-}
-
-type State = {
- entries: StoryType[]
- initialLoadComplete: boolean
- refreshing: boolean
-}
-
-export default class NewsContainer extends React.PureComponent {
- state = {
- entries: [],
- initialLoadComplete: false,
- refreshing: false,
- }
-
- componentDidMount() {
- this.fetchData().then(() => {
- this.setState(() => ({initialLoadComplete: true}))
- })
- }
-
- fetchData = async (reload?: boolean) => {
- let url
- if (typeof this.props.source === 'string') {
- url = API(`/news/named/${this.props.source}`)
- } else if (this.props.source.type === 'rss') {
- url = API('/news/rss', {url: this.props.source.url})
- } else if (this.props.source.type === 'wp-json') {
- url = API('/news/wpjson', {url: this.props.source.url})
- } else {
- throw new Error('invalid news source type!')
- }
-
- let entries: Array = await fetch(url, {
- delay: reload ? 500 : 0,
- }).json()
-
- this.setState(() => ({entries}))
- }
-
- refresh = async (): Promise => {
- this.setState(() => ({refreshing: true}))
- await this.fetchData(true)
- this.setState(() => ({refreshing: false}))
- }
-
- render() {
- if (!this.state.initialLoadComplete) {
- return
- }
-
- return (
-
- )
- }
-}
diff --git a/source/views/news/news-list.tsx b/source/views/news/news-list.tsx
index 588f2b2231..cd0f395bb5 100644
--- a/source/views/news/news-list.tsx
+++ b/source/views/news/news-list.tsx
@@ -1,12 +1,18 @@
import * as React from 'react'
-import {StyleSheet, FlatList} from 'react-native'
-import * as c from '@frogpond/colors'
+import {FlatList, StyleSheet} from 'react-native'
import type {StoryType} from './types'
+import {API} from '@frogpond/api'
+import * as c from '@frogpond/colors'
+import {useFetch} from 'react-async'
import {ListSeparator} from '@frogpond/lists'
-import {NoticeView} from '@frogpond/notice'
-import type {TopLevelViewPropsType} from '../types'
-import {NewsRow} from './news-row'
+import {LoadingView, NoticeView} from '@frogpond/notice'
import {openUrl} from '@frogpond/open-url'
+import {NewsRow} from './news-row'
+
+type Props = {
+ source: string | {url: string; type: 'rss' | 'wp-json'}
+ thumbnail: false | number
+}
const styles = StyleSheet.create({
listContainer: {
@@ -17,54 +23,72 @@ const styles = StyleSheet.create({
},
})
-type Props = TopLevelViewPropsType & {
- name: string
- onRefresh: () => any
- entries: StoryType[]
- loading: boolean
- thumbnail: false | number
-}
-
-export class NewsList extends React.PureComponent {
- onPressNews = (url: string) => {
- return openUrl(url)
+const useNews = (source: Props['source']) => {
+ let url
+ if (typeof source === 'string') {
+ url = API(`/news/named/${source}`)
+ } else if (source.type === 'rss') {
+ url = API('/news/rss', {url: source.url})
+ } else if (source.type === 'wp-json') {
+ url = API('/news/wpjson', {url: source.url})
+ } else {
+ throw new Error('invalid news source type!')
}
- renderSeparator = () => (
-
- )
-
- renderItem = ({item}: {item: StoryType}) => (
-
- )
+ return useFetch(url, {
+ headers: {accept: 'application/json'},
+ })
+}
- keyExtractor = (item: StoryType) => item.title
+export const NewsList = (props: Props): JSX.Element => {
+ let {
+ data = [],
+ error,
+ reload,
+ isPending,
+ isInitial,
+ isLoading,
+ } = useNews(props.source)
- render() {
- // remove all entries with blank excerpts
- // remove all entries with a }
- contentContainerStyle={styles.contentContainer}
- data={entries}
- keyExtractor={this.keyExtractor}
- onRefresh={this.props.onRefresh}
- refreshing={this.props.loading}
- renderItem={this.renderItem}
- style={styles.listContainer}
+
)
}
+
+ return (
+ (
+
+ )}
+ ListEmptyComponent={
+ isLoading ? :
+ }
+ contentContainerStyle={styles.contentContainer}
+ data={entries}
+ keyExtractor={(item: StoryType) => item.title}
+ onRefresh={reload}
+ refreshing={isPending && !isInitial}
+ renderItem={({item}: {item: StoryType}) => (
+ openUrl(url)}
+ story={item}
+ thumbnail={props.thumbnail}
+ />
+ )}
+ style={styles.listContainer}
+ />
+ )
}
diff --git a/source/views/news/news-row.tsx b/source/views/news/news-row.tsx
index 94904428cc..a734c6843b 100644
--- a/source/views/news/news-row.tsx
+++ b/source/views/news/news-row.tsx
@@ -10,42 +10,41 @@ type Props = {
thumbnail: false | number
}
-export class NewsRow extends React.PureComponent {
- _onPress = () => {
- if (!this.props.story.link) {
+export const NewsRow = (props: Props): JSX.Element => {
+ let _onPress = () => {
+ if (!props.story.link) {
Alert.alert('There is nowhere to go for this story')
return
}
- this.props.onPress(this.props.story.link)
+ props.onPress(props.story.link)
}
- render() {
- let {story} = this.props
- let thumb =
- this.props.thumbnail !== false
- ? story.featuredImage
- ? {uri: story.featuredImage}
- : this.props.thumbnail
- : null
+ let {story} = props
- return (
-
-
- {thumb !== null ? (
-
- ) : null}
-
- {story.title}
- {story.excerpt}
-
-
-
- )
- }
+ let thumb =
+ props.thumbnail !== false
+ ? story.featuredImage
+ ? {uri: story.featuredImage}
+ : props.thumbnail
+ : null
+
+ return (
+
+
+ {thumb !== null ? (
+
+ ) : null}
+
+ {story.title}
+ {story.excerpt}
+
+
+
+ )
}
const styles = StyleSheet.create({
diff --git a/tsc-counts b/tsc-counts
index e492f91aac..3ec226576a 100644
--- a/tsc-counts
+++ b/tsc-counts
@@ -1,13 +1,13 @@
18 TS2740
16 TS2345
13 TS2322
-8 TS7031
7 TS2769
-7 TS2339
6 TS2307
+5 TS7031
5 TS7016
-4 TS2571
+4 TS2339
3 TS2739
+3 TS2571
2 TS7006
2 TS2783
2 TS2613
diff --git a/tsc-errors b/tsc-errors
index 07e294ee5e..b296d140d9 100644
--- a/tsc-errors
+++ b/tsc-errors
@@ -86,13 +86,6 @@ source/views/menus/lib/process-menu-shorthands.ts(31,3): error TS2740: Type '{}'
source/views/menus/lib/process-menu-shorthands.ts(35,3): error TS2783: 'special' is specified more than once, so this usage will be overwritten.
source/views/menus/menu-github.tsx(105,4): error TS2322: Type '{ foodItems: MenuItemContainerType; meals: ProcessedMealType[]; menuCorIcons: MasterCorIconMapType; name: string; navigation: NavigationScreenProp<...>; now: Moment; }' is not assignable to type 'IntrinsicAttributes & ReactProps'.
Property 'navigation' does not exist on type 'IntrinsicAttributes & ReactProps'.
-source/views/news/index.tsx(9,13): error TS7031: Binding element 'navigation' implicitly has an 'any' type.
-source/views/news/index.tsx(13,27): error TS2339: Property 'stolaf' does not exist on type 'typeof import("./images/news-sources/index")'.
-source/views/news/index.tsx(24,13): error TS7031: Binding element 'navigation' implicitly has an 'any' type.
-source/views/news/index.tsx(28,27): error TS2339: Property 'oleville' does not exist on type 'typeof import("./images/news-sources/index")'.
-source/views/news/index.tsx(39,13): error TS7031: Binding element 'navigation' implicitly has an 'any' type.
-source/views/news/index.tsx(43,27): error TS2339: Property 'mess' does not exist on type 'typeof import("./images/news-sources/index")'.
-source/views/news/index.tsx(53,1): error TS2571: Object is of type 'unknown'.
source/views/settings/components/logo.tsx(2,24): error TS7016: Could not find a declaration file for module '@hawkrives/react-native-alternate-icons'. './node_modules/@hawkrives/react-native-alternate-icons/index.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/hawkrives__react-native-alternate-icons` if it exists or add a new declaration (.d.ts) file containing `declare module '@hawkrives/react-native-alternate-icons';`
source/views/settings/components/logo.tsx(27,29): error TS7006: Parameter 'name' implicitly has an 'any' type.