mirror of
https://github.com/urosran/cally.git
synced 2025-11-26 08:24:55 +00:00
ui fixes
This commit is contained in:
19
assets/svgs/PlusIcon.tsx
Normal file
19
assets/svgs/PlusIcon.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import Svg, { SvgProps, Path } from "react-native-svg";
|
||||||
|
const PlusIcon = (props: SvgProps) => (
|
||||||
|
<Svg
|
||||||
|
width={props.width || 14}
|
||||||
|
height={props.height || 15}
|
||||||
|
fill="none"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Path
|
||||||
|
stroke="#fff"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M1 7.632h12m-6-6v12"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
export default PlusIcon;
|
||||||
@ -6,6 +6,7 @@ import HeaderTemplate from "@/components/shared/HeaderTemplate";
|
|||||||
import {Feather, MaterialIcons} from "@expo/vector-icons";
|
import {Feather, MaterialIcons} from "@expo/vector-icons";
|
||||||
import AddBrainDump from "./AddBrainDump";
|
import AddBrainDump from "./AddBrainDump";
|
||||||
import LinearGradient from "react-native-linear-gradient";
|
import LinearGradient from "react-native-linear-gradient";
|
||||||
|
import PlusIcon from "@/assets/svgs/PlusIcon";
|
||||||
|
|
||||||
const BrainDumpPage = () => {
|
const BrainDumpPage = () => {
|
||||||
const [searchText, setSearchText] = useState<string>("");
|
const [searchText, setSearchText] = useState<string>("");
|
||||||
@ -85,10 +86,10 @@ const BrainDumpPage = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View row centerV centerH>
|
<View row centerV centerH>
|
||||||
<MaterialIcons name="add" size={22} color={"white"}/>
|
<PlusIcon />
|
||||||
<Text
|
<Text
|
||||||
white
|
white
|
||||||
style={{fontSize: 16, fontFamily: "Manrope_600SemiBold"}}
|
style={{fontSize: 16, fontFamily: "Manrope_600SemiBold", marginLeft: 5}}
|
||||||
>
|
>
|
||||||
New
|
New
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import CalendarIcon from "@/assets/svgs/CalendarIcon";
|
|||||||
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
|
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
|
||||||
import {useSetAtom} from "jotai";
|
import {useSetAtom} from "jotai";
|
||||||
import {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms";
|
import {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms";
|
||||||
|
import PlusIcon from "@/assets/svgs/PlusIcon";
|
||||||
|
|
||||||
export const AddEventDialog = () => {
|
export const AddEventDialog = () => {
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
@ -50,8 +51,8 @@ export const AddEventDialog = () => {
|
|||||||
onPress={() => setShow(true)}
|
onPress={() => setShow(true)}
|
||||||
>
|
>
|
||||||
<View row centerV centerH>
|
<View row centerV centerH>
|
||||||
<MaterialIcons name="add" size={22} color={"white"}/>
|
<PlusIcon />
|
||||||
<Text white style={{fontSize: 16, fontFamily: 'Manrope_600SemiBold'}}>
|
<Text white style={{fontSize: 16, fontFamily: 'Manrope_600SemiBold', marginLeft: 5}}>
|
||||||
New
|
New
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import React from "react";
|
|||||||
import {Button, View,} from "react-native-ui-lib";
|
import {Button, View,} from "react-native-ui-lib";
|
||||||
import {useGroceryContext} from "@/contexts/GroceryContext";
|
import {useGroceryContext} from "@/contexts/GroceryContext";
|
||||||
import {FontAwesome6} from "@expo/vector-icons";
|
import {FontAwesome6} from "@expo/vector-icons";
|
||||||
|
import PlusIcon from "@/assets/svgs/PlusIcon";
|
||||||
|
|
||||||
const AddGroceryItem = () => {
|
const AddGroceryItem = () => {
|
||||||
const {setIsAddingGrocery} = useGroceryContext();
|
const {setIsAddingGrocery} = useGroceryContext();
|
||||||
@ -11,12 +12,12 @@ const AddGroceryItem = () => {
|
|||||||
<View
|
<View
|
||||||
row
|
row
|
||||||
spread
|
spread
|
||||||
paddingH-25
|
paddingH-20
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: 65,
|
bottom: 15,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: 60,
|
height: 53.26,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View style={styles.btnContainer} row>
|
<View style={styles.btnContainer} row>
|
||||||
@ -25,7 +26,7 @@ const AddGroceryItem = () => {
|
|||||||
backgroundColor="#fd1775"
|
backgroundColor="#fd1775"
|
||||||
label="Add item"
|
label="Add item"
|
||||||
text70L
|
text70L
|
||||||
iconSource={() => <FontAwesome6 name="add" size={18} color="white"/>}
|
iconSource={() => <PlusIcon />}
|
||||||
style={styles.finishShopBtn}
|
style={styles.finishShopBtn}
|
||||||
labelStyle={styles.addBtnLbl}
|
labelStyle={styles.addBtnLbl}
|
||||||
enableShadow
|
enableShadow
|
||||||
@ -69,6 +70,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
finishShopBtn: {
|
finishShopBtn: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
height: 53.26,
|
||||||
},
|
},
|
||||||
shoppingBtn: {
|
shoppingBtn: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React, {useEffect, useRef} from "react";
|
|||||||
import { GroceryCategory, useGroceryContext } from "@/contexts/GroceryContext";
|
import { GroceryCategory, useGroceryContext } from "@/contexts/GroceryContext";
|
||||||
import { Dropdown } from "react-native-element-dropdown";
|
import { Dropdown } from "react-native-element-dropdown";
|
||||||
import CloseXIcon from "@/assets/svgs/CloseXIcon";
|
import CloseXIcon from "@/assets/svgs/CloseXIcon";
|
||||||
import {StyleSheet} from "react-native";
|
import { findNodeHandle, StyleSheet, UIManager } from "react-native";
|
||||||
import DropdownIcon from "@/assets/svgs/DropdownIcon";
|
import DropdownIcon from "@/assets/svgs/DropdownIcon";
|
||||||
import { AntDesign } from "@expo/vector-icons";
|
import { AntDesign } from "@expo/vector-icons";
|
||||||
|
|
||||||
@ -18,10 +18,37 @@ interface IEditGrocery {
|
|||||||
handleEditSubmit?: Function;
|
handleEditSubmit?: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EditGroceryItem = ({
|
||||||
const EditGroceryItem = ({editGrocery}: { editGrocery: IEditGrocery }) => {
|
editGrocery,
|
||||||
|
onInputFocus,
|
||||||
|
}: {
|
||||||
|
editGrocery: IEditGrocery;
|
||||||
|
onInputFocus: (y: number) => void;
|
||||||
|
}) => {
|
||||||
const { fuzzyMatchGroceryCategory } = useGroceryContext();
|
const { fuzzyMatchGroceryCategory } = useGroceryContext();
|
||||||
const inputRef = useRef<TextFieldRef>(null);
|
const inputRef = useRef<TextFieldRef>(null);
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
|
||||||
|
const handleFocus = () => {
|
||||||
|
if (containerRef.current) {
|
||||||
|
const handle = findNodeHandle(containerRef.current);
|
||||||
|
if (handle) {
|
||||||
|
UIManager.measure(
|
||||||
|
handle,
|
||||||
|
(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
pageX: number,
|
||||||
|
pageY: number
|
||||||
|
) => {
|
||||||
|
onInputFocus(pageY);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const groceryCategoryOptions = Object.values(GroceryCategory).map(
|
const groceryCategoryOptions = Object.values(GroceryCategory).map(
|
||||||
(category) => ({
|
(category) => ({
|
||||||
@ -31,8 +58,8 @@ const EditGroceryItem = ({editGrocery}: { editGrocery: IEditGrocery }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
inputRef?.current?.blur()
|
inputRef?.current?.blur();
|
||||||
console.log("CALLLLLL")
|
console.log("CALLLLLL");
|
||||||
if (editGrocery.setSubmit) {
|
if (editGrocery.setSubmit) {
|
||||||
editGrocery.setSubmit(true);
|
editGrocery.setSubmit(true);
|
||||||
}
|
}
|
||||||
@ -46,7 +73,7 @@ const EditGroceryItem = ({editGrocery}: { editGrocery: IEditGrocery }) => {
|
|||||||
if (editGrocery.closeEdit) {
|
if (editGrocery.closeEdit) {
|
||||||
editGrocery.closeEdit();
|
editGrocery.closeEdit();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inputRef.current) {
|
if (inputRef.current) {
|
||||||
@ -58,6 +85,7 @@ const EditGroceryItem = ({editGrocery}: { editGrocery: IEditGrocery }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
ref={containerRef}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@ -71,6 +99,7 @@ const EditGroceryItem = ({editGrocery}: { editGrocery: IEditGrocery }) => {
|
|||||||
<TextField
|
<TextField
|
||||||
text70T
|
text70T
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
onFocus={handleFocus}
|
||||||
placeholder="Grocery"
|
placeholder="Grocery"
|
||||||
value={editGrocery.title}
|
value={editGrocery.title}
|
||||||
onSubmitEditing={handleSubmit}
|
onSubmitEditing={handleSubmit}
|
||||||
@ -108,12 +137,14 @@ const EditGroceryItem = ({editGrocery}: { editGrocery: IEditGrocery }) => {
|
|||||||
style={{ marginTop: 15 }}
|
style={{ marginTop: 15 }}
|
||||||
data={groceryCategoryOptions}
|
data={groceryCategoryOptions}
|
||||||
placeholder="Select grocery category"
|
placeholder="Select grocery category"
|
||||||
placeholderStyle={{color: "#a2a2a2", fontFamily: "Manrope_500Medium", fontSize: 13.2}}
|
placeholderStyle={{
|
||||||
|
color: "#a2a2a2",
|
||||||
|
fontFamily: "Manrope_500Medium",
|
||||||
|
fontSize: 13.2,
|
||||||
|
}}
|
||||||
labelField="label"
|
labelField="label"
|
||||||
valueField="value"
|
valueField="value"
|
||||||
value={
|
value={editGrocery.category}
|
||||||
editGrocery.category
|
|
||||||
}
|
|
||||||
iconColor="white"
|
iconColor="white"
|
||||||
activeColor={"#fd1775"}
|
activeColor={"#fd1775"}
|
||||||
containerStyle={styles.dropdownStyle}
|
containerStyle={styles.dropdownStyle}
|
||||||
@ -121,8 +152,14 @@ const EditGroceryItem = ({editGrocery}: { editGrocery: IEditGrocery }) => {
|
|||||||
itemContainerStyle={styles.itemStyle}
|
itemContainerStyle={styles.itemStyle}
|
||||||
selectedTextStyle={styles.selectedText}
|
selectedTextStyle={styles.selectedText}
|
||||||
renderLeftIcon={() => (
|
renderLeftIcon={() => (
|
||||||
<DropdownIcon style={{marginRight: 8}}
|
<DropdownIcon
|
||||||
color={editGrocery.category == GroceryCategory.None ? "#7b7b7b" : "#fd1775"}/>
|
style={{ marginRight: 8 }}
|
||||||
|
color={
|
||||||
|
editGrocery.category == GroceryCategory.None
|
||||||
|
? "#7b7b7b"
|
||||||
|
: "#fd1775"
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
renderItem={(item) => {
|
renderItem={(item) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -13,9 +13,11 @@ import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
|
|||||||
const GroceryItem = ({
|
const GroceryItem = ({
|
||||||
item,
|
item,
|
||||||
handleItemApproved,
|
handleItemApproved,
|
||||||
|
onInputFocus,
|
||||||
}: {
|
}: {
|
||||||
item: IGrocery;
|
item: IGrocery;
|
||||||
handleItemApproved: (id: string, changes: Partial<IGrocery>) => void;
|
handleItemApproved: (id: string, changes: Partial<IGrocery>) => void;
|
||||||
|
onInputFocus: (y: number) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { updateGroceryItem } = useGroceryContext();
|
const { updateGroceryItem } = useGroceryContext();
|
||||||
const { profileData } = useAuthContext();
|
const { profileData } = useAuthContext();
|
||||||
@ -24,12 +26,14 @@ const GroceryItem = ({
|
|||||||
const [openFreqEdit, setOpenFreqEdit] = useState<boolean>(false);
|
const [openFreqEdit, setOpenFreqEdit] = useState<boolean>(false);
|
||||||
const [isEditingTitle, setIsEditingTitle] = useState<boolean>(false);
|
const [isEditingTitle, setIsEditingTitle] = useState<boolean>(false);
|
||||||
const [newTitle, setNewTitle] = useState<string>(item.title ?? "");
|
const [newTitle, setNewTitle] = useState<string>(item.title ?? "");
|
||||||
const [category, setCategory] = useState<GroceryCategory>(item.category ?? GroceryCategory.None);
|
const [category, setCategory] = useState<GroceryCategory>(
|
||||||
|
item.category ?? GroceryCategory.None
|
||||||
|
);
|
||||||
const [itemCreator, setItemCreator] = useState<UserProfile>(null);
|
const [itemCreator, setItemCreator] = useState<UserProfile>(null);
|
||||||
|
|
||||||
const closeEdit = () => {
|
const closeEdit = () => {
|
||||||
setIsEditingTitle(false);
|
setIsEditingTitle(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleTitleChange = (newTitle: string) => {
|
const handleTitleChange = (newTitle: string) => {
|
||||||
updateGroceryItem({ id: item?.id, title: newTitle });
|
updateGroceryItem({ id: item?.id, title: newTitle });
|
||||||
@ -82,7 +86,7 @@ const GroceryItem = ({
|
|||||||
setOpenFreqEdit(false);
|
setOpenFreqEdit(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{isEditingTitle ?
|
{isEditingTitle ? (
|
||||||
<EditGroceryItem
|
<EditGroceryItem
|
||||||
editGrocery={{
|
editGrocery={{
|
||||||
id: item.id,
|
id: item.id,
|
||||||
@ -93,23 +97,26 @@ const GroceryItem = ({
|
|||||||
closeEdit: closeEdit,
|
closeEdit: closeEdit,
|
||||||
handleEditSubmit: updateGroceryItem,
|
handleEditSubmit: updateGroceryItem,
|
||||||
}}
|
}}
|
||||||
/> :
|
onInputFocus={onInputFocus}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<View>
|
<View>
|
||||||
{isParent ?
|
{isParent ? (
|
||||||
<TouchableOpacity onPress={() => setIsEditingTitle(true)}>
|
<TouchableOpacity onPress={() => setIsEditingTitle(true)}>
|
||||||
<Text text70T black style={styles.title}>
|
<Text text70T black style={styles.title}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity> :
|
</TouchableOpacity>
|
||||||
|
) : (
|
||||||
<Text text70T black style={styles.title}>
|
<Text text70T black style={styles.title}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
)}
|
||||||
</View>
|
</View>
|
||||||
}
|
)}
|
||||||
{!item.approved ? (
|
{!item.approved ? (
|
||||||
<View row centerV marginB-10>
|
<View row centerV marginB-10>
|
||||||
{isParent &&
|
{isParent && (
|
||||||
<>
|
<>
|
||||||
<AntDesign
|
<AntDesign
|
||||||
name="check"
|
name="check"
|
||||||
@ -118,18 +125,28 @@ const GroceryItem = ({
|
|||||||
color: "green",
|
color: "green",
|
||||||
marginRight: 15,
|
marginRight: 15,
|
||||||
}}
|
}}
|
||||||
onPress={isParent ? () => handleItemApproved(item.id, { approved: true }) : null}
|
onPress={
|
||||||
|
isParent
|
||||||
|
? () => handleItemApproved(item.id, { approved: true })
|
||||||
|
: null
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<AntDesign
|
<AntDesign
|
||||||
name="close"
|
name="close"
|
||||||
size={24}
|
size={24}
|
||||||
style={{ color: "red" }}
|
style={{ color: "red" }}
|
||||||
onPress={isParent ? () => handleItemApproved(item.id, { approved: false }) : null}
|
onPress={
|
||||||
|
isParent
|
||||||
|
? () => handleItemApproved(item.id, { approved: false })
|
||||||
|
: null
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</>}
|
</>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
!isEditingTitle && isParent && (
|
!isEditingTitle &&
|
||||||
|
isParent && (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={item.bought}
|
value={item.bought}
|
||||||
containerStyle={[styles.checkbox, { borderRadius: 50 }]}
|
containerStyle={[styles.checkbox, { borderRadius: 50 }]}
|
||||||
@ -150,7 +167,8 @@ const GroceryItem = ({
|
|||||||
<View height={0.7} backgroundColor="#e7e7e7" width={"98%"} />
|
<View height={0.7} backgroundColor="#e7e7e7" width={"98%"} />
|
||||||
</View>
|
</View>
|
||||||
<View paddingL-0 paddingT-12 flexS row centerV>
|
<View paddingL-0 paddingT-12 flexS row centerV>
|
||||||
{profileData?.pfp ? <ImageBackground
|
{profileData?.pfp ? (
|
||||||
|
<ImageBackground
|
||||||
source={require("../../../assets/images/child-picture.png")}
|
source={require("../../../assets/images/child-picture.png")}
|
||||||
style={{
|
style={{
|
||||||
height: 24.64,
|
height: 24.64,
|
||||||
@ -158,13 +176,14 @@ const GroceryItem = ({
|
|||||||
borderRadius: 22,
|
borderRadius: 22,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
/> :
|
/>
|
||||||
|
) : (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
position: "relative",
|
position: "relative",
|
||||||
width: 24.64,
|
width: 24.64,
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
marginRight: 4
|
marginRight: 4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
@ -184,10 +203,13 @@ const GroceryItem = ({
|
|||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{itemCreator ? getInitials(itemCreator.firstName, itemCreator.lastName) : ""}
|
{itemCreator
|
||||||
|
? getInitials(itemCreator.firstName, itemCreator.lastName)
|
||||||
|
: ""}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>}
|
</View>
|
||||||
|
)}
|
||||||
<Text color="#858585" style={styles.authorTxt}>
|
<Text color="#858585" style={styles.authorTxt}>
|
||||||
Requested by {itemCreator?.firstName}
|
Requested by {itemCreator?.firstName}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
|
|||||||
import {IGrocery} from "@/hooks/firebase/types/groceryData";
|
import {IGrocery} from "@/hooks/firebase/types/groceryData";
|
||||||
import AddPersonIcon from "@/assets/svgs/AddPersonIcon";
|
import AddPersonIcon from "@/assets/svgs/AddPersonIcon";
|
||||||
|
|
||||||
const GroceryList = () => {
|
const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
|
||||||
const {
|
const {
|
||||||
groceries,
|
groceries,
|
||||||
updateGroceryItem,
|
updateGroceryItem,
|
||||||
@ -169,6 +169,7 @@ const GroceryList = () => {
|
|||||||
handleItemApproved={(id, changes) =>
|
handleItemApproved={(id, changes) =>
|
||||||
updateGroceryItem({...changes, id: id})
|
updateGroceryItem({...changes, id: id})
|
||||||
}
|
}
|
||||||
|
onInputFocus={onInputFocus}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
keyExtractor={(item) => item.id.toString()}
|
keyExtractor={(item) => item.id.toString()}
|
||||||
@ -230,6 +231,7 @@ const GroceryList = () => {
|
|||||||
setSubmit: setSubmitted,
|
setSubmit: setSubmitted,
|
||||||
closeEdit: () => setIsAddingGrocery(false)
|
closeEdit: () => setIsAddingGrocery(false)
|
||||||
}}
|
}}
|
||||||
|
onInputFocus={onInputFocus}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@ -254,6 +256,7 @@ const GroceryList = () => {
|
|||||||
handleItemApproved={(id, changes) =>
|
handleItemApproved={(id, changes) =>
|
||||||
updateGroceryItem({...changes, id: id})
|
updateGroceryItem({...changes, id: id})
|
||||||
}
|
}
|
||||||
|
onInputFocus={onInputFocus}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,41 +1,78 @@
|
|||||||
import { Dimensions, ScrollView } from "react-native";
|
import { Dimensions, ScrollView, Keyboard, Platform } from "react-native";
|
||||||
import { View } from "react-native-ui-lib";
|
import { View } from "react-native-ui-lib";
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import AddGroceryItem from "./AddGroceryItem";
|
import AddGroceryItem from "./AddGroceryItem";
|
||||||
import GroceryList from "./GroceryList";
|
import GroceryList from "./GroceryList";
|
||||||
import { useGroceryContext } from "@/contexts/GroceryContext";
|
import { useGroceryContext } from "@/contexts/GroceryContext";
|
||||||
|
|
||||||
const GroceryWrapper = () => {
|
const GroceryWrapper = () => {
|
||||||
const { isAddingGrocery } = useGroceryContext();
|
const { isAddingGrocery } = useGroceryContext();
|
||||||
const scrollViewRef = useRef<ScrollView>(null); // Reference to the ScrollView
|
const scrollViewRef = useRef<ScrollView>(null);
|
||||||
|
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const keyboardWillShowListener = Keyboard.addListener(
|
||||||
|
Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow',
|
||||||
|
(e) => {
|
||||||
|
setKeyboardHeight(e.endCoordinates.height);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const keyboardWillHideListener = Keyboard.addListener(
|
||||||
|
Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide',
|
||||||
|
() => {
|
||||||
|
setKeyboardHeight(0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
keyboardWillShowListener.remove();
|
||||||
|
keyboardWillHideListener.remove();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isAddingGrocery && scrollViewRef.current) {
|
if (isAddingGrocery && scrollViewRef.current) {
|
||||||
scrollViewRef.current.scrollTo({
|
scrollViewRef.current.scrollTo({
|
||||||
y: 400, // Adjust this value to scroll a bit down (100 is an example)
|
y: 400,
|
||||||
animated: true,
|
animated: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [isAddingGrocery]);
|
}, [isAddingGrocery]);
|
||||||
|
|
||||||
|
const handleInputFocus = (y: number) => {
|
||||||
|
if (scrollViewRef.current) {
|
||||||
|
// Get the window height
|
||||||
|
const windowHeight = Dimensions.get('window').height;
|
||||||
|
// Calculate the space we want to leave at the top
|
||||||
|
const topSpacing = 20;
|
||||||
|
|
||||||
|
// Calculate the target scroll position:
|
||||||
|
// y (position of input) - topSpacing (space we want at top)
|
||||||
|
// if keyboard is shown, we need to account for its height
|
||||||
|
const scrollPosition = Math.max(0, y - topSpacing);
|
||||||
|
|
||||||
|
scrollViewRef.current.scrollTo({
|
||||||
|
y: scrollPosition,
|
||||||
|
animated: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View height={Dimensions.get("window").height}>
|
<>
|
||||||
<View>
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
ref={scrollViewRef}
|
ref={scrollViewRef}
|
||||||
automaticallyAdjustKeyboardInsets={true}
|
automaticallyAdjustKeyboardInsets={true}
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
>
|
>
|
||||||
<View height={"100%"}>
|
<View marginB-60>
|
||||||
<View marginB-115>
|
<GroceryList onInputFocus={handleInputFocus} />
|
||||||
<GroceryList />
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
|
||||||
{!isAddingGrocery && <AddGroceryItem />}
|
{!isAddingGrocery && <AddGroceryItem />}
|
||||||
</View>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,38 +1,46 @@
|
|||||||
import { StyleSheet } from "react-native";
|
import { Dimensions, StyleSheet } from "react-native";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Button, ButtonSize, Text, View } from "react-native-ui-lib";
|
import { Button, ButtonSize, Text, View } from "react-native-ui-lib";
|
||||||
import { AntDesign } from "@expo/vector-icons";
|
import { AntDesign } from "@expo/vector-icons";
|
||||||
import LinearGradient from "react-native-linear-gradient";
|
import LinearGradient from "react-native-linear-gradient";
|
||||||
import AddChoreDialog from "./AddChoreDialog";
|
import AddChoreDialog from "./AddChoreDialog";
|
||||||
|
import PlusIcon from "@/assets/svgs/PlusIcon";
|
||||||
|
|
||||||
const AddChore = () => {
|
const AddChore = () => {
|
||||||
const [isVisible, setIsVisible] = useState<boolean>(false);
|
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LinearGradient
|
<View
|
||||||
colors={["#f9f8f700", "#f9f8f7", "#f9f8f700"]}
|
row
|
||||||
locations={[0, 0.5, 1]}
|
spread
|
||||||
style={styles.gradient}
|
paddingH-20
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 15,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<View style={styles.buttonContainer}>
|
<View style={styles.buttonContainer}>
|
||||||
<Button
|
<Button
|
||||||
marginH-25
|
marginH-20
|
||||||
size={ButtonSize.large}
|
size={ButtonSize.large}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
onPress={() => setIsVisible(!isVisible)}
|
onPress={() => setIsVisible(!isVisible)}
|
||||||
>
|
>
|
||||||
<AntDesign name="plus" size={24} color="white" />
|
<PlusIcon />
|
||||||
<Text
|
<Text
|
||||||
white
|
white
|
||||||
style={{ fontFamily: "Manrope_600SemiBold", fontSize: 15 }}
|
style={{ fontFamily: "Manrope_600SemiBold", fontSize: 15 }}
|
||||||
marginL-10
|
marginL-5
|
||||||
>
|
>
|
||||||
Create new to do
|
Create new to do
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
{isVisible && <AddChoreDialog isVisible={isVisible} setIsVisible={setIsVisible} />}
|
{isVisible && (
|
||||||
</LinearGradient>
|
<AddChoreDialog isVisible={isVisible} setIsVisible={setIsVisible} />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,8 +61,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
backgroundColor: "rgb(253, 23, 117)",
|
backgroundColor: "rgb(253, 23, 117)",
|
||||||
paddingVertical: 15,
|
height: 53.26,
|
||||||
paddingHorizontal: 30,
|
|
||||||
borderRadius: 30,
|
borderRadius: 30,
|
||||||
width: 335,
|
width: 335,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -17,8 +17,13 @@ import AddChoreDialog from "@/components/pages/todos/AddChoreDialog";
|
|||||||
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
|
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
|
||||||
import RepeatIcon from "@/assets/svgs/RepeatIcon";
|
import RepeatIcon from "@/assets/svgs/RepeatIcon";
|
||||||
import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
|
import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
|
||||||
const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => {
|
const ToDoItem = (props: {
|
||||||
|
item: IToDo;
|
||||||
|
isSettings?: boolean;
|
||||||
|
is7Days?: boolean;
|
||||||
|
}) => {
|
||||||
const { updateToDo } = useToDosContext();
|
const { updateToDo } = useToDosContext();
|
||||||
const { data: members } = useGetFamilyMembers();
|
const { data: members } = useGetFamilyMembers();
|
||||||
const { profileData } = useAuthContext();
|
const { profileData } = useAuthContext();
|
||||||
@ -55,6 +60,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
key={props.item.id}
|
||||||
centerV
|
centerV
|
||||||
paddingV-10
|
paddingV-10
|
||||||
paddingH-13
|
paddingH-13
|
||||||
@ -154,6 +160,9 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => {
|
|||||||
return props.item.repeatType;
|
return props.item.repeatType;
|
||||||
}
|
}
|
||||||
})()}
|
})()}
|
||||||
|
{props.item.date &&
|
||||||
|
props.is7Days &&
|
||||||
|
" / " + format(props.item.date, "EEEE")}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -14,8 +14,10 @@ import { IToDo } from "@/hooks/firebase/types/todoData";
|
|||||||
|
|
||||||
const groupToDosByDate = (toDos: IToDo[]) => {
|
const groupToDosByDate = (toDos: IToDo[]) => {
|
||||||
let sortedTodos = toDos.sort((a, b) => a.date - b.date);
|
let sortedTodos = toDos.sort((a, b) => a.date - b.date);
|
||||||
return sortedTodos.reduce((groups, toDo) => {
|
return sortedTodos.reduce(
|
||||||
|
(groups, toDo) => {
|
||||||
let dateKey;
|
let dateKey;
|
||||||
|
let subDateKey;
|
||||||
|
|
||||||
const isNext7Days = (date: Date) => {
|
const isNext7Days = (date: Date) => {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
@ -24,7 +26,10 @@ const groupToDosByDate = (toDos: IToDo[]) => {
|
|||||||
|
|
||||||
const isNext30Days = (date: Date) => {
|
const isNext30Days = (date: Date) => {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
return isWithinInterval(date, { start: today, end: addDays(today, 30) });
|
return isWithinInterval(date, {
|
||||||
|
start: today,
|
||||||
|
end: addDays(today, 30),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (toDo.date === null) {
|
if (toDo.date === null) {
|
||||||
@ -37,17 +42,36 @@ const groupToDosByDate = (toDos: IToDo[]) => {
|
|||||||
dateKey = "Next 7 Days";
|
dateKey = "Next 7 Days";
|
||||||
} else if (isNext30Days(toDo.date)) {
|
} else if (isNext30Days(toDo.date)) {
|
||||||
dateKey = "Next 30 Days";
|
dateKey = "Next 30 Days";
|
||||||
|
subDateKey = format(toDo.date, "MMM d");
|
||||||
} else {
|
} else {
|
||||||
dateKey = format(toDo.date, "EEE MMM dd");
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!groups[dateKey]) {
|
if (!groups[dateKey]) {
|
||||||
groups[dateKey] = [];
|
groups[dateKey] = {
|
||||||
|
items: [],
|
||||||
|
subgroups: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dateKey === "Next 30 Days" && subDateKey) {
|
||||||
|
if (!groups[dateKey].subgroups[subDateKey]) {
|
||||||
|
groups[dateKey].subgroups[subDateKey] = [];
|
||||||
|
}
|
||||||
|
groups[dateKey].subgroups[subDateKey].push(toDo);
|
||||||
|
} else {
|
||||||
|
groups[dateKey].items.push(toDo);
|
||||||
}
|
}
|
||||||
|
|
||||||
groups[dateKey].push(toDo);
|
|
||||||
return groups;
|
return groups;
|
||||||
}, {} as { [key: string]: IToDo[] });
|
},
|
||||||
|
{} as {
|
||||||
|
[key: string]: {
|
||||||
|
items: IToDo[];
|
||||||
|
subgroups: { [key: string]: IToDo[] };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ToDosList = ({ isSettings }: { isSettings?: boolean }) => {
|
const ToDosList = ({ isSettings }: { isSettings?: boolean }) => {
|
||||||
@ -67,59 +91,95 @@ const ToDosList = ({ isSettings }: { isSettings?: boolean }) => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const noDateToDos = groupedToDos["No Date"] || [];
|
const noDateToDos = groupedToDos["No Date"]?.items || [];
|
||||||
const datedToDos = Object.keys(groupedToDos).filter(
|
const datedToDos = Object.keys(groupedToDos).filter(
|
||||||
(key) => key !== "No Date"
|
(key) => key !== "No Date"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const renderTodoGroup = (dateKey: string) => {
|
||||||
|
const isExpanded = expandedGroups[dateKey] || false;
|
||||||
|
|
||||||
|
if (dateKey === "Next 30 Days") {
|
||||||
|
const subgroups = Object.entries(groupedToDos[dateKey].subgroups).sort(
|
||||||
|
([dateA], [dateB]) => {
|
||||||
|
const dateAObj = new Date(dateA);
|
||||||
|
const dateBObj = new Date(dateB);
|
||||||
|
return dateAObj.getTime() - dateBObj.getTime();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View marginB-402>
|
<View key={dateKey}>
|
||||||
{noDateToDos.length > 0 && (
|
<TouchableOpacity
|
||||||
<View key="No Date">
|
onPress={() => toggleExpand(dateKey)}
|
||||||
<View row spread paddingH-19 marginB-12>
|
|
||||||
<Text
|
|
||||||
text70
|
|
||||||
style={{
|
style={{
|
||||||
fontWeight: "bold",
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingHorizontal: 0,
|
||||||
|
marginBottom: 4,
|
||||||
|
marginTop: 15,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Unscheduled
|
<Text
|
||||||
|
style={{
|
||||||
|
fontFamily: "Manrope_700Bold",
|
||||||
|
fontSize: 15,
|
||||||
|
color: "#484848",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{dateKey}
|
||||||
</Text>
|
</Text>
|
||||||
{!expandNoDate && (
|
|
||||||
<AntDesign
|
<AntDesign
|
||||||
name="caretright"
|
name={isExpanded ? "caretdown" : "caretright"}
|
||||||
size={24}
|
size={24}
|
||||||
color="#fd1575"
|
color="#fd1575"
|
||||||
onPress={() => {
|
|
||||||
setExpandNoDate(!expandNoDate);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
</TouchableOpacity>
|
||||||
{expandNoDate && (
|
|
||||||
<AntDesign
|
{isExpanded &&
|
||||||
name="caretdown"
|
subgroups.map(([subDate, items]) => {
|
||||||
size={24}
|
const sortedItems = [
|
||||||
color="#fd1575"
|
...items.filter((toDo) => !toDo.done),
|
||||||
onPress={() => {
|
...items.filter((toDo) => toDo.done),
|
||||||
setExpandNoDate(!expandNoDate);
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key={subDate} marginT-15>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
marginBottom: 8,
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
)}
|
<Text
|
||||||
|
style={{
|
||||||
|
fontFamily: "Manrope_600SemiBold",
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#919191",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{subDate}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
{expandNoDate &&
|
|
||||||
noDateToDos
|
{sortedItems.map((item) => (
|
||||||
.sort((a, b) => Number(a.done) - Number(b.done))
|
<ToDoItem
|
||||||
.map((item) => (
|
isSettings={isSettings}
|
||||||
<ToDoItem isSettings={isSettings} key={item.id} item={item} />
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
is7Days={false}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
)}
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{datedToDos.map((dateKey) => {
|
|
||||||
const isExpanded = expandedGroups[dateKey] || false;
|
|
||||||
const sortedToDos = [
|
const sortedToDos = [
|
||||||
...groupedToDos[dateKey].filter((toDo) => !toDo.done),
|
...groupedToDos[dateKey].items.filter((toDo) => !toDo.done),
|
||||||
...groupedToDos[dateKey].filter((toDo) => toDo.done),
|
...groupedToDos[dateKey].items.filter((toDo) => toDo.done),
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -144,21 +204,58 @@ const ToDosList = ({ isSettings }: { isSettings?: boolean }) => {
|
|||||||
>
|
>
|
||||||
{dateKey}
|
{dateKey}
|
||||||
</Text>
|
</Text>
|
||||||
{!isExpanded && (
|
<AntDesign
|
||||||
<AntDesign name="caretright" size={24} color="#fd1575" />
|
name={isExpanded ? "caretdown" : "caretright"}
|
||||||
)}
|
size={24}
|
||||||
{isExpanded && (
|
color="#fd1575"
|
||||||
<AntDesign name="caretdown" size={24} color="#fd1575" />
|
/>
|
||||||
)}
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
{isExpanded &&
|
{isExpanded &&
|
||||||
sortedToDos.map((item) => (
|
sortedToDos.map((item) => (
|
||||||
<ToDoItem isSettings={isSettings} key={item.id} item={item} />
|
<ToDoItem
|
||||||
|
isSettings={isSettings}
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
is7Days={dateKey === "Next 7 Days"}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
})}
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View marginB-402>
|
||||||
|
{noDateToDos.length > 0 && (
|
||||||
|
<View key="No Date">
|
||||||
|
<View row spread paddingH-19 marginB-12>
|
||||||
|
<Text
|
||||||
|
text70
|
||||||
|
style={{
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Unscheduled
|
||||||
|
</Text>
|
||||||
|
<AntDesign
|
||||||
|
name={expandNoDate ? "caretdown" : "caretright"}
|
||||||
|
size={24}
|
||||||
|
color="#fd1575"
|
||||||
|
onPress={() => {
|
||||||
|
setExpandNoDate(!expandNoDate);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{expandNoDate &&
|
||||||
|
noDateToDos
|
||||||
|
.sort((a, b) => Number(a.done) - Number(b.done))
|
||||||
|
.map((item) => (
|
||||||
|
<ToDoItem isSettings={isSettings} key={item.id} item={item} />
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{datedToDos.map(renderTodoGroup)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -32,7 +32,6 @@ const ToDosPage = () => {
|
|||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
paddingH-25
|
paddingH-25
|
||||||
backgroundColor="#f9f8f7"
|
|
||||||
height={"100%"}
|
height={"100%"}
|
||||||
width={width}
|
width={width}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user