Leon %!s(int64=2) %!d(string=hai) anos
pai
achega
dfcea13e21

+ 9 - 3
src/app.tsx

@@ -11,6 +11,12 @@ import Taro from '@tarojs/taro'
 
 // import 'taro-ui/dist/style/index.scss'
 
+let LogBox;
+if (process.env.TARO_ENV == 'rn') {
+  LogBox = require("react-native").LogBox
+}
+
+
 
 const App: React.FC<PropsWithChildren> = ({ children }) => {
   if (process.env.TARO_ENV == 'weapp') {
@@ -41,9 +47,9 @@ const App: React.FC<PropsWithChildren> = ({ children }) => {
   }
   else {
     //关闭其中某些yellow警告
-    console.ignoredYellowBox = ['Warning: BackAndroid is deprecated. Please use BackHandler instead.', 'source.uri should not be an empty string', 'Invalid props.style key'];
-    // 关闭全部yellow警告
-    // console.disableYellowBox = true
+    LogBox.ignoreLogs(['Warning: ...']); // Ignore log notification by message
+    LogBox.ignoreAllLogs();//Ignore all log notifications
+    
     const test = require('./utils/push').default
     test()
     // jgPush()

+ 7 - 5
src/components/input/Inputs.tsx

@@ -12,6 +12,7 @@ interface CustomInputProps {
     openType?:string;
     disabled?: boolean;
     isSecure?: boolean;
+    autoFocus?:boolean;
 }
 
 export default function Component({
@@ -21,7 +22,8 @@ export default function Component({
     onConfirm,
     openType,
     disabled,
-    isSecure
+    isSecure,
+    autoFocus
 }: CustomInputProps) {
     const [inputValue, setInputValue] = useState(value ? value : '');
     const [isFocus, setIsFocus] = useState(false)
@@ -45,12 +47,12 @@ export default function Component({
                 // style={{backgroundColor:'red'}}
                 placeholder={placeholder ? placeholder : ''}
                 value={inputValue}
-                secureTextEntry={isSecure ? true : false}
-                textContentType={isSecure ? 'password' : 'none'}
-                type={openType?openType:'text'}
+                password={isSecure ? true : false}
+                type={isSecure ?'safe-password':'text'}
+                // type={openType?openType:'text'}
                 onInput={handleInputChange}
                 // onConfirm={() => onConfirm()}
-                focus
+                focus={autoFocus}
                 confirmType="done"
                 selectionColor={global.fastColor ? global.fastColor : ColorType.fast}
                 disabled={disabled ? disabled : false}

+ 0 - 1
src/components/layout/Box.rn.tsx

@@ -20,7 +20,6 @@ export default function Component(props: {
     const [height, setHeight] = useState<number>(1000000);
 
     const handleLayout = (event: LayoutChangeEvent) => {
-        console.log('oppsu1111')
         const { height } = event.nativeEvent.layout;
         console.log(height)
         setHeight(height);

+ 10 - 5
src/components/layout/Footer.scss

@@ -5,21 +5,26 @@
     right: 0;
     bottom: 0;
     z-index: 100;
-    // height: 120px;
     flex-direction: column;
     display: flex;
     align-items: center;
     padding-top: 46px;
     padding-bottom: constant(safe-area-inset-bottom);
-    /* 兼容 iOS < 11.2 */
     padding-bottom: env(safe-area-inset-bottom);
-    /* 兼容 iOS >= 11.2 */
-    // background: linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, #000000 100%);
-    // background: linear-gradient(180deg, red, blue);
 }
 
 /* #endif */
 
 /* #ifdef rn */
+.footer1 {
+    // position: absolute;
+    // left: 0;
+    // right: 0;
+    // bottom: 0;
+    // z-index: 100;
+    flex-direction: column;
+    display: flex;
+    align-items: center;
+}
 
 /* #endif */

+ 1 - 2
src/components/layout/TableCell.scss

@@ -23,10 +23,9 @@
 
 .table_cell_title {
     font-size: 32px;
-    font-weight: 400;
-    line-height: 32px;
     display: flex;
     flex: 1;
+    color: #fff;
 }
 
 .table_cell_arrow {

+ 4 - 3
src/features/auth/components/Auth.tsx

@@ -1,12 +1,13 @@
 import './Auth.scss'
-import { useState } from "react";
+import { useEffect, useState } from "react";
 
 import Login from './Login'
 import CreateAccount from './CreateAccount'
+import { useRoute } from '@react-navigation/native';
 
 export default function Auth() {
-
-    const [isSignup, setIsSignup] = useState(true);
+    const route = useRoute();
+    const [isSignup, setIsSignup] = useState(route.params && (route.params as any).isLogin?false:true);
 
     if (isSignup)
         return <CreateAccount login={() => setIsSignup(false)}></CreateAccount>

+ 1 - 1
src/features/auth/components/CreateAccount.tsx

@@ -35,7 +35,7 @@ export default function Component(props: { login: () => void }) {
 
   function signupComponent() {
     return <View>
-      <Inputs value={name} onChange={handleNameChange} onConfirm={(e) => { }} placeholder={t('feature.auth.create_account.input_username_placeholder')}></Inputs>
+      <Inputs autoFocus={true} value={name} onChange={handleNameChange} onConfirm={(e) => { }} placeholder={t('feature.auth.create_account.input_username_placeholder')}></Inputs>
       <View style={{ height: 20 }} />
       <Inputs value={email} onChange={handleEmailChange} onConfirm={(e) => { }} placeholder={t('feature.auth.create_account.input_email_placeholder')}></Inputs>
     </View>

+ 3 - 3
src/features/auth/components/CreatePassword.tsx

@@ -10,7 +10,7 @@ import { useDispatch, useSelector } from "react-redux";
 import check from '@/assets/svg/check.svg'
 import './Auth.scss'
 import Taro from "@tarojs/taro";
-import { register } from "@/services/user";
+import { clientId, register } from "@/services/user";
 import { ColorType } from "@/context/themes/color";
 import { registerSuccess } from "@/store/user";
 
@@ -57,7 +57,7 @@ export default function Component(prop: { name?: string, email?: string }) {
 
   function loginComponent() {
     return <View>
-      <Inputs value={password} isSecure={true} onChange={handlePasswordChange} onConfirm={(e) => { }} placeholder={t('feature.auth.create_password.input_password_placeholder')}></Inputs>
+      <Inputs value={password} isSecure={true} autoFocus={true} onChange={handlePasswordChange} onConfirm={(e) => { }} placeholder={t('feature.auth.create_password.input_password_placeholder')}></Inputs>
       <View style={{ height: 20 }} />
       <Inputs value={repeat} isSecure={true} onChange={handleRepeatChange} onConfirm={(e) => { }} placeholder={t('feature.auth.create_password.input_password_confirm_placeholder')}></Inputs>
     </View>
@@ -74,7 +74,7 @@ export default function Component(prop: { name?: string, email?: string }) {
       password
     ).then(res => {
       dispatch(registerSuccess(res))
-      
+      clientId()
     }).catch(e => {
       console.log('oppsu')
     })

+ 9 - 3
src/features/auth/components/login.tsx

@@ -8,7 +8,7 @@ import Box from "@/components/layout/Box";
 import { ComponentStatus, TextType } from "@/utils/types";
 import Buttons from "@/components/basic/Buttons";
 import './Auth.scss'
-import { login } from "@/services/user";
+import { clientId, login } from "@/services/user";
 import { useDispatch, useSelector } from "react-redux";
 import Taro from "@tarojs/taro";
 import { ColorType } from "@/context/themes/color";
@@ -67,7 +67,7 @@ export default function Login(props: { register: () => void }) {
 
     function loginComponent() {
         return <View>
-            <Inputs value={name} onChange={handleNameChange} onConfirm={(e) => { }} placeholder={t('feature.auth.login.input_account_placeholder')}></Inputs>
+            <Inputs autoFocus={true} value={name} onChange={handleNameChange} onConfirm={(e) => { }} placeholder={t('feature.auth.login.input_account_placeholder')}></Inputs>
             <View style={{ height: 20 }} />
             <Inputs value={password} isSecure={true} onChange={handlePasswordChange} onConfirm={(e) => { }} placeholder={t('feature.auth.login.input_password_placeholder')}></Inputs>
         </View>
@@ -79,10 +79,16 @@ export default function Login(props: { register: () => void }) {
         }
         console.log('begin login')
         login(name, password).then(res => {
-            console.log(res)
             dispatch(loginSuccess(res))
+            clientId()
             // navigation.goBack(3)
         }).catch(e => {
+            // alert(e.data.error_message)
+            // alert(''+JSON.stringify(e))
+            Taro.showToast({
+                icon:'none',
+                title:e.error_message
+            })
             console.log('error',e)
         })
         // dispatch(login(name, password) as any);

+ 1 - 1
src/features/common/SpecBtns.tsx

@@ -150,7 +150,7 @@ export const ChooseScenarioBtn = (props: { onClick: Function, title: string, bac
                 width: 300,
                 boxSizing: 'border-box',
                 borderRadius: 25,
-                background: props.background,
+                backgroundColor: props.background,
                 paddingLeft: 40,
                 paddingRight: 40,
                 color: 'black',

+ 1 - 0
src/features/trackTimeDuration/hooks/Common.tsx

@@ -21,6 +21,7 @@ export const jumpPage = (url?: string, pageName?: string, navigation?: any, para
         if (params){
             console.log(params)
         }
+        console.log(pageName)
         navigation.push(pageName,params);
     }
     else {

+ 11 - 2
src/pages/account/ChooseAuth.tsx

@@ -130,13 +130,22 @@ export default function Page() {
         jumpPage('/pages/account/Auth', 'Auth', navigation)
     }
 
+    function login1(){
+        jumpPage('/pages/account/Auth', 'Auth', navigation,{isLogin:true})
+    }
+
     return <View className="container choose_container">
         <Texts text='fast16cc' type={TextType.big} />
         <Text className="slogan">{t('page.choose_auth.slogan')}</Text>
         {
             process.env.TARO_ENV == 'weapp' ? <ChooseScenarioBtn disable={btnDisable} title={t('page.choose_auth.btn_wechat')} onClick={getCode} background={ColorType.fast} /> :
-                // <ChooseScenarioBtn disable={btnDisable} title={t('page.choose_auth.btn_signup')} onClick={createAccount1} background={ColorType.fast} />
-                <Text style={{color:'red'}} onClick={createAccount1}>{t('page.choose_auth.btn_signup')}</Text>
+                <ChooseScenarioBtn disable={btnDisable} title={t('page.choose_auth.btn_login')} onClick={login1} background={ColorType.fast} />
+                // <Text style={{color:'red'}} onClick={createAccount1}>{t('page.choose_auth.btn_signup')}</Text>
+        }
+
+        <View style={{height:30}}/>
+        {
+            process.env.TARO_ENV == 'rn' && <ChooseScenarioBtn disable={btnDisable} title={t('page.choose_auth.btn_signup')} onClick={createAccount1} background={ColorType.fast} />
         }
         {/* <ChooseScenarioBtn title={t('page.choose_auth.btn_wechat')} onClick={login} background={ColorType.fast} /> */}
         {/* <View style={{ height: 100 }} /> */}

+ 3 - 2
src/pages/account/Profile.tsx

@@ -109,7 +109,8 @@ export default function Page() {
     }
 
     function goSetting(e) {
-        jumpPage('/pages/account/Setting')
+        console.log('apple')
+        jumpPage('/pages/account/Setting','Setting',navigation)
         if (process.env.TARO_ENV == 'weapp') {
             e.stopPropagation()
         }
@@ -165,7 +166,7 @@ export default function Page() {
             </Box>
             {
                 user.isLogin && <View onClick={goSetting}>
-                    <TableCell title={t('page.more.setting')} showArrow={true} showMarginBottom={true}></TableCell>
+                    <TableCell title={t('page.more.setting')} showArrow={true} showMarginBottom={true} onClick={goSetting}></TableCell>
                 </View>
             }
             {/* {user.isLogin && <Box >

+ 38 - 6
src/pages/account/Setting.tsx

@@ -4,43 +4,75 @@ import TableCell from "@/components/layout/TableCell";
 import { ColorType } from "@/context/themes/color";
 import { ChooseScenarioBtn } from "@/features/common/SpecBtns";
 import { clear, logout } from "@/services/user";
+import { logoutSuccess } from "@/store/user";
 import { View, Text, Button } from "@tarojs/components";
 import Taro from "@tarojs/taro";
 import { useEffect } from "react";
 import { useTranslation } from "react-i18next";
 import { useDispatch, useSelector } from "react-redux";
 
+let useNavigation;
+if (process.env.TARO_ENV == 'rn') {
+    useNavigation = require("@react-navigation/native").useNavigation
+}
+
 export default function Page() {
     const dispatch = useDispatch();
     const { t } = useTranslation()
     const user = useSelector((state: any) => state.user);
+    let navigation;
+    if (useNavigation) {
+        navigation = useNavigation()
+    }
+
     useEffect(() => {
         Taro.setNavigationBarTitle({
             title: t('page.setting.title')
         })
     }, [])
     function logoutF() {
-        
+
         Taro.showModal({
             title: t('feature.common.modal.logout_title'),
             content: t('feature.common.modal.logout_content'),
             success: res => {
                 if (res.confirm) {
-                    dispatch(logout() as any);
+                    logout().then(res => {
+                        dispatch(logoutSuccess())
+                        if (process.env.TARO_ENV == 'weapp') {
+                            Taro.navigateBack();
+                        }
+                        else {
+                            navigation.goBack()
+                        }
+                    })
+                    // dispatch(logout() as any);
                 }
+
+                // if (process.env.TARO_ENV == 'rn'){
+                //     setTimeout(()=>{
+                //         navigation.goBack()
+                //     },1000)
+                // }
             }
         })
 
     }
 
-    return <View style={{ color: '#fff',display:'flex',flexDirection:'column' }}>
-        <View style={{height:20}}/>
-        <TableCell title={t('page.setting.version')} ><Text style={{ opacity: 0.8 }}>1.3.0</Text></TableCell>
+    return <View style={{ color: '#fff', display: 'flex', flexDirection: 'column', flex: 1 }}>
+        <View style={{ height: 20 }} />
+        <TableCell title={t('page.setting.version')} ><Text style={{ opacity: 0.8, color: '#fff' }}>1.3.0</Text></TableCell>
         {/* <Text style={{color:'#9E9E9E',textAlign:'center',fontSize:14}}>v1.2.2</Text> */}
-        
+        {
+            process.env.TARO_ENV == 'rn' && <View style={{ flex: 1 }} />
+        }
+
         <Footer>
             <ChooseScenarioBtn title={t('page.setting.logout')} onClick={logoutF} background={ColorType.fast} />
         </Footer>
+        {
+            process.env.TARO_ENV == 'rn' && <View style={{ height: 50 }} />
+        }
         {/* <Buttons title='退出登录' onClick={logoutF} btnStyle={{ width: 289, marginBottom: 30 }} /> */}
 
         {/* {

+ 1 - 0
src/pages/clock/Clock.weapp.tsx

@@ -93,6 +93,7 @@ export default function IndexPage() {
   }
 
   useEffect(() => {
+    console.log('clock page init')
     global.consoleType = 'idle'
     dispatch(staticResources() as any);
     // dispatch(gobalConfigs() as any);

+ 8 - 18
src/pages/clock/demoA.tsx

@@ -1,25 +1,15 @@
 // import { View } from "@tarojs/components";
-import { useLayoutEffect, useRef, useState } from "react";
-import { LayoutChangeEvent, View } from "react-native";
+import { useEffect, useLayoutEffect, useRef, useState } from "react";
+import { LayoutChangeEvent, View,Text } from "react-native";
 
-export default function DemoA(props:{ children}) {
-    const [height, setHeight] = useState<number>(1000000);
-
-    const handleLayout = (event: LayoutChangeEvent) => {
-        console.log('oppsu1111')
-        const { height } = event.nativeEvent.layout;
-        console.log(height)
-        setHeight(height);
-    };
-    //onLayout={handleLayout}
+export default function DemoA() {
+    useEffect(()=>{
+        alert('a')
+    },[])
 
     return (
-        <View style={{  backgroundColor: 'blue', height:height,width:200 }} >
-            <View onLayout={handleLayout}>
-
-            
-            {props.children}
-            </View>
+        <View style={{  backgroundColor: 'blue', height:100,width:200 }} >
+            <Text style={{color:'red',fontSize:50}}>hello world</Text>
         </View>
     );
 }

+ 13 - 8
src/pages/rn/RNMain.tsx

@@ -9,6 +9,8 @@ import Workout from '../workout/Workout'
 import Profile from '../account/Profile'
 import ChooseAuth from '@/pages/account/ChooseAuth'
 import Auth from '@/pages/account/Auth'
+import Setting from '@/pages/account/Setting'
+import DemoA from '../clock/demoA';
 import { View } from '@tarojs/components';
 
 // 创建底部 Tab 导航器
@@ -51,10 +53,11 @@ const App: React.FC = () => {
 */
 
 export default function RNMain() {
-  // const ClockPage = ()=> <Clock />
-  // const MetricPage = ()=> <Metric />
-  // const ActivityPage = ()=> <Activity />
-  // const ProfilePage = ()=> <Profile />
+  const ClockPage = ()=> <Clock />
+  const MetricPage = ()=> <Metric />
+  const WorkoutPage = ()=> <Workout />
+  const ProfilePage = ()=> <Profile />
+  const DemoPage = ()=><DemoA/>
 
   function tabNavigator() {
     return (
@@ -68,10 +71,11 @@ export default function RNMain() {
         tabBarActiveTintColor: 'white', // 活动标签的颜色
         tabBarInactiveTintColor: 'gray', // 非活动标签的颜色
       })}>
-        <Tab.Screen name="Clock" component={props=> <Clock />} />
-        <Tab.Screen name="Metric" component={props=> <Metric />} />
-        <Tab.Screen name="Activity" component={props=> <Workout />} />
-        <Tab.Screen name="Profile" component={props=> <Profile />} />
+        <Tab.Screen name="Clock" component={ClockPage} />
+        {/* <Tab.Screen name="Demo" component={DemoPage} /> */}
+        <Tab.Screen name="Metric" component={MetricPage} />
+        <Tab.Screen name="Activity" component={WorkoutPage}/>
+        <Tab.Screen name="Profile" component={ProfilePage} />
       </Tab.Navigator>
     )
   }
@@ -101,6 +105,7 @@ export default function RNMain() {
         <Stack.Screen name='Main' component={tabNavigator} options={{ headerShown: false }}/>
         <Stack.Screen name='ChooseAuth' component={ChooseAuth} />
         <Stack.Screen name='Auth' component={Auth} />
+        <Stack.Screen name='Setting' component={Setting} />
       </Stack.Navigator>
     </NavigationContainer>
   )

+ 2 - 1
src/services/http/api.js

@@ -1,4 +1,4 @@
-let online = true;
+let online = false;
 export let baseUrl = online ? 'https://api.fast.liveplus.fun' : 'https://api.fast.dev.liveplus.fun';
 export let imgUrl = online
     ? 'https://api.fast.liveplus.fun/static/image/'
@@ -16,6 +16,7 @@ export const API_STATIC_RESOURCES = `${baseUrl}/api/static-resource-urls`
 export const API_GLOBAL_CONFIGS = `${baseUrl}/api/system/global-configs`
 export const API_CONFIGS = `${baseUrl}/api/system/configs`
 export const API_USER_INFO = `${baseUrl}/api/user/info`
+export const API_CLIENT_ID = `${baseUrl}/api/user/client-id`
 export const API_CLEAR_FAST = `${baseUrl}/api/clear/fasting`
 export const API_CLEAR_SLEEP = `${baseUrl}/api/sleep/clear-my-data`
 export const API_CLEAR_MIX = `${baseUrl}/api/clear-data/FAST_SLEEP`

+ 10 - 1
src/services/http/request.ts

@@ -110,6 +110,12 @@ export async function request<T>(param: RequestParam): Promise<T> {
                     if (process.env.TARO_ENV=='weapp'){
                         Taro.stopPullDownRefresh()
                     }
+                    else if (process.env.TARO_ENV=='rn'){
+                        if (url.indexOf('login/password')!=-1){
+                            reject(data);
+                            return;
+                        }
+                    }
                     global.dispatch(logoutSuccess());
                 } else if (statusCode == 500 && response.data.error_code == 'WX_STEP_PARSE_FAIL') {
                     //单独对计步第一次请求失败处理
@@ -124,7 +130,10 @@ export async function request<T>(param: RequestParam): Promise<T> {
                 }
             },
             fail: err => {
-                console.log('oooooooddd', err)
+                if ((err as any).statusCode == 204){
+                    resolve()
+                    return;
+                }
                 // global.postBtnUpdateStatus('idle')
                 // console.log('oppsu');
                 // clearTimeout(timer);

+ 34 - 10
src/services/user.tsx

@@ -1,5 +1,5 @@
 import Taro from '@tarojs/taro'
-import { API_OAUTH_LOGIN, API_REGISTER, API_LOGIN, API_LOGOUT, API_CLEAR_USER, API_USER_INFO, API_CHECK_UNIQUE } from './http/api'
+import { API_OAUTH_LOGIN, API_REGISTER, API_LOGIN, API_LOGOUT, API_CLEAR_USER, API_USER_INFO, API_CHECK_UNIQUE, API_CLIENT_ID } from './http/api'
 import { request } from './http/request'
 import { clearSuccess, getInfoSuccess, loginSuccess, logoutSuccess, registerSuccess, updateSuccess } from '@/store/user'
 
@@ -78,13 +78,25 @@ export const register = (name: string, email: string, password: string) => {
     })
 }
 
-export const logout = () => (dispatch: any) => {
-    request({
-        url: API_LOGOUT, method: 'GET', data: {}
-    }).then(_ => {
-        dispatch(logoutSuccess());
-        Taro.navigateBack();
+export const logout = () => {
+
+    return new Promise((resolve, reject) => {
+        request({
+            url: API_LOGOUT, method: 'GET', data: {}
+        }).then(res => {
+            return resolve(res)
+            // dispatch(getInfoSuccess(res));
+        }).catch(e => {
+            return reject(e)
+        })
     })
+
+    // request({
+    //     url: API_LOGOUT, method: 'GET', data: {}
+    // }).then(_ => {
+    //     dispatch(logoutSuccess());
+    //     Taro.navigateBack();
+    // })
 }
 
 export const clear = () => (dispatch: any) => {
@@ -103,11 +115,11 @@ export const getInfo = () => {
         }).then(res => {
             return resolve(res)
             // dispatch(getInfoSuccess(res));
-        }).catch(e=>{
+        }).catch(e => {
             return reject(e)
         })
     })
-    
+
 }
 
 
@@ -118,9 +130,21 @@ export const update = (params: any) => {
         }).then(res => {
             // dispatch(updateSuccess(params));
             resolve(res)
-        }).catch(e=>{
+        }).catch(e => {
             reject(e)
         })
     })
 }
 
+export const clientId = () => {
+    if (global.registerID) {
+        request({
+            url: API_CLIENT_ID, method: 'POST', data: {
+                provider: 'JIGUANG',
+                client_type: 'IOS',
+                client_id: global.registerID
+            }
+        })
+    }
+}
+

+ 12 - 3
src/utils/push.tsx

@@ -1,9 +1,10 @@
+
+import { clientId } from '@/services/user';
 import JPush from 'jpush-react-native';
 
 export default function jgPush() {
     // const JPush = require('jpush-react-native')
     // debugger
-    console.log('asefasf')
     JPush.init({ appKey: "7cf918ada725a9e9aecc8a17", channel: "dev", production: false });
     //连接状态
     JPush.addConnectEventListener(result => {
@@ -16,7 +17,7 @@ export default function jgPush() {
     //通知回调
     var notificationListener = result => {
         console.log("notificationListener:" + JSON.stringify(result))
-        alert(JSON.stringify(result))
+        // alert(JSON.stringify(result))
     };
     JPush.addNotificationListener(notificationListener);
     //本地通知回调
@@ -33,7 +34,7 @@ export default function jgPush() {
     JPush.pageEnterTo("HomePage") // 进入首页,当页面退出时请调用 JPush.pageLeave('HomePage')
     var inappMessageListener = result => {
         console.log("inappMessageListener:" + JSON.stringify(result))
-        alert(JSON.stringify(result))
+        // alert(JSON.stringify(result))
     };
     JPush.addInappMessageListener(inappMessageListener);
     //tag alias事件回调
@@ -46,4 +47,12 @@ export default function jgPush() {
         console.log("mobileNumberListener:" + JSON.stringify(result))
     };
     JPush.addMobileNumberListener(mobileNumberListener);
+
+    JPush.getRegistrationID(obj => {
+        // alert('register id=' + obj.registerID)
+        global.registerID = obj.registerID
+        setTimeout(()=>{
+            clientId()
+        },3000)
+    })
 }