diff --git a/android/app/build.gradle b/android/app/build.gradle
index e9c558e..50a97f3 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -99,8 +99,8 @@ android {
applicationId "com.myclosetx"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 3
- versionName "1.0.4"
+ versionCode 4
+ versionName "1.0.5"
}
splits {
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index 4b185bc..6d5b6b0 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
+
+
@@ -7,6 +7,11 @@
+
+
+
+
+
{
-// Alert.alert("Color Selected", `You selected color: ${color}`);
-// };
+import { localization } from "../utils/localization";
+import { useSelector } from "react-redux";
+import { RootState } from "../redux/store";
const ColorFilter = ({
colors,
@@ -21,8 +14,6 @@ const ColorFilter = ({
colors: string[];
setColors: Dispatch>;
}) => {
- // const isDarkMode = useColorScheme() === "dark";
-
const basicColors: { [key: string]: string } = {
yellow: "#FEFD00",
yellow_Orange: "#F8A900",
@@ -41,11 +32,12 @@ const ColorFilter = ({
gray: "#808080",
unknown: "",
};
+ const storedSettings = useSelector((state: RootState) => state.settings);
return (
- Filter By Color
+ {localization.FilterByColor[storedSettings.language]}
{Object.keys(basicColors).map((name) => (
diff --git a/package.json b/package.json
index 3a7f994..2397e9d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "closet-archive",
- "version": "1.0.4",
+ "version": "1.0.5",
"private": true,
"scripts": {
"android": "react-native run-android",
@@ -55,7 +55,12 @@
"expo-application",
"expo-constants",
"expo-font",
- "expo-keep-awake"
+ "expo-splash-screen",
+ "expo-notifications",
+ "expo-updates",
+ "expo-location",
+ "expo-web-browser",
+ "expo-av"
]
}
},
diff --git a/redux/settingsSlice.ts b/redux/settingsSlice.ts
index bcce8ef..fe01239 100644
--- a/redux/settingsSlice.ts
+++ b/redux/settingsSlice.ts
@@ -5,6 +5,7 @@ export type SettingsType = {
name: string;
laundryNumber: number;
enableLaundry: boolean;
+ enableReminder: boolean;
appVer: string;
};
const initialState: SettingsType = {
@@ -12,6 +13,7 @@ const initialState: SettingsType = {
name: "User",
laundryNumber: 5,
enableLaundry: true,
+ enableReminder: true,
appVer: "1.0.1",
};
const settingsSlice = createSlice({
@@ -30,10 +32,15 @@ const settingsSlice = createSlice({
setEnableLaundry: (state, action) => {
state.enableLaundry = action.payload.enableLaundry;
},
+ setReminder: (state, action) => {
+ state.enableReminder = action.payload.enableReminder;
+ },
importSettings: (state, action) => {
state.language = action.payload.language;
state.laundryNumber = action.payload.laundryNumber;
state.name = action.payload.name;
+ state.enableLaundry = action.payload.enableLaundry;
+ state.enableReminder = action.payload.enableReminder;
},
setAppVer: (state, action) => {
state.appVer = action.payload.appVer;
@@ -45,6 +52,7 @@ export const {
userNameSetter,
laundryNumberSetter,
setEnableLaundry,
+ setReminder,
importSettings,
setAppVer,
} = settingsSlice.actions;
diff --git a/routers/stack.tsx b/routers/stack.tsx
index ab649ff..fcc856e 100644
--- a/routers/stack.tsx
+++ b/routers/stack.tsx
@@ -9,6 +9,8 @@ import { EventLogForm } from "../screens/stackNav/EventLogForm";
import { CollectionForm } from "../screens/stackNav/CollectionForm";
import { Settings } from "../screens/stackNav/Settings";
import { ClosetInfo } from "../screens/stackNav/ClosetInfo";
+import { ModeScreen } from "../screens/stackNav/ModeScreen";
+import { BulkModeForm } from "../screens/stackNav/BulkModeForm";
export default function Navigator() {
const Stack = createNativeStackNavigator();
return (
@@ -16,7 +18,7 @@ export default function Navigator() {
+
+
{
+ const { selectedCategory } = route.params;
+ const [prefix, setPrefix] = useState("");
+ const [imageNum, setImageNum] = useState(2);
+ const [openType, setOpenType] = useState(false);
+ const [openCollection, setOpenCollection] = useState(false);
+ const colorScheme = String(useColorScheme()?.toUpperCase()) as ThemeNameType;
+ const [collection, setCollection] = useState([]);
+ const [type, setType] = useState("");
+ const dispatch = useDispatch();
+ const [errorsList, setErrorsList] = useState([]);
+ // const [images, setImages] = useState<[]>([]);
+
+ const storedSettings = useSelector((state: RootState) => state.settings);
+ const collectionState = useSelector(
+ (state: RootState) => state.itemsList.collectionTags,
+ );
+
+ const handleImagePicker = async (type: number, createItem: any) => {
+ if (type === 0) {
+ try {
+ const response = await launchImageLibrary({
+ mediaType: "photo",
+ selectionLimit: imageNum,
+ });
+
+ if (response.didCancel) {
+ console.log("Image picker cancelled");
+ return;
+ } else if (response.errorCode) {
+ console.log("Image picker error: ", response.errorMessage);
+ return;
+ }
+
+ const newImages = [];
+ for (let i = 0; i < response.assets?.length; i++) {
+ newImages.push(response.assets[i]?.uri);
+ }
+ createItem(newImages); // Update the state with the new array
+ } catch (error) {
+ console.error("Image picker error:", error);
+ }
+ } else {
+ const newImages = [];
+ for (let i = 0; i < imageNum; i++) {
+ try {
+ await launchCamera(
+ {
+ mediaType: "photo",
+ },
+ (response) => {
+ if (response.didCancel) {
+ console.log("Image picker cancelled");
+ } else if (response.errorCode) {
+ console.log("Image picker error: ", response.errorMessage);
+ return;
+ } else {
+ newImages.push(response.assets[0]?.uri);
+ }
+ },
+ );
+ } catch (error) {
+ console.log("permission error", error);
+ }
+ }
+ createItem(newImages);
+ }
+ };
+ function addItemHandler(mode: number) {
+ const errors = [];
+
+ if (prefix.length <= 0) {
+ errors.push("Please enter item prefix");
+ }
+ if (prefix.length > 20) {
+ errors.push("Please enter a name within 20 characters");
+ }
+
+ if (errors.length > 0) {
+ setErrorsList(errors);
+ return;
+ }
+ setErrorsList([]);
+
+ handleImagePicker(mode, createItem);
+
+ function createItem(images: any[]) {
+ for (let i = 0; i < images.length; i++) {
+ ImageResizer.createResizedImage(images[i], 500, 500, "JPEG", 40, 0)
+ .then(async (response) => {
+ const imgBase64 = await FileSystem.readAsStringAsync(response.uri, {
+ encoding: "base64",
+ });
+ console.log("imageSize", response.size);
+ dispatch(
+ addItem({
+ name: prefix + ` ${i + 1}`,
+ collection: collection,
+ category: selectedCategory,
+ type: type,
+ fit: "",
+ season: "",
+ size: "",
+ sizeUnit: "",
+ quantity: 1,
+ purchaseDate: JSON.stringify(new Date()),
+ image: imgBase64,
+ automaticColor: false,
+ primaryColor: "",
+ secondaryColor: "",
+ tertiaryColor: "",
+ }),
+ );
+ })
+ .catch((err) => {
+ console.log("image comparison error: ", err);
+ });
+ }
+ }
+
+ navigation.popToTop("Category");
+
+ navigation.dispatch(CommonActions.goBack());
+ }
+ return (
+
+ <>
+
+
+
+ {localization.BulkMode[storedSettings.language]}
+
+
+
+ setPrefix(text.nativeEvent.text)}
+ />
+ {selectedCategory <= 3 && (
+
+
+
+ )}
+
+
+
+
+ handleNumberChange(
+ () => {
+ setImageNum(Number(text.nativeEvent.text));
+ },
+ text.nativeEvent.text,
+ 0,
+ 30,
+ )
+ }
+ keyboardType="numeric"
+ />
+
+
+
+ {localization.bulkInfo[storedSettings.language]}
+
+
+ {errorsList.length > 0 && (
+
+ {errorsList.map((error, index) => {
+ return (
+
+ {error}
+
+ );
+ })}
+
+ )}
+
+
+
+ >
+
+ );
+};
diff --git a/screens/stackNav/Category.tsx b/screens/stackNav/Category.tsx
index df1f429..c6b9187 100644
--- a/screens/stackNav/Category.tsx
+++ b/screens/stackNav/Category.tsx
@@ -100,7 +100,8 @@ export function Category() {
>
- navigation.navigate(item.screen, {
+ navigation.navigate("ModeScreen", {
+ screenName: item.screen,
selectedCategory: item.index,
})
}
diff --git a/screens/stackNav/Home.tsx b/screens/stackNav/Home.tsx
index b04c5b0..e483ae4 100644
--- a/screens/stackNav/Home.tsx
+++ b/screens/stackNav/Home.tsx
@@ -85,10 +85,7 @@ export function Home() {
(state: RootState) => state.settings.language,
);
useEffect(() => {
- // PushNotification.deleteChannel("channel-id-1");
-
GetAllPermissions();
- createChannels(selectedLang, storedSettings.name);
}, []);
useEffect(() => {
@@ -96,7 +93,13 @@ export function Home() {
if (storedSettings.enableLaundry != true || numberOfLaundry == 0) {
PushNotification.cancelLocalNotification("2");
}
- }, [storedSettings.enableLaundry, numberOfLaundry]);
+ }, [storedSettings.enableLaundry, , numberOfLaundry]);
+ useEffect(() => {
+ createChannels(selectedLang, storedSettings.name);
+ if (storedSettings.enableReminder != true) {
+ PushNotification.cancelLocalNotification("1");
+ }
+ }, [storedSettings.enableReminder]);
return (
diff --git a/screens/stackNav/ModeScreen.tsx b/screens/stackNav/ModeScreen.tsx
new file mode 100644
index 0000000..8aa47e9
--- /dev/null
+++ b/screens/stackNav/ModeScreen.tsx
@@ -0,0 +1,65 @@
+import { Image, TouchableOpacity } from "react-native";
+import { ThemeView } from "../../components/ThemeView";
+import { ThemeText } from "../../components/ThemeText";
+import { BackButton } from "../../components/BackButton";
+import bulkBoxes from "../../assets/images/bulk.png";
+import singleBox from "../../assets/images/single.png";
+import { localization } from "../../utils/localization";
+import { useSelector } from "react-redux";
+import { RootState } from "../../redux/store";
+
+export const ModeScreen = ({
+ navigation,
+ route,
+}: {
+ navigation: any;
+ route: any;
+}) => {
+ const { selectedCategory, screenName } = route.params;
+ const storedSettings = useSelector((state: RootState) => state.settings);
+ return (
+
+ <>
+
+
+ navigation.navigate(screenName, {
+ selectedCategory: selectedCategory,
+ })
+ }
+ >
+
+ {localization.addSingleItem[storedSettings.language]}
+
+
+
+
+ navigation.navigate("BulkModeForm", {
+ selectedCategory: selectedCategory,
+ })
+ }
+ >
+
+ {localization.addMultiItems[storedSettings.language]}
+
+
+
+ >
+
+ );
+};
diff --git a/screens/stackNav/Settings.tsx b/screens/stackNav/Settings.tsx
index 0b694a4..af61503 100644
--- a/screens/stackNav/Settings.tsx
+++ b/screens/stackNav/Settings.tsx
@@ -12,6 +12,7 @@ import {
changeLanguage,
laundryNumberSetter,
setEnableLaundry,
+ setReminder,
userNameSetter,
} from "../../redux/settingsSlice";
import { Button, Checkbox, Snackbar } from "react-native-paper";
@@ -21,19 +22,7 @@ import Icon from "react-native-vector-icons/FontAwesome";
import { colors } from "../../utils/colors";
import { laundryRefresher } from "../../redux/itemsSlice";
import { localization } from "../../utils/localization";
-
-export const handleNumberChange = (
- func: any,
- text: string,
- min: number,
- max: number,
-) => {
- // Use regular expressions to allow only numbers and optionally a single decimal point
- const regex = /^[0-9]*\.?[0-9]*$/;
- if (regex.test(text) && Number(text) >= min && Number(text) <= max) {
- func();
- }
-};
+import { handleNumberChange } from "../../utils/validators";
export const Settings = () => {
const [openLang, setOpenLang] = useState(false);
@@ -49,8 +38,12 @@ export const Settings = () => {
const onToggleSnackBar = () => setVisible(!visible);
const onDismissSnackBar = () => setVisible(false);
const [exportOrImport, setExportOrImport] = useState(0);
- const [checked, setChecked] = useState(storedSettings.enableLaundry);
-
+ const [checkedLau, setCheckedLau] = useState(storedSettings.enableLaundry);
+ const [checkedRem, setCheckedRem] = useState(storedSettings.enableReminder);
+ const setCheckboxes = (enableLaundry: boolean, enableReminder: boolean) => {
+ setCheckedLau(enableLaundry);
+ setCheckedRem(enableReminder);
+ };
return (
<>
@@ -149,7 +142,7 @@ export const Settings = () => {
@@ -216,6 +209,22 @@ export const Settings = () => {
{localization.ThisWillRemind[storedSettings.language]}
+
+
+ Daily logging reminder
+
+ {
+ setCheckedRem((prev) => !prev);
+ dispatch(setReminder({ enableReminder: !checkedRem }));
+ }}
+ />
+
{/* */}
>
diff --git a/utils/importStoreFromJson.ts b/utils/importStoreFromJson.ts
index 9331637..5dbe537 100644
--- a/utils/importStoreFromJson.ts
+++ b/utils/importStoreFromJson.ts
@@ -6,7 +6,9 @@ import { importItems } from "../redux/itemsSlice";
import { importSettings } from "../redux/settingsSlice";
// Function to import the Redux store from a JSON file in the Documents folder
-export const importStoreFromJson = async () => {
+export const importStoreFromJson = async (
+ setCheckboxes: (enableReminder: boolean, enableLaundry: boolean) => void,
+) => {
try {
const permissions =
await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
@@ -41,9 +43,15 @@ export const importStoreFromJson = async () => {
: state.settings.language,
laundryNumber: state.settings.laundryNumber,
name: state.settings.name,
+ enableLaundry: state.settings.enableLaundry,
+ enableReminder: state.settings.enableReminder,
}),
);
await persistor.persist();
+ setCheckboxes(
+ state.settings.enableLaundry,
+ state.settings.enableReminder,
+ );
console.log("Store imported successfully.");
return true;
} else {
diff --git a/utils/localization.ts b/utils/localization.ts
index 0c72eea..d0bd5dc 100644
--- a/utils/localization.ts
+++ b/utils/localization.ts
@@ -143,6 +143,35 @@ export const localization = {
"تذكير بالغسيل؟",
"¿Recordatorio de lavandería?",
],
+ addSingleItem: [
+ "Add Single Item",
+ "إضافة عنصر واحد",
+ "Agregar Ítem Individual",
+ ],
+ addMultiItems: [
+ "Add multiple Items",
+ "إضافة عناصر متعددة",
+ "Agregar Varios Ítems",
+ ],
+ startLocalImages: [
+ "Start with Local images",
+ "ابدأ بالصور المحلية",
+ "Comenzar con Imágenes Locales",
+ ],
+ startWCamera: [
+ "Start with camera",
+ "ابدأ بالكاميرا",
+ "Comenzar con la Cámara",
+ ],
+ bulkInfo: [
+ "This will let you create items rapidly according to the number of images",
+ "سيمكنك هذا من إنشاء العناصر بسرعة حسب عدد الصور",
+ "Esto te permitirá crear ítems rápidamente según la cantidad de imágenes",
+ ],
+ BulkMode: ["Bulk Mode", "وضع الجملة", "Modo Masivo"],
+ Prefix: ["Prefix", "بادئة", "Prefijo"],
+ NumberOfItems: ["Number of items", "عدد العناصر", "Número de ítems"],
+ FilterByColor: ["Filter by color", "تصفية حسب اللون", "Filtrar por color"],
};
export const clothingReminderMessages = [
diff --git a/utils/validators.ts b/utils/validators.ts
new file mode 100644
index 0000000..77cb217
--- /dev/null
+++ b/utils/validators.ts
@@ -0,0 +1,12 @@
+export const handleNumberChange = (
+ func: any,
+ text: string,
+ min: number,
+ max: number,
+) => {
+ // Use regular expressions to allow only numbers and optionally a single decimal point
+ const regex = /^[0-9]*\.?[0-9]*$/;
+ if (regex.test(text) && Number(text) >= min && Number(text) <= max) {
+ func();
+ }
+};