-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#705 @taxi/app
init
#704
#705 @taxi/app
init
#704
Conversation
✅ Deploy Preview for taxi-dev-preview ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
41896d0
to
ab623cd
Compare
SERVER_URL: isDev | ||
? `http://${ip.v4.sync()}:3000` | ||
: "https://taxi.sparcs.org", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
개발 환경에서는 로컬 네트워크의 택시 웹이 뜨도록 하였습니다.
모바일 기기(expo go)를 이용하여 개발하는 경우 localhost:3000
으로는 접속하지 못하기 때문에 local ip를 가져오도록 하였습니다.
"web": "pnpm -F @taxi/web", | ||
"app": "pnpm --filter @taxi/app", | ||
"web": "pnpm --filter @taxi/web", | ||
"start:app": "pnpm -r --stream start", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pnpm start:app
을 할 경우 web과 app이 동시에 시작됩니다.
const onNavigationStateChange = useCallback( | ||
(event: WebViewNavigation) => { | ||
const screen = mapWebRoutes(event.url); | ||
if (!isFocused || isSameScreen(currentScreen, screen)) return; | ||
|
||
navigation.navigate(...screen); | ||
ref.current?.stopLoading(); | ||
ref.current?.goBack(); | ||
}, | ||
[isFocused, currentScreen, navigation] | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onNavigationStateChange
리스너를 이용하여 새로운 route로 이동하는 경우 그 페이지에 해당하는 native app에서의 navigating을 진행해 줍니다.
나중에 앱과 웹의 코드베이스가 공유되는 경우에는 다른 접근을 취하는게 좋을 것 같습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Web route를 native screen으로 매핑하기 위해 필요한 유틸 함수들이 정의되어 있습니다.
현재는 web route와 native screen을 각각 정의하고, 둘을 매핑해주는 방식을 사용하고 있지만 추후에는 둘을 동시에 정의할 수 있는 공통 인터페이스를 구현하는 것이 유지보수 상 용이할 것 같습니다.
export const screen = <ScreenName extends keyof RootStackParamList>( | ||
...args: // this first condition allows us to iterate over a union type | ||
// This is to avoid getting a union of all the params from `ParamList[RouteName]`, | ||
// which will get our types all mixed up if a union RouteName is passed in. | ||
ScreenName extends unknown | ||
? // This condition checks if the params are optional, | ||
// which means it's either undefined or a union with undefined | ||
undefined extends RootStackParamList[ScreenName] | ||
? | ||
| [screen: ScreenName] // if the params are optional, we don't have to provide it | ||
| [screen: ScreenName, params: RootStackParamList[ScreenName]] | ||
: [screen: ScreenName, params: RootStackParamList[ScreenName]] | ||
: never | ||
) => args; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
screen을 정의하는 함수입니다. @/navigation/types
에 정의된 스크린을 type-safe하게 사용할 수 있도록 react-navigation
에 정의되어 있는 navigation.navigate()
의 타입 정의를 그대로 사용하였습니다.
screen("Chatting", { roomId })
와 같이 스크린을 표현할 수 있습니다.
type ExtractRouteParams<T extends string> = string extends T | ||
? Record<string, string> | ||
: T extends `${infer Start}:${infer Param}/${infer Rest}` | ||
? { [K in Param | keyof ExtractRouteParams<Rest>]: string } | ||
: T extends `${infer Start}:${infer Param}` | ||
? { [K in Param]: string } | ||
: {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"/chatting/:chatId/message/:messageId"
등의 string에서 path parameter 타입 { chatId: string, messageId: string }
을 추출해내기 위한 유틸리티 타입입니다.
export const route = <Pattern extends string>( | ||
routePattern: Pattern, | ||
getScreen: (params: ExtractRouteParams<Pattern>) => Screen | ||
): Route => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
route(<웹 라우트 패턴>, (파라미터) => <스크린>)
으로의 단일 매핑을 정의하는 유틸 함수입니다.
<웹 라우트 패턴>
에 정의된 :pathParameter
가 params
로 그대로 전달되며 타입도 infer됩니다.
? { [K in Param]: string } | ||
: {}; | ||
|
||
type Route = (route: string) => Screen | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
route
함수의 return type 입니다.
route가 string으로 주어졌을 때 매칭되는 스크린이 있다면 해당 Screen
을, 아닌 경우에는 null
을 반환하는 매칭 함수를 반환합니다.
const pattern = new URLPattern(routePattern); | ||
|
||
return (pathname: string) => { | ||
const params = pattern.match(pathname); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
url pattern matching은 url-pattern
라이브러리를 이용하였습니다. 다만 해당 라이브러리의 타입 지원은 대부분 any
로 지원되고 있어 위에서 정의한 커스텀 타입들을 덮어씌워 이용합니다.
export const getPath = (route: string) => { | ||
const match = route.match(new RegExp(`^${env.SERVER_URL}([\\w\\/]+)\\??`)); | ||
return match?.[1]; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
택시 웹의 주소 https://taxi.sparcs.org/chatting/123
이 주어졌을 때 path /chatting/123
을 반환합니다. (로컬 개발 환경일 경우에는 taxi.sparcs.org
대신 로컬 주소)
외부 주소(SPARCS SSO 등)일 경우에는 null
을 반환합니다.
return match?.[1]; | ||
}; | ||
|
||
export const routes = (routes: Route[]) => (uri: string) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여러 route
매핑 정의(매칭 함수)를 array로 받아 단일 매칭 함수로 합쳐 줍니다.
uri를 받아 해당하는 Screen
을 반환하는 매칭 함수를 반환합니다.
해당 매칭 함수는 uri가 외부 uri이거나, 매칭되는 스크린이 없는 경우 screen("Web", { uri })
스크린을 반환합니다. 해당 스크린은 모든 기타 웹 주소가 렌더링되는 스크린입니다.
|
||
const onNavigationStateChange = useCallback( | ||
(event: WebViewNavigation) => { | ||
const screen = mapWebRoutes(event.url); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uri가 바뀔 때 그 uri가 매칭되는 스크린을 받아옵니다.
const onNavigationStateChange = useCallback( | ||
(event: WebViewNavigation) => { | ||
const screen = mapWebRoutes(event.url); | ||
if (!isFocused || isSameScreen(currentScreen, screen)) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
매칭된 스크린이 현재 스크린과 동일한 경우, 새로운 route navigation을 진행하지 않습니다.
navigation.navigate(...screen); | ||
ref.current?.stopLoading(); | ||
ref.current?.goBack(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다른 스크린으로 이동하는 경우, 현재 WebView
에서의 routing은 취소하고, native에서 해당하는 스크린으로 navigation합니다. (tab navigation, stack navigation 모두 가능)
export const isSameScreen = ( | ||
screen1: Screen, | ||
screen2: Screen | ||
): screen1 is Screen => { | ||
const [screenName1, params1] = screen1; | ||
const [screenName2, params2] = screen2; | ||
if (screenName1 === "Web" && screenName2 === "Web") return true; | ||
return screenName1 === screenName2 && shallowEqualObjects(params1, params2); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두 개의 스크린이 동일한 스크린인지 확인하기 위한 equality 유틸 함수입니다.
for (const route of routes) { | ||
const screen = route(path); | ||
if (screen) return screen; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주어진 route array 중 먼저 정의된 route부터 검사합니다.
즉, 한 개 이상의 route가 매칭되는 경우, 더 위에 정의된 screen으로 이동합니다.
@@ -4,7 +4,7 @@ const env = { ...import.meta.env, ...window["env"] }; | |||
|
|||
// 환경변수 | |||
export const isDev = env.DEV; // automatically provided | |||
export const backServer = env.REACT_APP_BACK_URL; // required | |||
export const backServer = isDev ? "/api" : env.REACT_APP_BACK_URL; // use proxy in dev mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
local network(앱)에서도 backend에 접근할 수 있도록 하기 위해 local dev proxy를 이용합니다.
packages/web/vite.config.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
local dev proxy를 설정합니다.
아직 socket 쪽은 작동하고 있지 않아 확인해 보아야 할 것 같습니다.
다만 rewrite 규칙을 사용하는 것보다 �sparcs-kaist/taxi-back#446 와 같은 접근을 취하는 것이 더 나아보입니다.
packages/app/src/navigation/types.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://reactnavigation.org/docs/typescript/
react navigation docs에서 권장하는 방식의 navigiation screen 타입 정의입니다.
packages/app/src/navigation/web.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
웹에서 각각의 path와 path param이 어떤 스크린과 그 param에 대응되는지 정의되어 있습니다.
현재에 각각의 screen은 다시 WebView를 띄우고 있지만 그대로 저 param을 이용하는 native component로도 대체 가능합니다.
route
, routes
, screen
등 사용하는 util 함수들은 모두 type-safe 하게 작성했으며 구현은 @/utils/navigation
에서 찾아보실 수 있습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재에는 모든 스크린에서 WebView
를 다시 리턴하고 있습니다.
그러나 추후 params.roomId
등의 screen parameter를 직접 사용하여 native component도 구현할 수 있습니다.
WebView
-> Native Component
으로 이동한 경우에도 screen parameter가 자동으로 채워집니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Summary
It closes #705
@taxi/app
패키지를 초기화하고 웹뷰 스크린을 local navigation과 연결합니다.Images or Screenshots
Video in Slack
Further Work