暂无描述

ProfilePage.vue 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <template>
  2. <ion-page>
  3. <ion-header>
  4. <ion-toolbar>
  5. <ion-title>User Profile</ion-title>
  6. </ion-toolbar>
  7. </ion-header>
  8. <ion-content>
  9. <!--
  10. <ion-button @click='clear'>Clear Pref</ion-button>
  11. <ion-button @click='btnLogout'>Btn Logout</ion-button> -->
  12. <template v-if="refIsLogin && myprofile">
  13. <ion-img :src="myprofile.photo" v-if="myprofile.photo" class='profilePhoto' />
  14. <div class='ion-text-center'>
  15. <ion-button class='ion-margin-vertical' id='myqr-modal'>My QRCODE</ion-button>
  16. </div>
  17. <ion-modal ref="modal" trigger="myqr-modal">
  18. <ion-content class="ion-padding">
  19. <ion-img :src="'data:image/png;base64,'+myprofile.qrcode"/><br>
  20. <h5 class='ion-text-center'>{{ myprofile.text_qrcode }}</h5>
  21. <ion-button expand="full" @click="modal.$el.dismiss()">Close</ion-button>
  22. </ion-content>
  23. </ion-modal>
  24. <ion-list>
  25. <ion-list-header>
  26. <ion-label>Info</ion-label>
  27. </ion-list-header>
  28. <ion-item>
  29. <input type='file' @change="onUploadPhoto" />
  30. </ion-item>
  31. <ion-item>
  32. <ion-input label="First Name" label-placement="floating" placeholder="Enter text" :value="myprofile.first_name" @ionInput="onUpdateProfile" ref="first_name" debounce=1000></ion-input>
  33. <ion-input label="Last Name" label-placement="floating" placeholder="Enter text" :value="myprofile.last_name" @ionInput="onUpdateProfile" ref="last_name" debounce=1000></ion-input>
  34. </ion-item>
  35. <ion-item>
  36. <ion-label>Birthdate</ion-label>
  37. <ion-datetime-button datetime="datetime"></ion-datetime-button>
  38. <ion-modal :keep-contents-mounted="true">
  39. <ion-datetime id="datetime" presentation="date" :value="myprofile.birth_date" ref='birthdate' @ionBlur="onUpdateProfile" ></ion-datetime>
  40. </ion-modal>
  41. </ion-item>
  42. <ion-item>
  43. <ion-textarea label="Address" label-placement="floating" placeholder="Enter text" ref='loc' :value="myprofile.location" @ionInput="onUpdateProfile" debounce=1000></ion-textarea>
  44. </ion-item>
  45. <ion-item>
  46. <ion-input label="Tel." type="tel" ref="tel" placeholder="888-888-8888" :value="myprofile.phone_number" @ionInput="onUpdateProfile" debounce=1000></ion-input>
  47. </ion-item>
  48. <ion-list-header>
  49. <ion-label>Notifications</ion-label>
  50. </ion-list-header>
  51. <ion-item>
  52. <ion-toggle v-model="noti.liveNoti" color="primary" @ionChange="onNoti">Receive Live Notifications</ion-toggle>
  53. </ion-item>
  54. <ion-item>
  55. <ion-toggle v-model="noti.newsNoti" @ionChange="onNoti">Receive News Update</ion-toggle>
  56. </ion-item>
  57. <ion-list-header>
  58. <ion-label>Select Courses You like</ion-label>
  59. </ion-list-header>
  60. <ion-item>
  61. <ion-select aria-label="Training Courses" placeholder="Select Course You like"
  62. :compareWith="compareWith" @ionChange="handleChange($event)">
  63. <ion-select-option :value="c" v-for="c in list_courses">{{ c.name }}</ion-select-option>
  64. </ion-select>
  65. </ion-item>
  66. </ion-list>
  67. <div class='ion-padding' v-if="myprofile">
  68. <h1>I love ...</h1>
  69. <ion-chip v-for="s in myprofile.course_likes">
  70. <ion-label>{{ s.name }}</ion-label>
  71. <ion-icon :icon="close" @click="removeItem(s)" color="dark"></ion-icon>
  72. </ion-chip>
  73. </div>
  74. <ion-button @click='fbLogout' expand='full'>Logout</ion-button>
  75. </template>
  76. <template v-else>
  77. <ion-img src="/images/circlelogo-trans.png" style='width:200px; margin:10px auto' />
  78. <LoginForm @user-login="userLogin" />
  79. <ion-grid>
  80. <ion-row>
  81. <ion-col size=6>
  82. <ion-button fill='clear' router-link="/tabs/signup">Signup</ion-button>
  83. </ion-col>
  84. <ion-col size=6>
  85. <ion-button @click="openReset" fill='clear' class='ion-float-right'>Reset Password</ion-button>
  86. </ion-col>
  87. </ion-row>
  88. </ion-grid>
  89. <div color="light" v-if="formError" class='ion-margin ion-text-center'>{{ formError }}</div>
  90. <ion-grid>
  91. <h3 class='ion-text-center'>Login With</h3>
  92. <ion-row class='ion-justify-content-center'>
  93. <ion-col size=12>
  94. <ion-button @click='fbLogin' expand='full'>
  95. <ion-icon slot="start" :icon="logoFacebook"></ion-icon>
  96. Facebook</ion-button>
  97. </ion-col>
  98. <ion-col size=12>
  99. <ion-button @click='ggLoginClick' expand='full'>
  100. <ion-icon slot="start" :icon="logoGoogle"></ion-icon>
  101. Google</ion-button>
  102. </ion-col>
  103. <ion-col size=12 v-if="isPlatform('ios')">
  104. <ion-button @click='onAppleLogin' expand='full'>
  105. <ion-icon slot="start" :icon="logoApple"></ion-icon>
  106. Apple</ion-button>
  107. </ion-col>
  108. </ion-row>
  109. </ion-grid>
  110. </template>
  111. </ion-content>
  112. </ion-page>
  113. </template>
  114. <script setup lang="ts">
  115. import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonButton, onIonViewWillEnter, IonRow, IonCol, IonGrid, IonToggle, IonLabel, IonChip, IonIcon, IonSelect, IonSelectOption, IonList, IonListHeader, IonItem, IonText, IonImg, isPlatform, IonDatetime, IonDatetimeButton, IonModal, IonTextarea, IonInput } from '@ionic/vue';
  116. import ExploreContainer from '@/components/ExploreContainer.vue';
  117. import LoginForm from '@/components/LoginForm.vue';
  118. import { defineComponent, onMounted } from 'vue';
  119. import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth';
  120. import { facebookLogin, setUserToken, logout, clearPref, BASE_URL, listCourses, getPref, likeCourse, isLogin, ggLogin, updateNoti, APPLE_CALLBACK, appleLogin, updateProfile, uploadPhoto } from '@/composable/settings';
  121. import { InAppBrowser } from '@capgo/inappbrowser'
  122. import { close, closeCircle, pin, logoApple, logoFacebook, logoGoogle } from 'ionicons/icons';
  123. import {
  124. SignInWithApple,
  125. SignInWithAppleResponse,
  126. SignInWithAppleOptions,
  127. } from '@capacitor-community/apple-sign-in';
  128. let apple_options: SignInWithAppleOptions = {
  129. clientId: 'net.simplico.tmtlive',
  130. redirectURI: APPLE_CALLBACK,
  131. scopes: 'email name',
  132. state: 'TMTLIVE',
  133. nonce: 'nonce',
  134. };
  135. onMounted(() => {
  136. GoogleAuth.initialize();
  137. });
  138. import {
  139. FacebookLogin,
  140. FacebookLoginResponse,
  141. } from '@capacitor-community/facebook-login';
  142. import { ref, reactive } from 'vue';
  143. const FACEBOOK_PERMISSIONS = [
  144. 'email',
  145. 'public_profile',
  146. //'user_photos',
  147. //'user_gender',
  148. ];
  149. const refIsLogin = ref(false)
  150. const liveNoti = ref(true)
  151. const favCourseNoti = ref(true)
  152. const newsNoti = ref(true)
  153. const list_courses = ref([])
  154. const formError = ref(null)
  155. const selecteds = ref([])
  156. const myprofile = ref()
  157. const first_name = ref();
  158. const last_name = ref();
  159. const birthdate = ref();
  160. const loc = ref();
  161. const tel = ref();
  162. const modal = ref();
  163. const noti = reactive({liveNoti: true, newsNoti: true});
  164. const openReset = async () => {
  165. console.log("open Reset")
  166. const browser = await InAppBrowser.open({ url: BASE_URL + "accounts/password_reset/" });
  167. console.info("bw ", browser)
  168. InAppBrowser.addListener("urlChangeEvent", urlChange)
  169. }
  170. const removeItem = async(s) => {
  171. /*
  172. let temp = selecteds.value
  173. temp = temp.filter( e => e !== s)
  174. console.log(temp)
  175. selecteds.value = temp*/
  176. myprofile.value = await likeCourse(s.id)
  177. }
  178. const urlChange = (event) => {
  179. console.info("url change ", event)
  180. if(event.url.includes("/accounts/password_reset/done/")) {
  181. setTimeout(async () => {
  182. InAppBrowser.close()
  183. }, 3000)
  184. }else {
  185. }
  186. //accounts/password_reset/done/
  187. }
  188. const ggLoginClick = async () => {
  189. console.log("ggLogin")
  190. let response = null;
  191. try{
  192. response = await GoogleAuth.signIn();
  193. }catch(error) {
  194. console.log("Error ", error);
  195. }
  196. let t1 = response.authentication.accessToken;
  197. let t2 = response.authentication.idToken;
  198. let t3 = response.authentication.refreshToken;
  199. let token = response.authentication.idToken
  200. console.log(token)
  201. if(response.authentication) {
  202. try{
  203. let d = await ggLogin(t1, t2)
  204. await setUserToken(d.key)
  205. myprofile.value = await getPref()
  206. refIsLogin.value = true
  207. location.reload();
  208. }catch(error) {
  209. console.log(error);
  210. formError.value = error.response.data.non_field_errors.join("\n");
  211. console.log(error)
  212. }
  213. }
  214. }
  215. const fbLogin = async () => {
  216. const result = await (<FacebookLoginResponse>(
  217. FacebookLogin.login({ permissions: FACEBOOK_PERMISSIONS })
  218. ));
  219. console.log("-- fbLogin --")
  220. console.info(result.accessToken)
  221. if (result.accessToken) {
  222. try{
  223. let d = await facebookLogin(result.accessToken)
  224. console.log("facebook authen ", d)
  225. await setUserToken(d.key)
  226. myprofile.value = await getPref()
  227. await getProfile()
  228. // Login successful.
  229. refIsLogin.value = true
  230. location.reload();
  231. }catch(error) {
  232. formError.value = error.response.data.non_field_errors.join("\n");
  233. }
  234. console.log(`Facebook access token is ${result.accessToken.token}`);
  235. }
  236. }
  237. const fbLogout = async () => {
  238. await FacebookLogin.logout();
  239. await GoogleAuth.signOut()
  240. refIsLogin.value = false
  241. await logout()
  242. }
  243. const btnLogout = async () => {
  244. refIsLogin.value = false
  245. await logout()
  246. }
  247. onIonViewWillEnter(async () => {
  248. console.log("ion view will enter")
  249. console.log(await isLogin())
  250. /*
  251. const result = await (<FacebookLoginResponse>(
  252. FacebookLogin.getCurrentAccessToken()
  253. ));
  254. console.log(result == undefined)
  255. if (result != undefined && result.accessToken) {
  256. console.log(`Facebook access token is ${result.accessToken.token}`);
  257. refIsLogin.value = true
  258. await getProfile()
  259. }else {
  260. refIsLogin.value = false
  261. }*/
  262. list_courses.value = await listCourses()
  263. //ggCheckLogin()
  264. //appleCheckLogin()
  265. refIsLogin.value = await isLogin()
  266. if( refIsLogin.value == true ) {
  267. myprofile.value = await getPref()
  268. console.log(myprofile.value)
  269. noti.liveNoti = myprofile.value.live_noti;
  270. noti.newsNoti = myprofile.value.news_noti;
  271. }
  272. })
  273. const getProfile = async () => {
  274. const result = await FacebookLogin.getProfile<{
  275. email: string;
  276. }>({ fields: ['email'] });
  277. console.log(`Facebook user's email is ${result.email}`);
  278. }
  279. const ggCheckLogin = async() => {
  280. GoogleAuth.refresh()
  281. .then(async (data) => {
  282. if (data.accessToken) {
  283. refIsLogin.value = true
  284. myprofile.value = await getPref()
  285. }
  286. })
  287. .catch((error) => {
  288. if (error.type === 'userLoggedOut') {
  289. //this.signin()
  290. }
  291. });
  292. }
  293. const appleCheckLogin = async() => {
  294. SignInWithApple.authorize(apple_options)
  295. .then(async (result: SignInWithAppleResponse) => {
  296. console.debug(result);
  297. // Handle user information
  298. try{
  299. //let d = await appleLogin(result.response)
  300. //console.log("apple authen ", d)
  301. //await setUserToken(d.key)
  302. myprofile.value = await getPref()
  303. //await getProfile()
  304. // Login successful.
  305. refIsLogin.value = true
  306. }catch(error) {
  307. console.log("apple check login ", error);
  308. //formError.value = error.response.data.non_field_errors[0]
  309. }
  310. // Validate token with server and create new session
  311. })
  312. .catch(error => {
  313. // Handle error
  314. });
  315. }
  316. const clear = async() => {
  317. await clearPref()
  318. }
  319. const userLogin = async (data) => {
  320. console.info("user login = ", data)
  321. console.info(data.key)
  322. if(data.error) {
  323. console.log("ERROR")
  324. }else {
  325. console.log("Success")
  326. await setUserToken(data.key)
  327. myprofile.value = await getPref()
  328. // Login successful.
  329. refIsLogin.value = true
  330. }
  331. }
  332. const compareWith = (o1, o2) => {
  333. return o1 && o2 ? o1.id === o2.id : o1 === o2;
  334. }
  335. const handleChange = async (ev) =>{
  336. console.log('Current value:', ev.detail.value);
  337. let v = ev.detail.value
  338. myprofile.value = await likeCourse(v.id)
  339. if( selecteds.value.indexOf(v.name) == -1 ) {
  340. selecteds.value.push(v.name)
  341. }
  342. }
  343. const onNoti = async(ev) => {
  344. console.log(noti);
  345. const data = await updateNoti(noti);
  346. console.log(data);
  347. }
  348. const onAppleLogin = async () => {
  349. SignInWithApple.authorize(apple_options)
  350. .then(async (result: SignInWithAppleResponse) => {
  351. console.debug(result);
  352. // Handle user information
  353. try{
  354. let d = await appleLogin(result.response)
  355. console.log("apple authen ", d)
  356. await setUserToken(d.key)
  357. myprofile.value = await getPref()
  358. //await getProfile()
  359. // Login successful.
  360. refIsLogin.value = true
  361. location.reload();
  362. }catch(error) {
  363. console.log(error);
  364. formError.value = error.response.data.non_field_errors.join("\n");
  365. }
  366. // Validate token with server and create new session
  367. })
  368. .catch(error => {
  369. // Handle error
  370. });
  371. }
  372. const onUpdateProfile = async() => {
  373. console.log("update profile");
  374. console.log(typeof birthdate.value.$el.value)
  375. let obj = {
  376. first_name: first_name.value.$el.value,
  377. last_name: last_name.value.$el.value,
  378. birth_date: birthdate.value.$el.value.substring(0, 10),
  379. loc: loc.value.$el.value,
  380. phone_number: tel.value.$el.value
  381. }
  382. try{
  383. myprofile.value = await updateProfile(obj)
  384. }catch(error) {
  385. alert(error)
  386. }
  387. console.log(obj);
  388. }
  389. const onUploadPhoto = async($event) => {
  390. console.log("upload photo")
  391. console.log($event.target.files)
  392. const fileObj = $event.target.files[0]
  393. try{
  394. myprofile.value = await uploadPhoto(fileObj)
  395. }catch(error) {
  396. alert(error)
  397. }
  398. }
  399. </script>
  400. <style scoped>
  401. ion-datetime {
  402. --background: #151515;
  403. --wheel-fade-background-rgb: 5,5,5;
  404. }
  405. ion-datetime::part(wheel-item) {
  406. }
  407. ion-datetime::part(wheel-item active) {
  408. }
  409. .profilePhoto::part(image) {
  410. width:160px;
  411. height:160px;
  412. object-fit:cover;
  413. border-radius:80px;
  414. margin:auto;
  415. display:block;
  416. border:2px solid #fff;
  417. }
  418. </style>