feat: add food camera and recognition features
- Implemented FoodCameraScreen for capturing food images with meal type selection. - Created FoodRecognitionScreen for processing and recognizing food images. - Added Redux slice for managing food recognition state and results. - Integrated image upload functionality to cloud storage. - Enhanced UI components for better user experience during food recognition. - Updated FloatingFoodOverlay to navigate to the new camera screen. - Added food recognition service for API interaction. - Improved styling and layout for various components.
This commit is contained in:
111
store/foodRecognitionSlice.ts
Normal file
111
store/foodRecognitionSlice.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { type FoodRecognitionResponse } from '@/services/foodRecognition';
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
// 食物识别状态类型定义
|
||||
export interface FoodRecognitionState {
|
||||
// 按ID存储的识别结果
|
||||
recognitionResults: Record<string, FoodRecognitionResponse>;
|
||||
|
||||
// 当前正在处理的识别ID
|
||||
currentRecognitionId: string | null;
|
||||
|
||||
// 加载状态
|
||||
loading: boolean;
|
||||
|
||||
// 错误信息
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
// 初始状态
|
||||
const initialState: FoodRecognitionState = {
|
||||
recognitionResults: {},
|
||||
currentRecognitionId: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
const foodRecognitionSlice = createSlice({
|
||||
name: 'foodRecognition',
|
||||
initialState,
|
||||
reducers: {
|
||||
// 设置加载状态
|
||||
setLoading: (state, action: PayloadAction<boolean>) => {
|
||||
state.loading = action.payload;
|
||||
if (action.payload) {
|
||||
state.error = null;
|
||||
}
|
||||
},
|
||||
|
||||
// 设置错误信息
|
||||
setError: (state, action: PayloadAction<string | null>) => {
|
||||
state.error = action.payload;
|
||||
state.loading = false;
|
||||
},
|
||||
|
||||
// 保存识别结果
|
||||
saveRecognitionResult: (state, action: PayloadAction<{ id: string; result: FoodRecognitionResponse }>) => {
|
||||
const { id, result } = action.payload;
|
||||
state.recognitionResults[id] = result;
|
||||
state.currentRecognitionId = id;
|
||||
state.loading = false;
|
||||
state.error = null;
|
||||
},
|
||||
|
||||
// 设置当前识别ID
|
||||
setCurrentRecognitionId: (state, action: PayloadAction<string>) => {
|
||||
state.currentRecognitionId = action.payload;
|
||||
},
|
||||
|
||||
// 清除指定的识别结果
|
||||
clearRecognitionResult: (state, action: PayloadAction<string>) => {
|
||||
const id = action.payload;
|
||||
delete state.recognitionResults[id];
|
||||
if (state.currentRecognitionId === id) {
|
||||
state.currentRecognitionId = null;
|
||||
}
|
||||
},
|
||||
|
||||
// 清除所有识别结果
|
||||
clearAllRecognitionResults: (state) => {
|
||||
state.recognitionResults = {};
|
||||
state.currentRecognitionId = null;
|
||||
state.error = null;
|
||||
},
|
||||
|
||||
// 清除错误
|
||||
clearError: (state) => {
|
||||
state.error = null;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Action creators
|
||||
export const {
|
||||
setLoading,
|
||||
setError,
|
||||
saveRecognitionResult,
|
||||
setCurrentRecognitionId,
|
||||
clearRecognitionResult,
|
||||
clearAllRecognitionResults,
|
||||
clearError,
|
||||
} = foodRecognitionSlice.actions;
|
||||
|
||||
// Selectors
|
||||
export const selectFoodRecognitionResult = (id: string) => (state: { foodRecognition: FoodRecognitionState }) =>
|
||||
state.foodRecognition.recognitionResults[id] || null;
|
||||
|
||||
export const selectCurrentFoodRecognitionResult = (state: { foodRecognition: FoodRecognitionState }) => {
|
||||
const currentId = state.foodRecognition.currentRecognitionId;
|
||||
return currentId ? state.foodRecognition.recognitionResults[currentId] || null : null;
|
||||
};
|
||||
|
||||
export const selectFoodRecognitionLoading = (state: { foodRecognition: FoodRecognitionState }) =>
|
||||
state.foodRecognition.loading;
|
||||
|
||||
export const selectFoodRecognitionError = (state: { foodRecognition: FoodRecognitionState }) =>
|
||||
state.foodRecognition.error;
|
||||
|
||||
export const selectCurrentRecognitionId = (state: { foodRecognition: FoodRecognitionState }) =>
|
||||
state.foodRecognition.currentRecognitionId;
|
||||
|
||||
export default foodRecognitionSlice.reducer;
|
||||
@@ -3,6 +3,7 @@ import challengeReducer from './challengeSlice';
|
||||
import checkinReducer, { addExercise, autoSyncCheckin, removeExercise, replaceExercises, setNote, toggleExerciseCompleted } from './checkinSlice';
|
||||
import exerciseLibraryReducer from './exerciseLibrarySlice';
|
||||
import foodLibraryReducer from './foodLibrarySlice';
|
||||
import foodRecognitionReducer from './foodRecognitionSlice';
|
||||
import goalsReducer from './goalsSlice';
|
||||
import healthReducer from './healthSlice';
|
||||
import moodReducer from './moodSlice';
|
||||
@@ -56,6 +57,7 @@ export const store = configureStore({
|
||||
scheduleExercise: scheduleExerciseReducer,
|
||||
exerciseLibrary: exerciseLibraryReducer,
|
||||
foodLibrary: foodLibraryReducer,
|
||||
foodRecognition: foodRecognitionReducer,
|
||||
workout: workoutReducer,
|
||||
water: waterReducer,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user