Profile.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <template>
  2. <div class="content profile-tab">
  3. <h4 class="section-title">Change Profile</h4>
  4. <p class="section-description">
  5. Edit your public profile so users can find out more about you.
  6. </p>
  7. <hr class="section-horizontal-rule" />
  8. <div
  9. class="control is-expanded avatar-selection-outer-container"
  10. v-if="modifiedUser.avatar"
  11. >
  12. <label>Avatar</label>
  13. <div id="avatar-selection-inner-container">
  14. <profile-picture
  15. :avatar="modifiedUser.avatar"
  16. :name="modifiedUser.name"
  17. />
  18. <div class="select">
  19. <select v-model="modifiedUser.avatar.type">
  20. <option value="gravatar">Using Gravatar</option>
  21. <option value="initials">Based on initials</option>
  22. </select>
  23. </div>
  24. </div>
  25. </div>
  26. <p class="control is-expanded margin-top-zero">
  27. <label for="name">Name</label>
  28. <input
  29. class="input"
  30. id="name"
  31. type="text"
  32. placeholder="Enter name here..."
  33. maxlength="64"
  34. v-model="modifiedUser.name"
  35. />
  36. <span v-if="modifiedUser.name" class="character-counter"
  37. >{{ modifiedUser.name.length }}/64</span
  38. >
  39. </p>
  40. <p class="control is-expanded">
  41. <label for="location">Location</label>
  42. <input
  43. class="input"
  44. id="location"
  45. type="text"
  46. placeholder="Enter location here..."
  47. maxlength="50"
  48. v-model="modifiedUser.location"
  49. />
  50. <span v-if="modifiedUser.location" class="character-counter"
  51. >{{ modifiedUser.location.length }}/50</span
  52. >
  53. </p>
  54. <p class="control is-expanded">
  55. <label for="bio">Bio</label>
  56. <textarea
  57. class="textarea"
  58. id="bio"
  59. placeholder="Enter bio here..."
  60. maxlength="200"
  61. autocomplete="off"
  62. v-model="modifiedUser.bio"
  63. />
  64. <span v-if="modifiedUser.bio" class="character-counter"
  65. >{{ modifiedUser.bio.length }}/200</span
  66. >
  67. </p>
  68. <save-button ref="saveButton" @clicked="saveChanges()" />
  69. </div>
  70. </template>
  71. <script>
  72. import { mapState, mapActions, mapGetters } from "vuex";
  73. import Toast from "toasters";
  74. import validation from "../../../validation";
  75. import ProfilePicture from "../../../components/ui/ProfilePicture.vue";
  76. import SaveButton from "../../../components/ui/SaveButton.vue";
  77. export default {
  78. components: { ProfilePicture, SaveButton },
  79. computed: {
  80. ...mapState({
  81. userId: state => state.user.auth.userId,
  82. originalUser: state => state.settings.originalUser,
  83. modifiedUser: state => state.settings.modifiedUser
  84. }),
  85. ...mapGetters({
  86. socket: "websockets/getSocket"
  87. })
  88. },
  89. watch: {
  90. "modifiedUser.avatar.type": function watchAvatarType(newType, oldType) {
  91. if (
  92. oldType &&
  93. this.modifiedUser.avatar.type !==
  94. this.originalUser.avatar.type &&
  95. newType === "initials"
  96. ) {
  97. const colors = ["blue", "orange", "green", "purple", "teal"];
  98. const color = colors[Math.floor(Math.random() * colors.length)];
  99. this.modifiedUser.avatar.color = color;
  100. }
  101. }
  102. },
  103. methods: {
  104. saveChanges() {
  105. const nameChanged =
  106. this.modifiedUser.name !== this.originalUser.name;
  107. const locationChanged =
  108. this.modifiedUser.location !== this.originalUser.location;
  109. const bioChanged = this.modifiedUser.bio !== this.originalUser.bio;
  110. const avatarChanged =
  111. this.modifiedUser.avatar.type !== this.originalUser.avatar.type;
  112. if (nameChanged) this.changeName();
  113. if (locationChanged) this.changeLocation();
  114. if (bioChanged) this.changeBio();
  115. if (avatarChanged) this.changeAvatarType();
  116. if (
  117. !avatarChanged &&
  118. !bioChanged &&
  119. !locationChanged &&
  120. !nameChanged
  121. ) {
  122. this.$refs.saveButton.handleFailedSave();
  123. new Toast({
  124. content: "Please make a change before saving.",
  125. timeout: 8000
  126. });
  127. }
  128. },
  129. changeName() {
  130. const { name } = this.modifiedUser;
  131. if (!validation.isLength(name, 1, 64))
  132. return new Toast({
  133. content: "Name must have between 1 and 64 characters.",
  134. timeout: 8000
  135. });
  136. this.$refs.saveButton.status = "disabled";
  137. return this.socket.dispatch(
  138. "users.updateName",
  139. this.userId,
  140. name,
  141. res => {
  142. if (res.status !== "success") {
  143. new Toast({ content: res.message, timeout: 8000 });
  144. this.$refs.saveButton.handleFailedSave();
  145. } else {
  146. new Toast({
  147. content: "Successfully changed name",
  148. timeout: 4000
  149. });
  150. this.updateOriginalUser({
  151. property: "name",
  152. value: name
  153. });
  154. this.$refs.saveButton.handleSuccessfulSave();
  155. }
  156. }
  157. );
  158. },
  159. changeLocation() {
  160. const { location } = this.modifiedUser;
  161. if (!validation.isLength(location, 0, 50))
  162. return new Toast({
  163. content: "Location must have between 0 and 50 characters.",
  164. timeout: 8000
  165. });
  166. this.$refs.saveButton.status = "disabled";
  167. return this.socket.dispatch(
  168. "users.updateLocation",
  169. this.userId,
  170. location,
  171. res => {
  172. if (res.status !== "success") {
  173. new Toast({ content: res.message, timeout: 8000 });
  174. this.$refs.saveButton.handleFailedSave();
  175. } else {
  176. new Toast({
  177. content: "Successfully changed location",
  178. timeout: 4000
  179. });
  180. this.updateOriginalUser({
  181. property: "location",
  182. value: location
  183. });
  184. this.$refs.saveButton.handleSuccessfulSave();
  185. }
  186. }
  187. );
  188. },
  189. changeBio() {
  190. const { bio } = this.modifiedUser;
  191. if (!validation.isLength(bio, 0, 200))
  192. return new Toast({
  193. content: "Bio must have between 0 and 200 characters.",
  194. timeout: 8000
  195. });
  196. this.$refs.saveButton.status = "disabled";
  197. return this.socket.dispatch(
  198. "users.updateBio",
  199. this.userId,
  200. bio,
  201. res => {
  202. if (res.status !== "success") {
  203. new Toast({ content: res.message, timeout: 8000 });
  204. this.$refs.saveButton.handleFailedSave();
  205. } else {
  206. new Toast({
  207. content: "Successfully changed bio",
  208. timeout: 4000
  209. });
  210. this.updateOriginalUser({
  211. property: "bio",
  212. value: bio
  213. });
  214. this.$refs.saveButton.handleSuccessfulSave();
  215. }
  216. }
  217. );
  218. },
  219. changeAvatarType() {
  220. const { avatar } = this.modifiedUser;
  221. this.$refs.saveButton.status = "disabled";
  222. return this.socket.dispatch(
  223. "users.updateAvatarType",
  224. this.userId,
  225. avatar,
  226. res => {
  227. if (res.status !== "success") {
  228. new Toast({ content: res.message, timeout: 8000 });
  229. this.$refs.saveButton.handleFailedSave();
  230. } else {
  231. new Toast({
  232. content: "Successfully updated avatar type",
  233. timeout: 4000
  234. });
  235. this.updateOriginalUser({
  236. property: "avatar",
  237. value: avatar
  238. });
  239. this.$refs.saveButton.handleSuccessfulSave();
  240. }
  241. }
  242. );
  243. },
  244. ...mapActions("settings", ["updateOriginalUser"])
  245. }
  246. };
  247. </script>
  248. <style lang="scss" scoped>
  249. .content .control {
  250. margin-bottom: 15px;
  251. }
  252. .character-counter {
  253. height: initial;
  254. }
  255. .avatar-selection-outer-container {
  256. display: flex;
  257. flex-direction: column;
  258. align-items: flex-start;
  259. .select:after {
  260. border-color: var(--primary-color);
  261. }
  262. #avatar-selection-inner-container {
  263. display: flex;
  264. align-items: center;
  265. margin-top: 5px;
  266. .profile-picture {
  267. margin-right: 10px;
  268. width: 50px;
  269. height: 50px;
  270. font-size: 25px;
  271. }
  272. }
  273. }
  274. </style>