TAxios 추가 및 features 구조 변경, api 호출 방식 변경

This commit is contained in:
hyunwoo93.cha
2024-01-25 13:40:53 +09:00
parent 465afb64e5
commit 0b5cd55235
18 changed files with 431 additions and 213 deletions

View File

@@ -5,15 +5,15 @@ import css from "./App.module.less";
import MainView from "../views/MainView/MainView";
import { useDispatch } from "react-redux";
import { fetchAuthenticationCode } from "../features/auth/authThunks";
import { fetchHomeMenu } from "../features/menu/menuThunks";
import { getAuthenticationCode } from "../features/device/deviceSlice";
import { getHomeMenu } from "../features/home/homeSlice";
function AppBase(props) {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchAuthenticationCode());
dispatch(fetchHomeMenu());
dispatch(getAuthenticationCode());
dispatch(getHomeMenu());
}, [dispatch]);
return <MainView />;

View File

@@ -0,0 +1,200 @@
import { getUrl, URLS } from "./apiConfig";
import { addPanels, resetPanels } from "../features/panels/panelsSlice";
import * as HelperMethods from "../utils/helperMethods";
import * as Config from "../utils/Config";
import axios from "axios";
// refresh-token 구현 필요
let tokenRefreshing = false;
export const tokenRefresh = async (dispatch, getState, autoLogin, callback) => {
// console.log('getState ', getState());
// const {serverType} = getState().common.localSettings;
// const url = getUrl(URLS.TOKEN_REFRESH, serverType);
// const webOSVersionReal = getState().common.appStatus.webOSVersionReal;
// const {authenticationResult, tokenRequiredTime, userId} = getState().common.localSettings;
// const deviceId = getState().common.appStatus.deviceId;
// if(!userId){
// console.warn('tokenRefresh : not logged');
// if(callback){
// callback(false);
// }
// return;
// }
// const currentTime = new Date();
// const gap = (currentTime - tokenRequiredTime)/1000;
// const ExpiredIn = Number(authenticationResult.ExpiresIn);
// const limitTime = ExpiredIn - (ExpiredIn * THRESHOLD_AUTH_REFRESH);
// if(!autoLogin && (gap < limitTime || tokenRefreshing)){
// if(callback){
// callback(false);
// }
// return;
// }
// const tokenInfo =
// {
// "authenticationResult": authenticationResult,
// deviceInfo: {
// "deviceId": deviceId,
// "deviceName": "deviceName01",
// "os": "webos",
// "model": webOSVersionReal,
// "serialNumber": "xxxx-xxxx-xxxx-xxxx"
// }
// , "userId" : userId
// };
// if(useQAServerURL !== 'prd'){
// console.log('tokenInfo...', tokenInfo, gap, ExpiredIn);
// }
// try{
// tokenRefreshing = true;
// const response = await axios.put(url, tokenInfo, AUTHORIZATION);
// console.log(' tokenRefresh response....', response);
// Actions.AUTHORIZATION.headers.authorization = response.data.authenticationResult.AccessToken;
// dispatch(CommonActions.changeLocalSettings({authenticationResult:response.data.authenticationResult, tokenRequiredTime: new Date()}));
// console.log('토큰 갱신 완료...');
// if(callback){
// callback(true);
// }
// }catch(res) {
// const error = res && res.toJSON ? res.toJSON() : {};
// console.error('tokenRefresh', error, res);
// if(error.message ){
// if(error.message.indexOf('code 400')
// || error.message.indexOf('code 401')
// || error.message.indexOf('code 403')){
// dispatch(Actions.logout());
// }
// }
// if(callback){
// callback(false);
// }
// };
tokenRefreshing = false;
};
export const TAxios = async (
dispatch,
getState,
type,
baseUrl,
urlParams = {},
params = {},
onSuccess,
onFail,
noTokenRefresh = false
) => {
let accessToken = getState().device.accessToken;
const AUTHORIZATION = { headers: {} };
AUTHORIZATION.headers["app_id"] = "com.lgshop.app";
AUTHORIZATION.headers["app_ver"] = "1.0.0";
AUTHORIZATION.headers["dvc_id"] = "testdeviceid";
AUTHORIZATION.headers["cntry_cd"] = "US";
AUTHORIZATION.headers["prod_cd"] = "webOSTV 5.0";
AUTHORIZATION.headers["plat_cd"] = "W20H";
AUTHORIZATION.headers["lang_cd"] = "en-US";
AUTHORIZATION.headers["os_ver"] = "3.0";
AUTHORIZATION.headers["sdk_ver"] = "1.0.0";
AUTHORIZATION.headers["publish_flag"] = "Y";
if (
baseUrl !== URLS.GET_AUTHENTICATION_CODE &&
!accessToken &&
!noTokenRefresh
) {
console.log("Waiting for access token...");
while (!accessToken) {
await HelperMethods.wait(100);
accessToken = getState().device.accessToken;
}
}
if (accessToken) {
AUTHORIZATION.headers["lgsp_auth"] = accessToken;
}
if (typeof window === "object") {
let url = "";
if (Array.isArray(baseUrl)) {
url = getUrl(baseUrl[0]);
} else {
url = getUrl(baseUrl);
}
switch (type) {
case "get": {
const _urlparams = HelperMethods.createQueryString(urlParams);
url += url ? `?${_urlparams}` : "";
break;
}
}
console.log("TAxios ", type + ": " + url, params);
if (!noTokenRefresh) {
await tokenRefresh(dispatch, getState);
}
while (tokenRefreshing) {
await HelperMethods.wait(100);
}
let axiosInstance = null;
switch (type) {
case "get":
AUTHORIZATION.headers = { ...AUTHORIZATION.headers, ...params };
axiosInstance = axios[type](url, AUTHORIZATION);
break;
case "put":
// put이 있다면 구현 필요
case "post":
axiosInstance = axios[type](url, params, AUTHORIZATION);
break;
case "delete":
// delete가 있다면 구현 필요
}
if (axiosInstance) {
axiosInstance
.then((res) => {
console.log("TAxios response", url, res);
if (onSuccess) {
onSuccess(res);
}
})
.catch((res) => {
const error = res && res.toJSON ? res.toJSON() : {};
console.error("TAxios ", url, error);
if (error.message === "Network Error") {
dispatch(resetPanels());
dispatch(
addPanels({
name: Config.panel_names.ERROR_PANEL,
panelInfo: { reason: "server Error" },
})
);
} else {
// todo
}
if (onFail) {
onFail(res);
}
});
} else {
console.warn("TAxios invalid case type ", type);
}
}
};

View File

@@ -12,6 +12,7 @@ export const URLS = {
//home controller
GET_HOME_TERMS: "/lgsp/v1/home/terms.lge",
GET_HOME_MENU: "/lgsp/v1/home/menu.lge",
//brand-controller
GET_BRAND_LIST: "/lgsp/v1/brand/info.lge",
@@ -23,6 +24,6 @@ export const URLS = {
GET_ON_SALE_INFO: "/lgsp/v1/onsale/onsale.lge",
};
export const getUrl = (url) => {
return SHOPTIME_BASE_URL + url;
export const getUrl = (endStr) => {
return SHOPTIME_BASE_URL + endStr;
};

View File

@@ -1,3 +1,9 @@
/* ----------------------
삭제 예정
---------------------- */
import axios from "axios";
import { store } from "../store/store";
import { SHOPTIME_BASE_URL } from "./apiConfig";

View File

@@ -1,50 +0,0 @@
import { URLS } from "./apiConfig";
import api from "./axiosConfig";
// 인증코드 요청 IF-LGSP-000
export async function getAuthenticationCode() {
try {
const response = await api.get(URLS.GET_AUTHENTICATION_CODE);
return response.data;
} catch (error) {
const { response } = error;
if (response) {
const statusCode = response.status;
const statusText = response.statusText;
console.error(`${statusCode} Error, ${statusText}`);
}
throw new Error(error);
}
}
// 디바이스 부가 정보 조회 IF-LGSP-003
export async function getDeviceAdditionInfo() {}
// 디바이스 부가 정보 삭제 요청 IF-LGSP-087
export async function deleteDeviceAdditionInfo() {}
// 페어링 삭제 요청 IF-LGSP-078
export async function deleteDevicePairing() {}
// 페어링 생성 요청 IF-LGSP-077
export async function registerDevicePairing() {}
// 디바이스 등록 및 약관 동의 IF-LGSP-001
export async function registerDevice() {}
// 디바이스 부가 정보 저장 IF-LGSP-002
export async function registerDeviceInfo() {}
export default {
getAuthenticationCode,
getDeviceAdditionInfo,
deleteDeviceAdditionInfo,
deleteDevicePairing,
registerDevice,
registerDeviceInfo,
registerDevicePairing,
};

View File

@@ -1,3 +1,9 @@
/* ----------------------
삭제 예정
---------------------- */
import { URLS } from "./apiConfig";
import api from "./axiosConfig";

View File

@@ -1,3 +1,9 @@
/* ----------------------
삭제 예정
---------------------- */
import { URLS } from "./apiConfig";
import api from "./axiosConfig";
@@ -17,34 +23,10 @@ export async function getHomeMainContents() {}
export async function getHomeLayout() {}
// 메뉴 목록 조회 IF-LGSP-044
export async function getHomeMenu() {
try {
const response = await api.get(URLS.GET_HOME_MENU);
return response.data;
} catch (error) {
console.log("Error", error);
throw error;
}
}
export async function getHomeMenu() {}
// 약관 정보 조회 IF-LGSP-005
export async function getHomeTerms(props) {
const { trmsTpCdList, mbrNo } = props;
try {
const response = await api.get(URLS.GET_HOME_TERMS, {
params: {
trmsTpCdList,
mbrNo,
},
});
return response.data;
} catch (error) {
console.error("Error : ", error);
throw error;
}
}
export async function getHomeTerms() {}
// Theme 전시 정보 상세 조회 IF-LGSP-060
export async function getThemeCurationDetailInfo() {}

View File

@@ -1,3 +1,9 @@
/* ----------------------
삭제 예정
---------------------- */
import { URLS } from "./apiConfig";
import api from "./axiosConfig";

View File

@@ -104,7 +104,9 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
const [tabFocused, setTabFocused] = useState([false, false, false]); //COLLABSED_MAIN, ACTIVATED_MAIN, ACTIVATED_SUB
const panelSwitching = useRef(null);
const { cursorVisible } = useSelector((state) => state.common.appStatus);
const titles = useSelector((state) => state.menu.title);
const titles = useSelector((state) => state.home.menuData);
console.log("titles", titles);
// const items = useSelector((state) => state.menu.items);
const menuItems = useMemo(

View File

@@ -1,35 +0,0 @@
import { createSlice } from "@reduxjs/toolkit";
import { fetchAuthenticationCode } from "./authThunks";
const initialState = {
accessToken: null,
isLoading: false,
error: null,
};
export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchAuthenticationCode.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchAuthenticationCode.fulfilled, (state, action) => {
state.accessToken = action.payload;
state.isLoading = false;
})
.addCase(fetchAuthenticationCode.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload;
});
},
});
export const selectAccessToken = (state) => state.auth.accessToken;
export const selectAuthLoading = (state) => state.auth.isLoading;
export const selectAuthError = (state) => state.auth.error;
export default authSlice.reducer;

View File

@@ -1,16 +0,0 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getAuthenticationCode } from "../../api/deviceApi";
export const fetchAuthenticationCode = createAsyncThunk(
"auth/fetchAuthenticationCode",
async (_, { rejectWithValue }) => {
try {
const response = await getAuthenticationCode();
return response.data.accessToken;
} catch (error) {
return rejectWithValue("unknown Error");
}
}
);

View File

@@ -0,0 +1,51 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { URLS } from "../../api/apiConfig";
import { TAxios } from "../../api/TAxios";
// 인증코드 요청 IF-LGSP-000
export const getAuthenticationCode = createAsyncThunk(
"auth/getAuthenticationCode",
async (_, thunkAPI) => {
const onSuccess = (response) => {
console.log("getAuthenticationCode onSuccess ", response.data);
thunkAPI.dispatch(
deviceSlice.actions.updateAccessToken(response.data.data.accessToken)
);
};
const onFail = (error) => {
console.error("getAuthenticationCode onFail ", error);
};
TAxios(
thunkAPI.dispatch,
thunkAPI.getState,
"get",
URLS.GET_AUTHENTICATION_CODE,
{},
{},
onSuccess,
onFail
);
}
);
const initialState = {
accessToken: null,
};
export const deviceSlice = createSlice({
name: "device",
initialState,
reducers: {
updateAccessToken: (state, action) => {
state.accessToken = action.payload;
},
},
});
export const { updateAccessToken } = deviceSlice.actions;
export default deviceSlice.reducer;

View File

@@ -0,0 +1,83 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { URLS } from "../../api/apiConfig";
import { TAxios } from "../../api/TAxios";
// 약관 정보 조회 IF-LGSP-005
export const getHomeTerms = createAsyncThunk(
"home/getHomeTerms",
async (props, thunkAPI) => {
const { trmsTpCdList, mbrNo } = props;
const onSuccess = (response) => {
console.log("getHomeTerms onSuccess ", response.data);
thunkAPI.dispatch(homeSlice.actions.updateTermsData(response.data));
};
const onFail = (error) => {
console.error("getHomeTerms onFail ", error);
};
TAxios(
thunkAPI.dispatch,
thunkAPI.getState,
"get",
URLS.GET_HOME_TERMS,
{ trmsTpCdList, mbrNo },
{},
onSuccess,
onFail
);
}
);
// 메뉴 목록 조회 IF-LGSP-044
export const getHomeMenu = createAsyncThunk(
"home/getHomeMenu",
async (_, thunkAPI) => {
const onSuccess = (response) => {
console.log("getHomeMenu onSuccess ", response.data);
thunkAPI.dispatch(homeSlice.actions.updateMenuData(response.data));
};
const onFail = (error) => {
console.error("getHomeMenu onFail ", error);
};
TAxios(
thunkAPI.dispatch,
thunkAPI.getState,
"get",
URLS.GET_HOME_MENU,
{},
{},
onSuccess,
onFail
);
}
);
const initialState = {
termsData: {},
menuData: {},
};
export const homeSlice = createSlice({
name: "home",
initialState,
reducers: {
updateTermsData: (state, action) => {
state.termsData = action.payload;
},
updateMenuData: (state, action) => {
state.menuData = action.payload;
},
},
});
export const { updateTermsData, updateMenuData } = homeSlice.actions;
export default homeSlice.reducer;

View File

@@ -1,33 +0,0 @@
import { createSlice } from "@reduxjs/toolkit";
import { fetchHomeMenu } from "./menuThunks";
const initialState = {
title: "",
items: {},
};
export const menuSlice = createSlice({
name: "menu",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchHomeMenu.pending, (state, action) => {})
.addCase(fetchHomeMenu.fulfilled, (state, action) => {
state.title = action.payload.gnb.map((titles) => {
return titles.menuNm;
});
state.items = action.payload.gnb.map((items) => {
return items;
});
})
.addCase(fetchHomeMenu.rejected, (state, action) => {});
},
});
export const selectorMenuId = (state) => state.menu.menuId;
export const selectorMenuTitle = (state) => state.menu.title;
export const selectorMenuItem = (state) => state.menu.items;
export default menuSlice.reducer;

View File

@@ -1,15 +0,0 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getHomeMenu } from "../../api/homeApi";
export const fetchHomeMenu = createAsyncThunk(
"menu/fetchHomeMenu",
async (_, { rejectWithValue }) => {
try {
const response = await getHomeMenu();
return response.data;
} catch (error) {
return rejectWithValue("unkown Error");
}
}
);

View File

@@ -1,17 +1,17 @@
import { configureStore } from "@reduxjs/toolkit";
import appDataReducer from "../features/appData/appDataSlice";
import authReducer from "../features/auth/authSlice";
import deviceReducer from "../features/device/deviceSlice";
import commonReducer from "../features/common/commonSlice";
import menuReducer from "../features/menu/menuSlice";
import panelsReducer from "../features/panels/panelsSlice";
import homeReducer from "../features/home/homeSlice";
export const store = configureStore({
reducer: {
panels: panelsReducer,
auth: authReducer,
device: deviceReducer,
appData: appDataReducer,
common: commonReducer,
menu: menuReducer,
home: homeReducer,
},
});

View File

@@ -48,3 +48,25 @@ export const $L = (str) => {
return str && str.replace(/{br}/g, "{br}");
};
export const createQueryString = (object) => {
const parts = [];
for (const key of Object.getOwnPropertyNames(object)) {
if (
object[key] !== null &&
object[key] !== undefined &&
object[key] !== ""
) {
parts.push(`${key}=${encodeURIComponent(object[key])}`);
}
}
return parts.join("&");
};
export const wait = (time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
};

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import { useDispatch } from "react-redux";
import React, { useState, useEffect, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import * as Config from "../../utils/Config";
import TButton, { TYPES, SIZES } from "../../components/TButton/TButton";
@@ -7,13 +7,13 @@ import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDeco
import Spotlight from "@enact/spotlight";
import TPanel from "../../components/TPanel/TPanel";
import { getHomeTerms } from "../../api/homeApi";
import TPopUp from "../../components/TPopUp/TPopUp";
import css from "./IntroPanel.module.less";
import classNames from "classnames";
import { $L } from "../../utils/helperMethods";
import { addPanels, popPanel } from "../../features/panels/panelsSlice";
import { getHomeTerms } from "../../features/home/homeSlice";
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
@@ -22,39 +22,43 @@ const Container = SpotlightContainerDecorator(
export default function IntroPanel({ children, ...rest }) {
const dispatch = useDispatch();
const termsData = useSelector((state) => state.home.termsData);
const [showExitButton, setShowExitButton] = useState(false);
const [termsPopUpOpen, setTermsPopUpOpen] = useState(false);
const [currentTermsData, setCurrentTermsData] = useState(null);
const [currentTermsTitle, setCurrentTermsTitle] = useState(null);
const [currentTerms, setCurrentTerms] = useState(null);
const handleTermsClick = async (trmsTpCdList) => {
try {
const termsData = await getHomeTerms({ trmsTpCdList });
useEffect(() => {
dispatch(getHomeTerms({ trmsTpCdList: "MST00401, MST00402" }));
}, [dispatch]);
setCurrentTermsTitle(
trmsTpCdList === "MST00401" ? "Privacy Policy" : "Terms & Conditions"
);
setCurrentTermsData(termsData.data.terms[0]);
setTermsPopUpOpen(true);
} catch (error) {
console.error("Error Fetching terms: ", error);
}
};
const handleTermsClick = useCallback(
(trmsTpCdList) => {
if (termsData && termsData.data && termsData.data.terms) {
const selectedTerms = termsData.data.terms.find(
(term) => term.trmsTpCd === trmsTpCdList
);
const handleAgree = () => {
setCurrentTerms(selectedTerms);
setTermsPopUpOpen(true);
}
},
[termsData]
);
const handleAgree = useCallback(() => {
dispatch(popPanel(Config.panel_names.INTRO_PANEL));
};
}, [dispatch]);
const handleDisagree = () => {
const handleDisagree = useCallback(() => {
setShowExitButton(true);
};
}, [showExitButton]);
const confirmExit = () => {};
const confirmExit = useCallback(() => {}, []);
const cancelExit = () => {
const cancelExit = useCallback(() => {
setShowExitButton(false);
};
}, [showExitButton]);
useEffect(() => {
Spotlight.focus("introTermsAgree");
@@ -112,13 +116,17 @@ export default function IntroPanel({ children, ...rest }) {
hasButton
button1Text="OK"
>
{currentTermsData && (
{currentTerms && (
<div className={css.introTermsConts}>
<div className={css.termsTitle}>{currentTermsTitle}</div>
<div className={css.termsTitle}>
{currentTerms.trmsTpCd === "MST00401"
? "Privacy Policy"
: "Terms & Conditions"}
</div>
<div
className={css.termsDesc}
dangerouslySetInnerHTML={{
__html: currentTermsData && currentTermsData.trmsCntt,
__html: currentTerms && currentTerms.trmsCntt,
}}
/>
</div>