Profile.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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. <transition name="saving-changes-transition" mode="out-in">
  69. <button
  70. class="button save-changes"
  71. :class="saveButtonStyle"
  72. @click="saveChanges()"
  73. :key="saveStatus"
  74. :disabled="saveStatus === 'disabled'"
  75. v-html="saveButtonMessage"
  76. />
  77. </transition>
  78. </div>
  79. </template>
  80. <script>
  81. import { mapState, mapActions } from "vuex";
  82. import Toast from "toasters";
  83. import validation from "../../../validation";
  84. import io from "../../../io";
  85. import ProfilePicture from "../../../components/ui/ProfilePicture.vue";
  86. import SaveButton from "../mixins/SaveButton.vue";
  87. export default {
  88. components: { ProfilePicture },
  89. mixins: [SaveButton],
  90. computed: mapState({
  91. userId: state => state.user.auth.userId,
  92. originalUser: state => state.settings.originalUser,
  93. modifiedUser: state => state.settings.modifiedUser
  94. }),
  95. watch: {
  96. "modifiedUser.avatar.type": function watchAvatarType(newType, oldType) {
  97. if (
  98. oldType &&
  99. this.modifiedUser.avatar.type !==
  100. this.originalUser.avatar.type &&
  101. newType === "initials"
  102. ) {
  103. const colors = ["blue", "orange", "green", "purple", "teal"];
  104. const color = colors[Math.floor(Math.random() * colors.length)];
  105. this.modifiedUser.avatar.color = color;
  106. }
  107. }
  108. },
  109. mounted() {
  110. io.getSocket(socket => {
  111. this.socket = socket;
  112. });
  113. },
  114. methods: {
  115. saveChanges() {
  116. const nameChanged =
  117. this.modifiedUser.name !== this.originalUser.name;
  118. const locationChanged =
  119. this.modifiedUser.location !== this.originalUser.location;
  120. const bioChanged = this.modifiedUser.bio !== this.originalUser.bio;
  121. const avatarChanged =
  122. this.modifiedUser.avatar.type !== this.originalUser.avatar.type;
  123. if (nameChanged) this.changeName();
  124. if (locationChanged) this.changeLocation();
  125. if (bioChanged) this.changeBio();
  126. if (avatarChanged) this.changeAvatarType();
  127. if (
  128. !avatarChanged &&
  129. !bioChanged &&
  130. !locationChanged &&
  131. !nameChanged
  132. ) {
  133. this.failedSave();
  134. new Toast({
  135. content: "Please make a change before saving.",
  136. timeout: 8000
  137. });
  138. }
  139. },
  140. changeName() {
  141. const { name } = this.modifiedUser;
  142. if (!validation.isLength(name, 1, 64))
  143. return new Toast({
  144. content: "Name must have between 1 and 64 characters.",
  145. timeout: 8000
  146. });
  147. this.saveStatus = "disabled";
  148. return this.socket.emit(
  149. "users.updateName",
  150. this.userId,
  151. name,
  152. res => {
  153. if (res.status !== "success") {
  154. new Toast({ content: res.message, timeout: 8000 });
  155. this.failedSave();
  156. } else {
  157. new Toast({
  158. content: "Successfully changed name",
  159. timeout: 4000
  160. });
  161. this.updateOriginalUser({
  162. property: "name",
  163. value: name
  164. });
  165. this.successfulSave();
  166. }
  167. }
  168. );
  169. },
  170. changeLocation() {
  171. const { location } = this.modifiedUser;
  172. if (!validation.isLength(location, 0, 50))
  173. return new Toast({
  174. content: "Location must have between 0 and 50 characters.",
  175. timeout: 8000
  176. });
  177. this.saveStatus = "disabled";
  178. return this.socket.emit(
  179. "users.updateLocation",
  180. this.userId,
  181. location,
  182. res => {
  183. if (res.status !== "success") {
  184. new Toast({ content: res.message, timeout: 8000 });
  185. this.failedSave();
  186. } else {
  187. new Toast({
  188. content: "Successfully changed location",
  189. timeout: 4000
  190. });
  191. this.updateOriginalUser({
  192. property: "location",
  193. value: location
  194. });
  195. this.successfulSave();
  196. }
  197. }
  198. );
  199. },
  200. changeBio() {
  201. const { bio } = this.modifiedUser;
  202. if (!validation.isLength(bio, 0, 200))
  203. return new Toast({
  204. content: "Bio must have between 0 and 200 characters.",
  205. timeout: 8000
  206. });
  207. this.saveStatus = "disabled";
  208. return this.socket.emit(
  209. "users.updateBio",
  210. this.userId,
  211. bio,
  212. res => {
  213. if (res.status !== "success") {
  214. new Toast({ content: res.message, timeout: 8000 });
  215. this.failedSave();
  216. } else {
  217. new Toast({
  218. content: "Successfully changed bio",
  219. timeout: 4000
  220. });
  221. this.updateOriginalUser({
  222. property: "bio",
  223. value: bio
  224. });
  225. this.successfulSave();
  226. }
  227. }
  228. );
  229. },
  230. changeAvatarType() {
  231. const { avatar } = this.modifiedUser;
  232. this.saveStatus = "disabled";
  233. return this.socket.emit(
  234. "users.updateAvatarType",
  235. this.userId,
  236. avatar,
  237. res => {
  238. if (res.status !== "success") {
  239. new Toast({ content: res.message, timeout: 8000 });
  240. this.failedSave();
  241. } else {
  242. new Toast({
  243. content: "Successfully updated avatar type",
  244. timeout: 4000
  245. });
  246. this.updateOriginalUser({
  247. property: "avatar",
  248. value: avatar
  249. });
  250. this.successfulSave();
  251. }
  252. }
  253. );
  254. },
  255. ...mapActions("settings", ["updateOriginalUser"])
  256. }
  257. };
  258. </script>
  259. <style lang="scss" scoped>
  260. @import "../../../styles/global.scss";
  261. .content .control {
  262. margin-bottom: 15px;
  263. }
  264. .character-counter {
  265. height: initial;
  266. }
  267. .avatar-selection-outer-container {
  268. display: flex;
  269. flex-direction: column;
  270. align-items: flex-start;
  271. .select:after {
  272. border-color: $musare-blue;
  273. }
  274. #avatar-selection-inner-container {
  275. display: flex;
  276. align-items: center;
  277. margin-top: 5px;
  278. .profile-picture {
  279. margin-right: 10px;
  280. width: 50px;
  281. height: 50px;
  282. font-size: 25px;
  283. }
  284. }
  285. }
  286. </style>