Account.svelte 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <script lang="ts">
  2. import toast from 'svelte-french-toast';
  3. import { onMount } from 'svelte';
  4. import { user } from '$lib/stores';
  5. import { updateUserProfile } from '$lib/apis/auths';
  6. import UpdatePassword from './Account/UpdatePassword.svelte';
  7. import { getGravatarUrl } from '$lib/apis/utils';
  8. export let saveHandler: Function;
  9. let profileImageUrl = '';
  10. let name = '';
  11. const submitHandler = async () => {
  12. const updatedUser = await updateUserProfile(localStorage.token, name, profileImageUrl).catch(
  13. (error) => {
  14. toast.error(error);
  15. }
  16. );
  17. if (updatedUser) {
  18. await user.set(updatedUser);
  19. return true;
  20. }
  21. return false;
  22. };
  23. onMount(() => {
  24. name = $user.name;
  25. profileImageUrl = $user.profile_image_url;
  26. });
  27. </script>
  28. <div class="flex flex-col h-full justify-between text-sm">
  29. <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
  30. <input
  31. id="profile-image-input"
  32. type="file"
  33. hidden
  34. accept="image/*"
  35. on:change={(e) => {
  36. const files = e?.target?.files ?? [];
  37. let reader = new FileReader();
  38. reader.onload = (event) => {
  39. let originalImageUrl = `${event.target.result}`;
  40. const img = new Image();
  41. img.src = originalImageUrl;
  42. img.onload = function () {
  43. const canvas = document.createElement('canvas');
  44. const ctx = canvas.getContext('2d');
  45. // Calculate the aspect ratio of the image
  46. const aspectRatio = img.width / img.height;
  47. // Calculate the new width and height to fit within 100x100
  48. let newWidth, newHeight;
  49. if (aspectRatio > 1) {
  50. newWidth = 100 * aspectRatio;
  51. newHeight = 100;
  52. } else {
  53. newWidth = 100;
  54. newHeight = 100 / aspectRatio;
  55. }
  56. // Set the canvas size
  57. canvas.width = 100;
  58. canvas.height = 100;
  59. // Calculate the position to center the image
  60. const offsetX = (100 - newWidth) / 2;
  61. const offsetY = (100 - newHeight) / 2;
  62. // Draw the image on the canvas
  63. ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
  64. // Get the base64 representation of the compressed image
  65. const compressedSrc = canvas.toDataURL('image/jpeg');
  66. // Display the compressed image
  67. profileImageUrl = compressedSrc;
  68. e.target.files = null;
  69. };
  70. };
  71. if (
  72. files.length > 0 &&
  73. ['image/gif', 'image/jpeg', 'image/png'].includes(files[0]['type'])
  74. ) {
  75. reader.readAsDataURL(files[0]);
  76. }
  77. }}
  78. />
  79. <div class=" mb-2.5 text-sm font-medium">Profile</div>
  80. <div class="flex space-x-5">
  81. <div class="flex flex-col">
  82. <div class="self-center">
  83. <button
  84. class="relative rounded-full dark:bg-gray-700"
  85. type="button"
  86. on:click={() => {
  87. document.getElementById('profile-image-input')?.click();
  88. }}
  89. >
  90. <img
  91. src={profileImageUrl !== '' ? profileImageUrl : '/user.png'}
  92. alt="profile"
  93. class=" rounded-full w-16 h-16 object-cover"
  94. />
  95. <div
  96. class="absolute flex justify-center rounded-full bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-gray-700 bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-50"
  97. >
  98. <div class="my-auto text-gray-100">
  99. <svg
  100. xmlns="http://www.w3.org/2000/svg"
  101. viewBox="0 0 20 20"
  102. fill="currentColor"
  103. class="w-5 h-5"
  104. >
  105. <path
  106. d="m2.695 14.762-1.262 3.155a.5.5 0 0 0 .65.65l3.155-1.262a4 4 0 0 0 1.343-.886L17.5 5.501a2.121 2.121 0 0 0-3-3L3.58 13.419a4 4 0 0 0-.885 1.343Z"
  107. />
  108. </svg>
  109. </div>
  110. </div>
  111. </button>
  112. </div>
  113. <button
  114. class=" text-xs text-gray-600"
  115. on:click={async () => {
  116. const url = await getGravatarUrl($user.email);
  117. profileImageUrl = url;
  118. }}>Use Gravatar</button
  119. >
  120. </div>
  121. <div class="flex-1">
  122. <div class="flex flex-col w-full">
  123. <div class=" mb-1 text-xs text-gray-500">Name</div>
  124. <div class="flex-1">
  125. <input
  126. class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
  127. type="text"
  128. bind:value={name}
  129. required
  130. />
  131. </div>
  132. </div>
  133. </div>
  134. </div>
  135. <hr class=" dark:border-gray-700 my-4" />
  136. <UpdatePassword />
  137. </div>
  138. <div class="flex justify-end pt-3 text-sm font-medium">
  139. <button
  140. class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
  141. on:click={async () => {
  142. const res = await submitHandler();
  143. if (res) {
  144. saveHandler();
  145. }
  146. }}
  147. >
  148. Save
  149. </button>
  150. </div>
  151. </div>