Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 852de08e

Přidáno uživatelem Fantič před téměř 2 roky(ů)

re #10454: ItemView: Concordance scroll view, Loading Component

Zobrazit rozdíly:

App.tsx
11 11
    <Provider store={store}>
12 12
      <NativeBaseProvider>
13 13
          {/* TODO: remove placeholder pro header / nav */}
14
          <Center w="100%" h="40" bg="primary.100" rounded="md" shadow={3} />
14
          <Center w="100%" h="40" rounded="md" background="success.300"/>
15 15
          <ItemViewPage itemId={'PrgA-811'} />
16 16
      </NativeBaseProvider>
17 17
    </Provider>
src/components/item/ItemView.tsx
1
import { Center, Box, Heading, VStack, FormControl, Link, Input, Button, HStack, Text, Divider } from "native-base"
2
import React, { useState } from "react"
1
import { Center, Box, VStack, Button, HStack, Text, Image, ScrollView, View, Spacer } from "native-base"
2
import React from "react"
3 3
import { Item } from "../../types/item"
4 4
import { Note } from "../../types/note"
5
import LoadingBox from "../loading/loadingBox"
5 6

  
6 7
interface ItemDetailProps {
7 8
    item: Item,
8
    notes: Note[]
9
    itemLoading: boolean,
10
    notes: Note[],
11
    notesLoading: boolean
9 12
}
10 13

  
11 14

  
12 15
const ItemDetail = (props: { item: Item }) => {
13 16
    const item = props.item;
17

  
14 18
    return (
15
        <Center>
19
        <View alignItems="center" flex="1" justifyContent="center">
16 20
            {
17 21
                item && (
18
                    <VStack>
19
                        <Text>id: {item.id}</Text>
20
                        <Text>name: {item.workName}</Text>
21
                    </VStack>
22
                    <ScrollView>
23
                        <VStack>
24
                            <Spacer/>
25
                            <Spacer/>
26
                            <Text>id: {item.id}</Text>
27
                            <Text>name: {item.workName}</Text>
28
                            {
29
                                item.fullView && item.imageUrl && (
30
                                    <Image size={250} alt="image" source={{ uri: `http:147.228.173.159/static/images/${item.imageUrl}` }} />
31
                                )
32
                            }
33
                        </VStack>
34
                    </ScrollView>
35

  
22 36
                )
23 37
            }
24
        </Center>
38
        </View>
25 39
    );
26 40
}
27 41

  
......
53 67
    // item shown / note shown
54 68
    const [itemShown, setItemShown] = React.useState(true);
55 69

  
70
    const {itemLoading, notesLoading} = props;
71

  
56 72
    const handleItemClick = () => {
57 73
        setItemShown(true);
58 74
    }
......
61 77
        setItemShown(false);
62 78
    }
63 79

  
64

  
65 80
    return (
66
        <>
81
        <Box display="flex" flex={1} flexDirection="column" justifyContent="space-between">
67 82
            {itemShown ? (
83
                itemLoading ? 
84
                <LoadingBox text="Item loading"/>
85
                :
68 86
                <ItemDetail item={props.item} />
69 87
            ) : (
88
                notesLoading ? 
89
                <LoadingBox text="Notes loading"/>
90
                :
70 91
                <ItemNotes notes={props.notes} />
71 92
            )}
72
            <HStack mt={4}>
73
                <Button
74
                    colorScheme={itemShown ? "blue" : undefined}
93

  
94
            {/* item notes switch tab */}
95
            <HStack alignItems="center">
96
                <Button 
97
                    variant={itemShown ? "subtle" : "outline"}
98
                    w="50%"
75 99
                    onPress={handleItemClick}
76 100
                >
77 101
                    Item
78 102
                </Button>
79
                <Button
80
                    colorScheme={!itemShown ? "blue" : undefined}
103
                <Button 
104
                    variant={itemShown ? "outline" : "subtle"}
105
                    w="50%"
81 106
                    onPress={handleNotesClick}
82 107
                >
83 108
                    Notes
84 109
                </Button>
85 110
            </HStack>
86
        </>
111
        </Box>
87 112
    )
88 113
}
89 114

  
src/components/loading/loadingBox.tsx
1
import { Center, Box, VStack, Button, HStack, Text, Image, ScrollView, Heading, Spinner } from "native-base"
2
import React from "react"
3

  
4

  
5
interface loadingBoxProps {
6
    text: string
7
}
8

  
9
const LoadingBox = (props: loadingBoxProps) => {
10
    const { text } = props;
11

  
12
    return (
13
        <HStack alignItems="center" flex="1" justifyContent="center">
14
            <Spinner/>
15
            <Heading color="primary.400" fontSize="md">
16
                {text}
17
            </Heading>
18
        </HStack>
19
    );
20
}
21

  
22

  
23
export default LoadingBox
src/pages/ItemViewPage.tsx
1
import { Center, Box, Heading, VStack, FormControl, Link, Input, Button, HStack, Text, StatusBar, Container, Divider, Spacer, Row, View } from "native-base"
2
import React, { useState, useEffect } from "react"
1
import React, { useEffect } from "react"
3 2
import { useDispatch, useSelector } from "react-redux"
4 3
import { AppDispatch, RootState } from "../stores/store"
5 4
import { getItem, getItemNotes, setConcordances, setSelectedConcordance } from "../stores/actions/itemThunks"
6 5
import { login } from "../stores/actions/userThunks"
7
import { SceneRendererProps, TabView } from "react-native-tab-view"
8
import { useWindowDimensions } from "react-native"
6
import { NavigationState, SceneRendererProps, TabBar, TabView } from "react-native-tab-view"
9 7
import ItemView from "../components/item/ItemView"
8
import { Button, Pressable, View, Text, Icon, CircleIcon, ScrollView, HStack, VStack } from "native-base"
9
import { useWindowDimensions } from "react-native"
10
import LoadingBox from "../components/loading/loadingBox"
11
import { CertaintyWithColors } from "../stores/reducers/itemSlice"
10 12

  
11 13

  
12 14
interface ItemViewProps {
......
16 18
const ItemViewPage = (props: ItemViewProps) => {
17 19

  
18 20
  const layout = useWindowDimensions();
19

  
20 21
  const [routes, setRoutes] = React.useState([
21 22
    // initial tab
22 23
    { key: 'loading', title: 'Loading item...' },
23 24
  ]);
25
  const [loadedScenes, setLoadedScenes] = React.useState({});
24 26

  
25
  const dispatch = useDispatch<AppDispatch>();
26 27

  
27
  const { item, notes, concordances, selectedConcordance } = useSelector((state: RootState) => state.itemViewState)
28 28

  
29
  const dispatch = useDispatch<AppDispatch>();
30

  
31
  const { item, itemLoading, notes, notesLoading, concordances, selectedConcordance } = useSelector((state: RootState) => state.itemViewState)
29 32

  
30 33
  // initial: load main item
31 34
  useEffect(() => {
32 35
    // TODO remove login from here
33 36
    dispatch(login({ username: "Viktorie", password: "Golem123." }));
34 37
    dispatch(getItem(props.itemId));
35
  }, [props.itemId]);
38
  }, []);
36 39

  
37 40

  
38 41
  // concordances are loaded
......
52 55
  }, [concordances]);
53 56

  
54 57
  const handleTabChange = (index: number) => {
58
  
55 59
    dispatch(setSelectedConcordance(index));
56 60
  };
57 61

  
......
65 69

  
66 70
  // item changes
67 71
  useEffect(() => {
68

  
69
    if (item && item.id) {
72
    if (!itemLoading && item && item.id) {
70 73
      if (selectedConcordance == 0) {
71 74
        dispatch(setConcordances(item.concordances));
72 75
      }
......
75 78
        dispatch(getItemNotes(item.id));
76 79
      }
77 80
    }
78

  
79 81
  }, [item]);
80 82

  
83
  // custom render Tab
84
  const _renderTabBar = (props: { navigationState: { routes: any[] } }) => {
85
    if (concordances && concordances.length > 0)
86
      return (
87
        <View h="16">
88
          <ScrollView horizontal={true} >
89
            <HStack>
90
              {props.navigationState.routes.map((route, i) => {
91
                return (
92
                  <Button size="lg" key={i}
93
                    variant={selectedConcordance == i ? "subtle" : "outline"}
94
                    rightIcon={<CircleIcon size="2" color={CertaintyWithColors[concordances[i].cert].color} />}
95
                    onPress={() => handleTabChange(i)}
96
                  >
97
                    {concordances[i].id}
98
                  </Button>
99
                );
100
              })}
101
            </HStack>
102

  
103
          </ScrollView>
104
        </View>
105
      )
106
    else {
107
      return <LoadingBox text={`Loading concordances...`}></LoadingBox>
108
    }
109
  };
110

  
81 111
  return (
82
    <TabView
83
      navigationState={{ index: selectedConcordance, routes }}
84
      renderScene={() => <ItemView item={item} notes={notes} />}
85
      onIndexChange={handleTabChange}
86
      initialLayout={{ width: layout.width }}
87
    />
112
    !concordances || concordances.length < 1 ?
113
      <LoadingBox text={`Loading item ${props.itemId}...`}></LoadingBox>
114
      :
115
      <TabView
116
        renderTabBar={_renderTabBar}
117
        navigationState={{ index: selectedConcordance, routes }}
118
        renderScene={() => (
119
          <ItemView item={item} notes={notes} itemLoading={itemLoading} notesLoading={notesLoading} />
120
        )}
121
        initialLayout={{ width: layout.width }}
122

  
123
        // prevent default action on index change
124
        onIndexChange={handleTabChange}
125

  
126
      />
88 127
  );
89 128
}
90 129

  
130

  
131

  
91 132
export default ItemViewPage;
src/stores/reducers/itemSlice.ts
2 2
import { getItem, getItemNotes } from "../actions/itemThunks"
3 3
import { Certainty, ItemViewState, Item } from "../../types/item";
4 4

  
5

  
5
// TODO set colors
6 6
export const CertaintyWithColors: Record<Certainty, { certainty: Certainty; color: string }> = {
7
    same_as: { certainty: Certainty.SameAs, color: "#000000" },
8
    high: { certainty: Certainty.High, color: "#FF0000" },
9
    medium: { certainty: Certainty.Medium, color: "#FFFF00" },
10
    low: { certainty: Certainty.Low, color: "#00FF00" },
11
    unknown : { certainty: Certainty.Unknown, color: "#000000"}
7
    same_as: { certainty: Certainty.SameAs, color: "light.300" },
8
    high: { certainty: Certainty.High, color: "success.300" },
9
    medium: { certainty: Certainty.Medium, color: "warning.300" },
10
    low: { certainty: Certainty.Low, color: "danger.300" },
11
    unknown : { certainty: Certainty.Unknown, color: "light.300"}
12 12
};
13 13

  
14 14
const initialState: ItemViewState = {
......
16 16
    selectedConcordance: 0,
17 17
    concordances: [],
18 18
    notes: [],
19
    lastError: ""
19
    lastError: "",
20
    itemLoading: true,
21
    notesLoading: true
20 22
}
21 23

  
22 24
export const itemSlice = createSlice({
......
62 64
                state.item.provenance = undefined;
63 65
                state.item.description = undefined;
64 66
            }
67

  
68
            state.itemLoading = false;
69
        })
70
        builder.addCase(getItem.pending, (state, action) => {
71
            state.itemLoading = true;
65 72
        })
66 73
        builder.addCase(getItem.rejected, (state, action) => {
74
            state.itemLoading = false;
67 75
            state.lastError = action.error.message
68 76
        })
69 77

  
70 78
        // getItemNotes
71 79
        builder.addCase(getItemNotes.fulfilled, (state, action) => {
80
            state.notesLoading = false;
72 81
            state.notes = action.payload.notes;
73 82
        })
83
        builder.addCase(getItemNotes.pending, (state, action) => {
84
            state.notesLoading = true;
85
        })
74 86
        builder.addCase(getItemNotes.rejected, (state, action) => {
87
            state.notesLoading = false;
75 88
            state.lastError = action.error.message
76 89
        })
77 90

  
src/types/item.ts
28 28

  
29 29
export type ItemViewState = {
30 30
    item: Item
31
    itemLoading: boolean
31 32
    concordances: Concordance[]
32 33
    selectedConcordance: number
33 34
    notes: Note[]
35
    notesLoading: boolean
34 36
    lastError?: string
35 37
}
36 38

  
......
46 48
    Low = "low",
47 49
    Unknown = "unknown"
48 50
}
51

  

Také k dispozici: Unified diff