mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Notifications working
This commit is contained in:
@ -5,6 +5,7 @@ import { enableScreens } from 'react-native-screens'
|
|||||||
enableScreens()
|
enableScreens()
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { Feather } from '@expo/vector-icons'
|
||||||
import store from 'src/stacks/common/store'
|
import store from 'src/stacks/common/store'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { StatusBar } from 'expo-status-bar'
|
import { StatusBar } from 'expo-status-bar'
|
||||||
@ -21,10 +22,39 @@ export default function Index () {
|
|||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<StatusBar style='auto' />
|
<StatusBar style='auto' />
|
||||||
<NavigationContainer>
|
<NavigationContainer>
|
||||||
<Tab.Navigator>
|
<Tab.Navigator
|
||||||
|
screenOptions={({ route }) => ({
|
||||||
|
tabBarIcon: ({ focused, color, size }) => {
|
||||||
|
let name
|
||||||
|
switch (route.name) {
|
||||||
|
case 'Local':
|
||||||
|
name = 'home'
|
||||||
|
break
|
||||||
|
case 'Public':
|
||||||
|
name = 'globe'
|
||||||
|
break
|
||||||
|
case 'Post':
|
||||||
|
name = 'plus'
|
||||||
|
break
|
||||||
|
case 'Notifications':
|
||||||
|
name = 'bell'
|
||||||
|
break
|
||||||
|
case 'Me':
|
||||||
|
name = focused ? 'smile' : 'meh'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return <Feather name={name} size={size} color={color} />
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
tabBarOptions={{
|
||||||
|
activeTintColor: 'black',
|
||||||
|
inactiveTintColor: 'gray',
|
||||||
|
showLabel: false
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Tab.Screen name='Local' component={Local} />
|
<Tab.Screen name='Local' component={Local} />
|
||||||
<Tab.Screen name='Public' component={Public} />
|
<Tab.Screen name='Public' component={Public} />
|
||||||
{/* <Tab.Screen name='Notifications' component={Notifications} /> */}
|
<Tab.Screen name='Notifications' component={Notifications} />
|
||||||
<Tab.Screen name='Me' component={Me} />
|
<Tab.Screen name='Me' component={Me} />
|
||||||
</Tab.Navigator>
|
</Tab.Navigator>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
|
@ -5,7 +5,7 @@ import HTML from 'react-native-render-html'
|
|||||||
|
|
||||||
import relativeTime from 'src/utils/relativeTime'
|
import relativeTime from 'src/utils/relativeTime'
|
||||||
|
|
||||||
export default function TootTimeline ({ item }) {
|
export default function TootTimeline ({ item, notification }) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.tootTimeline}>
|
<View style={styles.tootTimeline}>
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
@ -36,7 +36,13 @@ export default function TootTimeline ({ item }) {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{item.content ? <HTML html={item.content} /> : <></>}
|
{notification ? (
|
||||||
|
<HTML html={item.status.content} />
|
||||||
|
) : item.content ? (
|
||||||
|
<HTML html={item.content} />
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -54,7 +60,8 @@ TootTimeline.propTypes = {
|
|||||||
website: PropTypes.string
|
website: PropTypes.string
|
||||||
}),
|
}),
|
||||||
content: PropTypes.string
|
content: PropTypes.string
|
||||||
}).isRequired
|
}).isRequired,
|
||||||
|
notification: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -5,6 +5,7 @@ import TimelinesCombined from 'src/stacks/common/TimelinesCombined'
|
|||||||
export default function Local () {
|
export default function Local () {
|
||||||
return (
|
return (
|
||||||
<TimelinesCombined
|
<TimelinesCombined
|
||||||
|
page='Local'
|
||||||
route={[
|
route={[
|
||||||
{ title: '关注', timeline: { endpoint: 'home' } },
|
{ title: '关注', timeline: { endpoint: 'home' } },
|
||||||
{ title: '本站', timeline: { endpoint: 'public', local: true } }
|
{ title: '本站', timeline: { endpoint: 'public', local: true } }
|
||||||
|
@ -1,9 +1,33 @@
|
|||||||
import React, { useEffect } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { ActivityIndicator, FlatList, View } from 'react-native'
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
import { useSelector, useDispatch } from 'react-redux'
|
import { Feather } from '@expo/vector-icons'
|
||||||
|
|
||||||
import TootTimeline from 'src/components/TootTimeline'
|
import Timeline from 'src/stacks/common/Timeline'
|
||||||
|
|
||||||
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
export default function Notifications () {
|
export default function Notifications () {
|
||||||
return <View></View>
|
const [renderHeader, setRenderHeader] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const nbr = setTimeout(() => setRenderHeader(true), 50)
|
||||||
|
return
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack.Navigator
|
||||||
|
screenOptions={{
|
||||||
|
statusBarAnimation: 'none',
|
||||||
|
headerRight: () =>
|
||||||
|
renderHeader ? (
|
||||||
|
<Feather name='search' size={24} color='black' />
|
||||||
|
) : null,
|
||||||
|
headerTitle: '通知'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen name='Notifications'>
|
||||||
|
{props => <Timeline endpoint='notifications' {...props} />}
|
||||||
|
</Stack.Screen>
|
||||||
|
</Stack.Navigator>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import TimelinesCombined from 'src/stacks/common/TimelinesCombined'
|
|||||||
export default function Public () {
|
export default function Public () {
|
||||||
return (
|
return (
|
||||||
<TimelinesCombined
|
<TimelinesCombined
|
||||||
|
page='Public'
|
||||||
route={[
|
route={[
|
||||||
{ title: '跨站', timeline: { endpoint: 'public' } },
|
{ title: '跨站', timeline: { endpoint: 'public' } },
|
||||||
{ title: '他站', timeline: { remote: true } }
|
{ title: '他站', timeline: { remote: true } }
|
||||||
|
@ -1,11 +1,61 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { ActivityIndicator, FlatList, View } from 'react-native'
|
import { ActivityIndicator, FlatList, View } from 'react-native'
|
||||||
import { connect, useSelector, useDispatch } from 'react-redux'
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
|
|
||||||
import TootTimeline from 'src/components/TootTimeline'
|
import TootTimeline from 'src/components/TootTimeline'
|
||||||
import { fetch, getToots, getStatus } from './timelineSlice'
|
import { fetch, getToots, getStatus } from './timelineSlice'
|
||||||
|
|
||||||
|
const Default = ({ toots, status, remote, endpoint, local }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FlatList
|
||||||
|
data={toots}
|
||||||
|
keyExtractor={({ id }) => id}
|
||||||
|
renderItem={TootTimeline}
|
||||||
|
onRefresh={() =>
|
||||||
|
dispatch(
|
||||||
|
fetch({ remote, endpoint, local, id: toots[0].id, newer: true })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
refreshing={status === 'loading'}
|
||||||
|
onEndReached={() =>
|
||||||
|
dispatch(
|
||||||
|
fetch({ remote, endpoint, local, id: toots[toots.length - 1].id })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onEndReachedThreshold={0.5}
|
||||||
|
style={{ height: '100%', width: '100%' }}
|
||||||
|
/>
|
||||||
|
{status === 'loading' && <ActivityIndicator />}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Notifications = ({ toots, status, endpoint }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FlatList
|
||||||
|
data={toots}
|
||||||
|
keyExtractor={({ status: { id } }) => id}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<TootTimeline item={item} notification={true} />
|
||||||
|
)}
|
||||||
|
// onRefresh={() =>
|
||||||
|
// dispatch(fetch({ endpoint, id: toots[0].status.id, newer: true }))
|
||||||
|
// }
|
||||||
|
// refreshing={status === 'loading'}
|
||||||
|
// onEndReached={() =>
|
||||||
|
// dispatch(fetch({ endpoint, id: toots[toots.length - 1].status.id }))
|
||||||
|
// }
|
||||||
|
onEndReachedThreshold={0.5}
|
||||||
|
style={{ height: '100%', width: '100%' }}
|
||||||
|
/>
|
||||||
|
{status === 'loading' && <ActivityIndicator />}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function Timeline ({ remote, endpoint, local }) {
|
export default function Timeline ({ remote, endpoint, local }) {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const toots = useSelector(state =>
|
const toots = useSelector(state =>
|
||||||
@ -20,34 +70,26 @@ export default function Timeline ({ remote, endpoint, local }) {
|
|||||||
dispatch(fetch({ remote, endpoint, local }))
|
dispatch(fetch({ remote, endpoint, local }))
|
||||||
}
|
}
|
||||||
}, [status, dispatch])
|
}, [status, dispatch])
|
||||||
let content
|
|
||||||
|
|
||||||
|
let content
|
||||||
if (status === 'error') {
|
if (status === 'error') {
|
||||||
content = <Text>Error message</Text>
|
content = <Text>Error message</Text>
|
||||||
} else {
|
} else {
|
||||||
content = (
|
if (endpoint === 'notifications') {
|
||||||
<>
|
content = (
|
||||||
<FlatList
|
<Notifications toots={toots} status={status} endpoint={endpoint} />
|
||||||
data={toots}
|
)
|
||||||
keyExtractor={({ id }) => id}
|
} else {
|
||||||
renderItem={TootTimeline}
|
content = (
|
||||||
onRefresh={() =>
|
<Default
|
||||||
dispatch(
|
toots={toots}
|
||||||
fetch({ remote, endpoint, local, id: toots[0].id, newer: true })
|
status={status}
|
||||||
)
|
remote={remote}
|
||||||
}
|
endpoint={endpoint}
|
||||||
refreshing={status === 'loading'}
|
local={local}
|
||||||
onEndReached={() =>
|
|
||||||
dispatch(
|
|
||||||
fetch({ remote, endpoint, local, id: toots[toots.length - 1].id })
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onEndReachedThreshold={0.5}
|
|
||||||
style={{ height: '100%', width: '100%' }}
|
|
||||||
/>
|
/>
|
||||||
{status === 'loading' && <ActivityIndicator />}
|
)
|
||||||
</>
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <View>{content}</View>
|
return <View>{content}</View>
|
||||||
|
@ -9,7 +9,7 @@ import Timeline from './Timeline'
|
|||||||
|
|
||||||
const Stack = createNativeStackNavigator()
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
export default function TimelinesCombined ({ route }) {
|
export default function TimelinesCombined ({ page, route }) {
|
||||||
const [segment, setSegment] = useState(0)
|
const [segment, setSegment] = useState(0)
|
||||||
const [renderHeader, setRenderHeader] = useState(false)
|
const [renderHeader, setRenderHeader] = useState(false)
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ export default function TimelinesCombined ({ route }) {
|
|||||||
) : null
|
) : null
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack.Screen name='LocalView'>
|
<Stack.Screen name={page}>
|
||||||
{props => (
|
{props => (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={{
|
style={{
|
||||||
|
@ -6,8 +6,8 @@ import timelineSlice from 'src/stacks/common/timelineSlice'
|
|||||||
// get site information from local storage and pass to reducers
|
// get site information from local storage and pass to reducers
|
||||||
const preloadedState = {
|
const preloadedState = {
|
||||||
instanceInfo: {
|
instanceInfo: {
|
||||||
current: 'social.xmflsct.com',
|
current: 'm.cmx.im',
|
||||||
currentToken: 'qjzJ0IjvZ1apsn0_wBkGcdjKgX7Dao9KEPhGwggPwAo',
|
currentToken: 'Cxx19XX2VNHnPy_dr_HCHMh4HvwHEvYwWrrU3r3BNzQ',
|
||||||
remote: 'mastodon.social'
|
remote: 'mastodon.social'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import { client } from 'src/api/client'
|
|||||||
// Local: public/local
|
// Local: public/local
|
||||||
// CurrentPublic: public
|
// CurrentPublic: public
|
||||||
// RemotePublic: remote
|
// RemotePublic: remote
|
||||||
|
// Notifications: notifications
|
||||||
|
|
||||||
const checkInstance = ({ remote, endpoint, local }) =>
|
const checkInstance = ({ remote, endpoint, local }) =>
|
||||||
remote ? 'remote' : `${endpoint}${local ? '/local' : ''}`
|
remote ? 'remote' : `${endpoint}${local ? '/local' : ''}`
|
||||||
@ -16,6 +17,8 @@ export const fetch = createAsyncThunk(
|
|||||||
async ({ remote, endpoint, local, id, newer }, { getState }) => {
|
async ({ remote, endpoint, local, id, newer }, { getState }) => {
|
||||||
const instance = remote
|
const instance = remote
|
||||||
? `${getState().instanceInfo.remote}/api/v1/timelines/public`
|
? `${getState().instanceInfo.remote}/api/v1/timelines/public`
|
||||||
|
: endpoint === 'notifications'
|
||||||
|
? `${getState().instanceInfo.current}/api/v1/${endpoint}`
|
||||||
: `${getState().instanceInfo.current}/api/v1/timelines/${endpoint}`
|
: `${getState().instanceInfo.current}/api/v1/timelines/${endpoint}`
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
@ -58,6 +61,10 @@ export const timelineSlice = createSlice({
|
|||||||
remote: {
|
remote: {
|
||||||
toots: [],
|
toots: [],
|
||||||
status: 'idle'
|
status: 'idle'
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
toots: [],
|
||||||
|
status: 'idle'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
extraReducers: {
|
extraReducers: {
|
||||||
|
Reference in New Issue
Block a user