feat: 登录交互
@ -3,7 +3,7 @@ import { registerBackgroundUploadTask, triggerManualUpload } from '@/components/
|
|||||||
import * as MediaLibrary from 'expo-media-library';
|
import * as MediaLibrary from 'expo-media-library';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import * as SecureStore from 'expo-secure-store';
|
import * as SecureStore from 'expo-secure-store';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Platform, Text, TouchableOpacity, View } from 'react-native';
|
import { Platform, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
@ -15,20 +15,26 @@ export default function HomeScreen() {
|
|||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
const [token, setToken] = useState('');
|
||||||
|
const tokenInterval = useRef<NodeJS.Timeout | number>(null);
|
||||||
|
const isMounted = useRef(true);
|
||||||
|
|
||||||
|
const getAuthToken = async (): Promise<string> => {
|
||||||
|
let tokenValue = '';
|
||||||
|
if (Platform.OS === 'web') {
|
||||||
|
tokenValue = localStorage.getItem('token') || '';
|
||||||
|
} else {
|
||||||
|
tokenValue = (await SecureStore.getItemAsync('token')) || '';
|
||||||
|
}
|
||||||
|
setToken(tokenValue); // 只在获取到新token时更新状态
|
||||||
|
return tokenValue;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkAuthStatus = async () => {
|
const checkAuthStatus = async () => {
|
||||||
try {
|
try {
|
||||||
let token;
|
|
||||||
if (Platform.OS === 'web') {
|
|
||||||
token = localStorage.getItem('token') || '';
|
|
||||||
} else {
|
|
||||||
token = await SecureStore.getItemAsync('token') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const loggedIn = !!token;
|
const loggedIn = !!token;
|
||||||
setIsLoggedIn(loggedIn);
|
setIsLoggedIn(loggedIn);
|
||||||
console.log(loggedIn);
|
|
||||||
|
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
// 已登录,请求必要的权限
|
// 已登录,请求必要的权限
|
||||||
@ -49,10 +55,38 @@ export default function HomeScreen() {
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
checkAuthStatus();
|
checkAuthStatus();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// 轮询获取token
|
||||||
|
useEffect(() => {
|
||||||
|
// 如果已经有token,直接返回
|
||||||
|
if (token) {
|
||||||
|
if (tokenInterval.current) {
|
||||||
|
clearInterval(tokenInterval.current);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!tokenInterval.current) return;
|
||||||
|
// 设置轮询
|
||||||
|
tokenInterval.current = setInterval(async () => {
|
||||||
|
if (isMounted.current) {
|
||||||
|
const currentToken = await getAuthToken();
|
||||||
|
// 如果获取到token,清除定时器
|
||||||
|
if (currentToken && tokenInterval.current) {
|
||||||
|
clearInterval(tokenInterval.current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
// 返回清理函数
|
||||||
|
return () => {
|
||||||
|
if (tokenInterval.current) {
|
||||||
|
clearInterval(tokenInterval.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [token]); // 添加token作为依赖
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-bgPrimary justify-center items-center">
|
<View className="flex-1 bg-bgPrimary justify-center items-center">
|
||||||
@ -91,7 +125,7 @@ export default function HomeScreen() {
|
|||||||
{"\n"}
|
{"\n"}
|
||||||
{t('auth.welcomeAwaken.back', { ns: 'login' })}
|
{t('auth.welcomeAwaken.back', { ns: 'login' })}
|
||||||
</Text>
|
</Text>
|
||||||
{/* <MessagePush /> */}
|
|
||||||
{/* 唤醒按钮 */}
|
{/* 唤醒按钮 */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
className="bg-white rounded-full px-10 py-4 shadow-[0_2px_4px_rgba(0,0,0,0.1)] w-full items-center"
|
className="bg-white rounded-full px-10 py-4 shadow-[0_2px_4px_rgba(0,0,0,0.1)] w-full items-center"
|
||||||
|
|||||||
@ -5,11 +5,12 @@ import UserName from '@/components/user-message.tsx/userName';
|
|||||||
import { fetchApi } from '@/lib/server-api-util';
|
import { fetchApi } from '@/lib/server-api-util';
|
||||||
import { FileUploadItem } from '@/types/upload';
|
import { FileUploadItem } from '@/types/upload';
|
||||||
import { User } from '@/types/user';
|
import { User } from '@/types/user';
|
||||||
import { useEffect, useState } from 'react';
|
import { useLocalSearchParams } from 'expo-router';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
import { KeyboardAvoidingView, Platform, ScrollView, StatusBar, View } from 'react-native';
|
import { KeyboardAvoidingView, Platform, ScrollView, StatusBar, View } from 'react-native';
|
||||||
export type Steps = "userName" | "look" | "choice" | "done";
|
export type Steps = "userName" | "look" | "choice" | "done";
|
||||||
export default function UserMessage() {
|
export default function UserMessage() {
|
||||||
// 步骤
|
// 步骤
|
||||||
const [steps, setSteps] = useState<Steps>("userName")
|
const [steps, setSteps] = useState<Steps>("userName")
|
||||||
const [username, setUsername] = useState('')
|
const [username, setUsername] = useState('')
|
||||||
const [avatar, setAvatar] = useState('')
|
const [avatar, setAvatar] = useState('')
|
||||||
@ -18,6 +19,10 @@ export default function UserMessage() {
|
|||||||
const [userInfo, setUserInfo] = useState<User | null>(null);
|
const [userInfo, setUserInfo] = useState<User | null>(null);
|
||||||
const statusBarHeight = StatusBar?.currentHeight ?? 0;
|
const statusBarHeight = StatusBar?.currentHeight ?? 0;
|
||||||
|
|
||||||
|
// 获取路由参数
|
||||||
|
const params = useLocalSearchParams();
|
||||||
|
const { username: usernameParam } = params;
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
const getUserInfo = async () => {
|
const getUserInfo = async () => {
|
||||||
const res = await fetchApi<User>("/iam/user-info");
|
const res = await fetchApi<User>("/iam/user-info");
|
||||||
@ -44,7 +49,7 @@ export default function UserMessage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getUserInfo();
|
getUserInfo();
|
||||||
setSteps("userName")
|
setSteps("userName")
|
||||||
}, []);
|
}, [usernameParam]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<svg width="30" height="27" viewBox="0 0 30 27" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
|
||||||
<path d="M21.2222 7.25H27.4444C28.3036 7.25 29 7.94955 29 8.8125V26L23.8153 21.6734C23.536 21.4403 23.1834 21.3125 22.8203 21.3125H10.3333C9.47422 21.3125 8.77778 20.6129 8.77778 19.75V15.0625H7.17969C6.8166 15.0625 6.46396 15.1901 6.18468 15.4231L1 19.7505V2.5625C1 1.69956 1.69645 1 2.55556 1H19.6667C20.5258 1 21.2222 1.69956 21.2222 2.5625V7.25Z" fill="#4C320C"/>
|
<path d="M4.913 2.658c2.075-.27 4.19-.408 6.337-.408 2.147 0 4.262.139 6.337.408 1.922.25 3.291 1.861 3.405 3.727a4.403 4.403 0 0 0-1.032-.211 50.89 50.89 0 0 0-8.42 0c-2.358.196-4.04 2.19-4.04 4.434v4.286a4.47 4.47 0 0 0 2.433 3.984L7.28 21.53A.75.75 0 0 1 6 21v-4.03a48.527 48.527 0 0 1-1.087-.128C2.905 16.58 1.5 14.833 1.5 12.862V6.638c0-1.97 1.405-3.718 3.413-3.979Z" />
|
||||||
<path d="M21.2222 7.25H27.4444C28.3036 7.25 29 7.94956 29 8.8125V26L23.8153 21.6734C23.536 21.4403 23.1834 21.3125 22.8203 21.3125H10.3333C9.47422 21.3125 8.77778 20.6129 8.77778 19.75V15.0625M21.2222 7.25V2.5625C21.2222 1.69956 20.5258 1 19.6667 1H2.55556C1.69645 1 1 1.69956 1 2.5625V19.7505L6.18468 15.4231C6.46397 15.1901 6.8166 15.0625 7.17969 15.0625H8.77778M21.2222 7.25V13.5C21.2222 14.3629 20.5258 15.0625 19.6667 15.0625H8.77778" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M15.75 7.5c-1.376 0-2.739.057-4.086.169C10.124 7.797 9 9.103 9 10.609v4.285c0 1.507 1.128 2.814 2.67 2.94 1.243.102 2.5.157 3.768.165l2.782 2.781a.75.75 0 0 0 1.28-.53v-2.39l.33-.026c1.542-.125 2.67-1.433 2.67-2.94v-4.286c0-1.505-1.125-2.811-2.664-2.94A49.392 49.392 0 0 0 15.75 7.5Z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 992 B After Width: | Height: | Size: 780 B |
3
assets/icons/svg/chatNotIn.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 717 B |
@ -1 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M24 20a7 7 0 1 0 0-14a7 7 0 0 0 0 14M6 40.8V42h36v-1.2c0-4.48 0-6.72-.872-8.432a8 8 0 0 0-3.496-3.496C35.92 28 33.68 28 29.2 28H18.8c-4.48 0-6.72 0-8.432.872a8 8 0 0 0-3.496 3.496C6 34.08 6 36.32 6 40.8"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
|
||||||
|
<path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 344 B |
3
assets/icons/svg/personNotIn.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 350 B |
BIN
assets/images/png/owner/ask.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
@ -7,6 +7,7 @@ import { Message, Video } from "@/types/ask";
|
|||||||
import { MaterialItem } from "@/types/personal-info";
|
import { MaterialItem } from "@/types/personal-info";
|
||||||
import { useVideoPlayer, VideoView } from 'expo-video';
|
import { useVideoPlayer, VideoView } from 'expo-video';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
FlatList,
|
FlatList,
|
||||||
Image,
|
Image,
|
||||||
@ -40,7 +41,7 @@ const renderMessage = ({ insets, item, sessionId, setModalVisible, modalVisible,
|
|||||||
const isVideo = (data: Video | MaterialItem): data is Video => {
|
const isVideo = (data: Video | MaterialItem): data is Video => {
|
||||||
return 'video' in data;
|
return 'video' in data;
|
||||||
};
|
};
|
||||||
|
const { t } = useTranslation();
|
||||||
// 创建一个新的 VideoPlayer 组件
|
// 创建一个新的 VideoPlayer 组件
|
||||||
const VideoPlayer = ({
|
const VideoPlayer = ({
|
||||||
videoUrl,
|
videoUrl,
|
||||||
@ -222,7 +223,7 @@ const renderMessage = ({ insets, item, sessionId, setModalVisible, modalVisible,
|
|||||||
<TouchableOpacity onPress={() => setModalDetailsVisible(false)}>
|
<TouchableOpacity onPress={() => setModalDetailsVisible(false)}>
|
||||||
<ReturnArrow />
|
<ReturnArrow />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<ThemedText style={detailsStyles.headerText}>Select Photo</ThemedText>
|
<ThemedText style={detailsStyles.headerText}>{t('ask.selectPhoto', { ns: 'ask' })}</ThemedText>
|
||||||
<FolderSvg />
|
<FolderSvg />
|
||||||
</View>
|
</View>
|
||||||
<View style={{ overflow: 'scroll', height: "100%" }}>
|
<View style={{ overflow: 'scroll', height: "100%" }}>
|
||||||
@ -292,7 +293,7 @@ const renderMessage = ({ insets, item, sessionId, setModalVisible, modalVisible,
|
|||||||
activeOpacity={0.8}
|
activeOpacity={0.8}
|
||||||
>
|
>
|
||||||
<Text style={detailsStyles.continueButtonText}>
|
<Text style={detailsStyles.continueButtonText}>
|
||||||
Continue Asking
|
{t('ask.continueAsking', { ns: 'ask' })}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import ChatInSvg from "@/assets/icons/svg/chatIn.svg";
|
import ChatInSvg from "@/assets/icons/svg/chatIn.svg";
|
||||||
import NavbarSvg from "@/assets/icons/svg/navbar.svg";
|
import ChatNotInSvg from "@/assets/icons/svg/chatNotIn.svg";
|
||||||
import PersonInSvg from "@/assets/icons/svg/personIn.svg";
|
import PersonInSvg from "@/assets/icons/svg/personIn.svg";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import PersonNotInSvg from "@/assets/icons/svg/personNotIn.svg";
|
||||||
import { router, usePathname } from "expo-router";
|
import { router, usePathname } from "expo-router";
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Dimensions, Platform, TouchableOpacity, View } from 'react-native';
|
import { Dimensions, Image, Platform, TouchableOpacity, View } from 'react-native';
|
||||||
import { Circle, Ellipse, G, Mask, Path, Rect, Svg } from 'react-native-svg';
|
import { Circle, Ellipse, G, Mask, Path, Rect, Svg } from 'react-native-svg';
|
||||||
|
|
||||||
const AskNavbar = () => {
|
const AskNavbar = () => {
|
||||||
@ -21,10 +21,11 @@ const AskNavbar = () => {
|
|||||||
shadowRadius: 8,
|
shadowRadius: 8,
|
||||||
elevation: 10, // For Android
|
elevation: 10, // For Android
|
||||||
}}>
|
}}>
|
||||||
<NavbarSvg className="w-full h-full" />
|
{/* <NavbarSvg className="w-[150%] h-full" /> */}
|
||||||
|
<Image source={require('@/assets/images/png/owner/ask.png')} style={{ width: width, height: 80, resizeMode: 'cover' }} />
|
||||||
<View className="absolute bottom-0 top-0 left-0 right-0 flex flex-row justify-between items-center px-[2rem]">
|
<View className="absolute bottom-0 top-0 left-0 right-0 flex flex-row justify-between items-center px-[2rem]">
|
||||||
<TouchableOpacity onPress={() => router.push('/memo-list')} >
|
<TouchableOpacity onPress={() => router.push('/memo-list')} style={{ padding: 16 }}>
|
||||||
{pathname === "/memo-list" ? <ChatInSvg /> : <Ionicons name="chatbubbles-outline" size={24} color="#4C320C" />}
|
{pathname === "/memo-list" ? <ChatInSvg width={24} height={24} /> : <ChatNotInSvg width={24} height={24} />}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@ -34,7 +35,7 @@ const AskNavbar = () => {
|
|||||||
params: { newSession: "true" }
|
params: { newSession: "true" }
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className={`${Platform.OS === 'web' ? '-mt-[4rem]' : width <= 375 ? '-mt-[5rem] ml-[2rem]' : '-mt-[5rem] ml-[0.8rem]'}`}
|
className={`${Platform.OS === 'web' ? '-mt-[4rem]' : width <= 375 ? '-mt-[5rem]' : '-mt-[5rem]'}`}
|
||||||
>
|
>
|
||||||
<View style={{
|
<View style={{
|
||||||
shadowColor: '#FFB645',
|
shadowColor: '#FFB645',
|
||||||
@ -74,9 +75,9 @@ const AskNavbar = () => {
|
|||||||
</Svg>
|
</Svg>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity onPress={() => router.push('/owner')}>
|
<TouchableOpacity onPress={() => router.push('/owner')} style={{ padding: 16 }}>
|
||||||
<View>
|
<View>
|
||||||
{pathname === "/owner" ? <PersonInSvg width={24} height={24} /> : <Ionicons name="person-outline" size={24} color="#4C320C" />}
|
{pathname === "/owner" ? <PersonInSvg width={24} height={24} /> : <PersonNotInSvg width={24} height={24} />}
|
||||||
{/* <View className="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full" /> */}
|
{/* <View className="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full" /> */}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
@ -45,9 +45,13 @@ const Login = ({ updateUrlParam, setError, setShowPassword, showPassword }: Logi
|
|||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
login({ ...res, email: res?.account }, res.access_token || '');
|
login({ ...res, email: res?.account }, res.access_token || '');
|
||||||
router.replace('/user-message');
|
const userInfo = await fetchApi<User>("/iam/user-info");
|
||||||
|
if (userInfo?.nickname) {
|
||||||
|
router.replace('/ask');
|
||||||
|
} else {
|
||||||
|
router.replace('/user-message');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('Login failed', error);
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,7 +125,13 @@ const UserInfo = (props: UserInfoProps) => {
|
|||||||
}
|
}
|
||||||
<TouchableOpacity style={styles.edit} onPress={() => {
|
<TouchableOpacity style={styles.edit} onPress={() => {
|
||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
router.push('/user-message')
|
// 携带参数跳转
|
||||||
|
router.push({
|
||||||
|
pathname: '/user-message',
|
||||||
|
params: {
|
||||||
|
username: "true"
|
||||||
|
}
|
||||||
|
});
|
||||||
}}>
|
}}>
|
||||||
<EditSvg />
|
<EditSvg />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
"hi": "Hi,",
|
"hi": "Hi,",
|
||||||
"iAmMemo": "I'm Memo!",
|
"iAmMemo": "I'm Memo!",
|
||||||
"ready": "Ready to wake up your memories?",
|
"ready": "Ready to wake up your memories?",
|
||||||
"justAsk": "Just ask MeMo, let me bring them back to life!"
|
"justAsk": "Just ask MeMo, let me bring them back to life!",
|
||||||
|
"selectPhoto": "Select Photo",
|
||||||
|
"continueAsking": "Continue Asking"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,6 +3,8 @@
|
|||||||
"hi": "Hi,",
|
"hi": "Hi,",
|
||||||
"iAmMemo": "I'm Memo!",
|
"iAmMemo": "I'm Memo!",
|
||||||
"ready": "Ready to wake up your memories?",
|
"ready": "Ready to wake up your memories?",
|
||||||
"justAsk": "Just ask MeMo, let me bring them back to life!"
|
"justAsk": "Just ask MeMo, let me bring them back to life!",
|
||||||
|
"selectPhoto": "Select Photo",
|
||||||
|
"continueAsking": "Continue Asking"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||