Revize 97deff21
Přidáno uživatelem Fantič před téměř 2 roky(ů)
App.tsx | ||
---|---|---|
1 | 1 |
|
2 | 2 |
import { StyleSheet, Text, View } from 'react-native' |
3 |
import { NativeBaseProvider, Box } from "native-base" |
|
3 |
import { NativeBaseProvider, Box, Center, VStack } from "native-base"
|
|
4 | 4 |
import { Provider } from "react-redux" |
5 | 5 |
import store from "./src/stores/store" |
6 | 6 |
import ItemViewPage from './src/pages/ItemViewPage' |
7 |
import LoginPage from './src/pages/LoginPage' |
|
7 | 8 |
|
8 | 9 |
export default function App() { |
9 | 10 |
return ( |
10 |
<Provider store={store}> |
|
11 |
<NativeBaseProvider> |
|
12 |
<ItemViewPage /> |
|
13 |
</NativeBaseProvider> |
|
14 |
</Provider> |
|
11 |
<Provider store={store}> |
|
12 |
<NativeBaseProvider> |
|
13 |
<VStack space={4} alignItems="center"> |
|
14 |
{/* TODO: remove placeholder pro navbar */} |
|
15 |
<Center w="100%" h="40" bg="indigo.300" rounded="md" shadow={3} /> |
|
16 |
<ItemViewPage itemId={'PrgA-811'} /> |
|
17 |
</VStack> |
|
18 |
</NativeBaseProvider> |
|
19 |
</Provider> |
|
15 | 20 |
) |
16 | 21 |
} |
17 | 22 |
|
src/api/itemservice.ts | ||
---|---|---|
1 | 1 |
import { axiosInstance } from "./api" |
2 |
|
|
3 |
|
|
4 |
export const getItemRequest = async (itemId : string) => { |
|
5 |
return await axiosInstance.get( |
|
6 |
`/api/item/${itemId}` |
|
7 |
) |
|
8 |
} |
|
9 |
|
|
10 |
// export const getItemConcordancesRequest = async (itemId : string) => { |
|
11 |
// return await axiosInstance.get( |
|
12 |
// `/api/concordances/${itemId}` |
|
13 |
// ) |
|
14 |
// } |
src/api/notesservice.ts | ||
---|---|---|
1 |
import { axiosInstance } from "./api" |
|
2 |
|
|
3 |
|
|
4 |
export const getItemNotesRequest = async (itemId : string) => { |
|
5 |
return await axiosInstance.get( |
|
6 |
`/api/notes/?item_id[]=${itemId}` |
|
7 |
) |
|
8 |
} |
src/components/ItemDetail.tsx | ||
---|---|---|
1 |
import { Center, Box, Heading, VStack, FormControl, Link, Input, Button, HStack, Text } from "native-base" |
|
2 |
import React, { useState } from "react" |
|
3 |
|
|
4 |
const ItemDetail = () => { |
|
5 |
return ( |
|
6 |
<Center w="100%"> |
|
7 |
<Box> |
|
8 |
Item Detail :-) |
|
9 |
</Box> |
|
10 |
</Center> |
|
11 |
) |
|
12 |
} |
|
13 |
|
|
14 |
export default ItemDetail |
src/components/ItemNotes.tsx | ||
---|---|---|
1 |
import { Center, Box, Heading, VStack, FormControl, Link, Input, Button, HStack, Text } from "native-base" |
|
2 |
import React, { useState } from "react" |
|
3 |
|
|
4 |
const ItemNotes = () => { |
|
5 |
return ( |
|
6 |
<Center w="100%"> |
|
7 |
<Box> |
|
8 |
Item notes :-) |
|
9 |
</Box> |
|
10 |
</Center> |
|
11 |
) |
|
12 |
} |
|
13 |
|
|
14 |
export default ItemNotes |
src/components/item/ItemDetail.tsx | ||
---|---|---|
1 |
import { Center, Box, Heading, VStack, FormControl, Link, Input, Button, HStack, Text } from "native-base" |
|
2 |
import React, { useState } from "react" |
|
3 |
|
|
4 |
const ItemDetail = () => { |
|
5 |
return ( |
|
6 |
<Center w="100%"> |
|
7 |
<Box> |
|
8 |
Item Detail :-) |
|
9 |
</Box> |
|
10 |
</Center> |
|
11 |
) |
|
12 |
} |
|
13 |
|
|
14 |
export default ItemDetail |
src/components/item/ItemNotes.tsx | ||
---|---|---|
1 |
import { Center, Box, Heading, VStack, FormControl, Link, Input, Button, HStack, Text } from "native-base" |
|
2 |
import React, { useState } from "react" |
|
3 |
|
|
4 |
const ItemNotes = () => { |
|
5 |
return ( |
|
6 |
<Center w="100%"> |
|
7 |
<Box> |
|
8 |
Item notes :-) |
|
9 |
</Box> |
|
10 |
</Center> |
|
11 |
) |
|
12 |
} |
|
13 |
|
|
14 |
export default ItemNotes |
src/pages/ItemViewPage.tsx | ||
---|---|---|
1 |
import { Center, Box, Heading, VStack, FormControl, Link, Input, Button, HStack, Text, StatusBar } from "native-base" |
|
1 |
import { Center, Box, Heading, VStack, FormControl, Link, Input, Button, HStack, Text, StatusBar, Container, Divider, Spacer } from "native-base"
|
|
2 | 2 |
import React, { useState } from "react" |
3 |
import { useDispatch } from "react-redux" |
|
3 |
import { useDispatch, useSelector } from "react-redux"
|
|
4 | 4 |
import { AppDispatch } from "../stores/store" |
5 |
import { useWindowDimensions } from "react-native" |
|
6 |
import { SceneMap, TabView } from "react-native-tab-view" |
|
7 |
import ItemDetail from "../components/ItemDetail" |
|
8 |
import ItemNotes from "../components/ItemNotes" |
|
9 |
|
|
10 |
const renderScene = SceneMap({ |
|
11 |
first: ItemDetail, |
|
12 |
second: ItemNotes, |
|
13 |
}); |
|
14 |
|
|
15 |
const ItemViewPage = () => { |
|
16 |
|
|
17 |
const layout = useWindowDimensions(); |
|
18 |
|
|
19 |
const [index, setIndex] = React.useState(0); |
|
20 |
const [routes] = React.useState([ |
|
21 |
{ key: 'first', title: 'Detail' }, |
|
22 |
{ key: 'second', title: 'Notes' }, |
|
23 |
]); |
|
24 |
|
|
25 |
|
|
26 |
return ( |
|
27 |
<TabView |
|
28 |
navigationState={{ index, routes }} |
|
29 |
renderScene={renderScene} |
|
30 |
onIndexChange={setIndex} |
|
31 |
initialLayout={{ width: layout.width }} |
|
32 |
tabBarPosition="bottom" |
|
33 |
/> |
|
34 |
); |
|
5 |
import { getPreviousConcordance, getNextConcordance, getItem } from "../stores/actions/itemThunks" |
|
6 |
import { Item, ItemState } from "../stores/reducers/itemSlice" |
|
7 |
|
|
8 |
interface ItemViewProps { |
|
9 |
itemId: string |
|
35 | 10 |
} |
36 | 11 |
|
37 |
export default ItemViewPage |
|
12 |
const ItemViewPage = (props: ItemViewProps) => { |
|
13 |
|
|
14 |
const dispatch = useDispatch<AppDispatch>(); |
|
15 |
|
|
16 |
const itemState = useSelector((state: ItemState) => state) |
|
17 |
|
|
18 |
useEffect(() => { |
|
19 |
dispatch(getItem(props.itemId)); |
|
20 |
}, []); |
|
21 |
|
|
22 |
const handleGetPreviousConcordance = async () => { |
|
23 |
await dispatch(getPreviousConcordance()); |
|
24 |
}; |
|
25 |
|
|
26 |
const handleGetNextConcordance = async () => { |
|
27 |
await dispatch(getNextConcordance()); |
|
28 |
}; |
|
29 |
|
|
30 |
|
|
31 |
|
|
32 |
return ( |
|
33 |
<VStack> |
|
34 |
<Center> |
|
35 |
<Heading |
|
36 |
size="lg" |
|
37 |
fontWeight="600" |
|
38 |
color="coolGray.800" |
|
39 |
_dark={{ color: "warmGrey.50" }} |
|
40 |
> |
|
41 |
{item.authorName} |
|
42 |
</Heading> |
|
43 |
|
|
44 |
<Text fontSize="xl" mt="2"> |
|
45 |
{item.caption} |
|
46 |
</Text> |
|
47 |
|
|
48 |
<HStack> |
|
49 |
<Button onPress={handleGetPreviousConcordance} mr={2}> |
|
50 |
Previous Concordance |
|
51 |
</Button> |
|
52 |
<Button onPress={handleGetNextConcordance}>Next Concordance</Button> |
|
53 |
</HStack> |
|
54 |
<Divider my="2" /> |
|
55 |
</Center> |
|
56 |
</VStack> |
|
57 |
); |
|
58 |
} |
|
59 |
|
|
60 |
export default ItemViewPage |
|
61 |
|
|
62 |
function useEffect(arg0: () => void, arg1: (string | (import("redux-thunk").ThunkDispatch<{ user: import("../stores/reducers/userSlice").UserState; item: import("../stores/reducers/itemSlice").ItemState }, undefined, import("redux").AnyAction> & import("redux").Dispatch<...>))[]) { |
|
63 |
throw new Error("Function not implemented.") |
|
64 |
} |
src/stores/actions/itemThunks.ts | ||
---|---|---|
1 |
import { createAsyncThunk } from "@reduxjs/toolkit" |
|
2 |
import { getItemRequest } from "../../api/itemservice" |
|
3 |
import { getItemNotesRequest } from "../../api/notesservice" |
|
4 |
import { ItemState } from "../reducers/itemSlice" |
|
5 |
|
|
6 |
export const getItem = createAsyncThunk( |
|
7 |
"item/getItem", |
|
8 |
async (itemId: string) => { |
|
9 |
try { |
|
10 |
const response = await getItemRequest(itemId) |
|
11 |
console.log(response) |
|
12 |
if (response.status === 200) { |
|
13 |
return { |
|
14 |
authorName: response.data.object[0].getty_data.display_name, |
|
15 |
caption: response.data.object[0].caption, |
|
16 |
concordances: response.data.concordances |
|
17 |
} |
|
18 |
} else { |
|
19 |
return Promise.reject(response.data ? response.data : "Error") |
|
20 |
} |
|
21 |
} catch (err: any) { |
|
22 |
return Promise.reject(err.response.data) |
|
23 |
} |
|
24 |
} |
|
25 |
) |
|
26 |
|
|
27 |
export const getNextConcordance = createAsyncThunk( |
|
28 |
"item/getNextConcordance", |
|
29 |
async (_, { getState, dispatch }) => { |
|
30 |
const state = getState() as ItemState; |
|
31 |
|
|
32 |
const nextIndex = (state.selectedConcordance + 1) % state.concordances.length; |
|
33 |
|
|
34 |
// Dispatch the getItem action with the next concordance id |
|
35 |
await dispatch(getItem(state.concordances[nextIndex].id)); |
|
36 |
|
|
37 |
// Return the next concordance index for updating the state in the reducer |
|
38 |
return nextIndex; |
|
39 |
} |
|
40 |
); |
|
41 |
|
|
42 |
export const getPreviousConcordance = createAsyncThunk( |
|
43 |
"item/getPreviousConcordance", |
|
44 |
async (_, { getState, dispatch }) => { |
|
45 |
const state = getState() as ItemState; |
|
46 |
const previousIndex = (state.selectedConcordance - 1 + state.concordances.length) % state.concordances.length; |
|
47 |
await dispatch(getItem(state.concordances[previousIndex].id)); |
|
48 |
return previousIndex; |
|
49 |
} |
|
50 |
); |
|
51 |
|
|
52 |
// export const getItemConcordances = createAsyncThunk( |
|
53 |
// "item/getItemConcordances", |
|
54 |
// async (itemId : string) => { |
|
55 |
// try { |
|
56 |
// const response = await getItemConcordancesRequest(itemId) |
|
57 |
// console.log(response) |
|
58 |
// if (response.status === 200) { |
|
59 |
// return { |
|
60 |
// // TODO set item concordances |
|
61 |
// } |
|
62 |
// } else { |
|
63 |
// return Promise.reject(response.data ? response.data : "Error") |
|
64 |
// } |
|
65 |
// } catch (err: any) { |
|
66 |
// return Promise.reject(err.response.data) |
|
67 |
// } |
|
68 |
// } |
|
69 |
// ) |
|
70 |
|
|
71 |
export const getItemNotes = createAsyncThunk( |
|
72 |
"item/getItemNotes", |
|
73 |
async (itemId: string) => { |
|
74 |
try { |
|
75 |
const response = await getItemNotesRequest(itemId) |
|
76 |
console.log(response) |
|
77 |
if (response.status === 200) { |
|
78 |
return { |
|
79 |
// TODO set item notes |
|
80 |
} |
|
81 |
} else { |
|
82 |
return Promise.reject(response.data ? response.data : "Error") |
|
83 |
} |
|
84 |
} catch (err: any) { |
|
85 |
return Promise.reject(err.response.data) |
|
86 |
} |
|
87 |
} |
|
88 |
) |
src/stores/reducers/itemSlice.ts | ||
---|---|---|
1 |
import { PayloadAction, createSlice } from "@reduxjs/toolkit" |
|
2 |
import { getItem, getItemNotes, getNextConcordance, getPreviousConcordance } from "../actions/itemThunks" |
|
3 |
import { Note } from "./notesSlice" |
|
4 |
|
|
5 |
|
|
6 |
export interface Item { |
|
7 |
authorName: string |
|
8 |
caption: string |
|
9 |
|
|
10 |
|
|
11 |
concordances: Concordance[] |
|
12 |
} |
|
13 |
|
|
14 |
export interface ItemState { |
|
15 |
item: Item |
|
16 |
concordances: Concordance[] |
|
17 |
selectedConcordance: number |
|
18 |
notes: Note[] |
|
19 |
lastError?: string |
|
20 |
} |
|
21 |
|
|
22 |
export interface Concordance { |
|
23 |
id: string |
|
24 |
certainty: Certainty |
|
25 |
} |
|
26 |
|
|
27 |
enum Certainty { |
|
28 |
SameAs = "same_as", |
|
29 |
High = "high", |
|
30 |
Medium = "medium", |
|
31 |
Low = "low", |
|
32 |
} |
|
33 |
|
|
34 |
export const CertaintyWithColors: Record<Certainty, { certainty: Certainty; color: string }> = { |
|
35 |
same_as: { certainty: Certainty.SameAs, color: "#000000" }, |
|
36 |
high: { certainty: Certainty.High, color: "#FF0000" }, |
|
37 |
medium: { certainty: Certainty.Medium, color: "#FFFF00" }, |
|
38 |
low: { certainty: Certainty.Low, color: "#00FF00" }, |
|
39 |
}; |
|
40 |
|
|
41 |
const initialState: ItemState = { |
|
42 |
item: {} as Item, |
|
43 |
selectedConcordance: 0, |
|
44 |
concordances: [], |
|
45 |
notes: [], |
|
46 |
lastError: "" |
|
47 |
} |
|
48 |
|
|
49 |
export const itemSlice = createSlice({ |
|
50 |
name: "item", |
|
51 |
initialState: initialState, |
|
52 |
reducers: { |
|
53 |
}, |
|
54 |
extraReducers: (builder) => { |
|
55 |
// getItem |
|
56 |
builder.addCase(getItem.fulfilled, (state, action) => { |
|
57 |
|
|
58 |
// set concordances from item, if selected concordance is 0 |
|
59 |
if (state.selectedConcordance === 0) { |
|
60 |
state.concordances = action.payload.concordances; |
|
61 |
} |
|
62 |
|
|
63 |
state.item = { |
|
64 |
authorName: action.payload.authorName, |
|
65 |
caption: action.payload.caption, |
|
66 |
concordances: action.payload.concordances |
|
67 |
} |
|
68 |
|
|
69 |
}) |
|
70 |
builder.addCase(getItem.rejected, (state, action) => { |
|
71 |
state.lastError = action.error.message |
|
72 |
}) |
|
73 |
|
|
74 |
// getNextConcordance |
|
75 |
builder.addCase(getNextConcordance.fulfilled, (state, action) => { |
|
76 |
state.selectedConcordance = action.payload; |
|
77 |
}); |
|
78 |
|
|
79 |
builder.addCase(getNextConcordance.rejected, (state, action) => { |
|
80 |
state.lastError = action.error.message; |
|
81 |
}); |
|
82 |
|
|
83 |
// getPreviousConcordance |
|
84 |
builder.addCase(getPreviousConcordance.fulfilled, (state, action) => { |
|
85 |
state.selectedConcordance = action.payload; |
|
86 |
}); |
|
87 |
|
|
88 |
builder.addCase(getPreviousConcordance.rejected, (state, action) => { |
|
89 |
state.lastError = action.error.message; |
|
90 |
}); |
|
91 |
|
|
92 |
} |
|
93 |
}) |
|
94 |
|
|
95 |
export default itemSlice.reducer |
src/stores/reducers/notesSlice.ts | ||
---|---|---|
1 |
|
|
2 |
export interface Note { |
|
3 |
username: string |
|
4 |
userId: string |
|
5 |
avatarUrl: string |
|
6 |
items: string[] |
|
7 |
createdTime: Date |
|
8 |
updatedTime: Date |
|
9 |
noteColor: string |
|
10 |
lastError?: string |
|
11 |
} |
src/stores/store.ts | ||
---|---|---|
1 | 1 |
import { configureStore } from "@reduxjs/toolkit" |
2 | 2 |
import userReducer from "./reducers/userSlice" |
3 |
import itemReducer from "./reducers/itemSlice" |
|
3 | 4 |
|
4 | 5 |
const store = configureStore({ |
5 | 6 |
reducer: { |
6 | 7 |
user: userReducer, |
8 |
item: itemReducer |
|
7 | 9 |
}, |
8 | 10 |
}) |
9 | 11 |
|
Také k dispozici: Unified diff
#re 10343 ItemView: Redux thunks, slice