Account.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <template>
  2. <div class="content account-tab">
  3. <h4 class="section-title">Change account details</h4>
  4. <p class="section-description">
  5. Keep these details up-to-date.
  6. </p>
  7. <hr class="section-horizontal-rule" />
  8. <p class="control is-expanded margin-top-zero">
  9. <label for="username">Username</label>
  10. <input
  11. class="input"
  12. id="username"
  13. type="text"
  14. placeholder="Enter username here..."
  15. v-model="modifiedUser.username"
  16. maxlength="32"
  17. autocomplete="off"
  18. @blur="onInputBlur('username')"
  19. />
  20. <span v-if="modifiedUser.username" class="character-counter"
  21. >{{ modifiedUser.username.length }}/32</span
  22. >
  23. </p>
  24. <transition name="fadein-helpbox">
  25. <input-help-box
  26. :valid="validation.username.valid"
  27. :message="validation.username.message"
  28. ></input-help-box>
  29. </transition>
  30. <p class="control is-expanded">
  31. <label for="email">Email</label>
  32. <input
  33. class="input"
  34. id="email"
  35. type="text"
  36. placeholder="Enter email address here..."
  37. v-if="modifiedUser.email"
  38. v-model="modifiedUser.email.address"
  39. @blur="onInputBlur('email')"
  40. autocomplete="off"
  41. />
  42. </p>
  43. <transition name="fadein-helpbox">
  44. <input-help-box
  45. :valid="validation.email.valid"
  46. :message="validation.email.message"
  47. ></input-help-box>
  48. </transition>
  49. <save-button ref="saveButton" @clicked="saveChanges()" />
  50. <!-- <div class="section-margin-bottom" />
  51. <h4 class="section-title">Export my data</h4>
  52. <p class="section-description">
  53. Download a copy of all data we store on you in JSON format.
  54. </p>
  55. <hr class="section-horizontal-rule" /> -->
  56. <div class="section-margin-bottom" />
  57. <h4 class="section-title">Remove any data we hold on you</h4>
  58. <p class="section-description">
  59. Permanently remove your account and/or data we store on you.
  60. </p>
  61. <hr class="section-horizontal-rule" />
  62. <div>
  63. <a
  64. class="button is-warning"
  65. href="#"
  66. @click.prevent="removeActivities()"
  67. >
  68. <i class="material-icons icon-with-button">clear</i>
  69. Clear my activities
  70. </a>
  71. <a
  72. class="button is-danger"
  73. href="#"
  74. @click.prevent="removeAccount()"
  75. >
  76. <i class="material-icons icon-with-button">delete</i>
  77. Remove my account
  78. </a>
  79. </div>
  80. </div>
  81. </template>
  82. <script>
  83. import { mapState, mapActions, mapGetters } from "vuex";
  84. import Toast from "toasters";
  85. import InputHelpBox from "@/components/InputHelpBox.vue";
  86. import SaveButton from "@/components/SaveButton.vue";
  87. import validation from "@/validation";
  88. export default {
  89. components: { InputHelpBox, SaveButton },
  90. data() {
  91. return {
  92. validation: {
  93. username: {
  94. entered: false,
  95. valid: false,
  96. message: "Please enter a valid username."
  97. },
  98. email: {
  99. entered: false,
  100. valid: false,
  101. message: "Please enter a valid email address."
  102. }
  103. }
  104. };
  105. },
  106. computed: {
  107. ...mapState({
  108. userId: state => state.user.auth.userId,
  109. originalUser: state => state.settings.originalUser,
  110. modifiedUser: state => state.settings.modifiedUser
  111. }),
  112. ...mapGetters({
  113. socket: "websockets/getSocket"
  114. })
  115. },
  116. watch: {
  117. // prettier-ignore
  118. // eslint-disable-next-line func-names
  119. "modifiedUser.username": function (value) {
  120. if (!validation.isLength(value, 2, 32)) {
  121. this.validation.username.message =
  122. "Username must have between 2 and 32 characters.";
  123. this.validation.username.valid = false;
  124. } else if (
  125. !validation.regex.azAZ09_.test(value) &&
  126. value !== this.originalUser.username // Sometimes a username pulled from GitHub won't succeed validation
  127. ) {
  128. this.validation.username.message =
  129. "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.";
  130. this.validation.username.valid = false;
  131. } else {
  132. this.validation.username.message = "Everything looks great!";
  133. this.validation.username.valid = true;
  134. }
  135. },
  136. // prettier-ignore
  137. // eslint-disable-next-line func-names
  138. "modifiedUser.email.address": function (value) {
  139. if (!validation.isLength(value, 3, 254)) {
  140. this.validation.email.message =
  141. "Email must have between 3 and 254 characters.";
  142. this.validation.email.valid = false;
  143. } else if (
  144. value.indexOf("@") !== value.lastIndexOf("@") ||
  145. !validation.regex.emailSimple.test(value)
  146. ) {
  147. this.validation.email.message = "Invalid Email format.";
  148. this.validation.email.valid = false;
  149. } else {
  150. this.validation.email.message = "Everything looks great!";
  151. this.validation.email.valid = true;
  152. }
  153. }
  154. },
  155. methods: {
  156. onInputBlur(inputName) {
  157. this.validation[inputName].entered = true;
  158. },
  159. saveChanges() {
  160. const usernameChanged =
  161. this.modifiedUser.username !== this.originalUser.username;
  162. const emailAddressChanged =
  163. this.modifiedUser.email.address !==
  164. this.originalUser.email.address;
  165. if (usernameChanged) this.changeUsername();
  166. if (emailAddressChanged) this.changeEmail();
  167. if (!usernameChanged && !emailAddressChanged) {
  168. this.$refs.saveButton.handleFailedSave();
  169. new Toast("Please make a change before saving.");
  170. }
  171. },
  172. changeEmail() {
  173. const email = this.modifiedUser.email.address;
  174. if (!validation.isLength(email, 3, 254))
  175. return new Toast(
  176. "Email must have between 3 and 254 characters."
  177. );
  178. if (
  179. email.indexOf("@") !== email.lastIndexOf("@") ||
  180. !validation.regex.emailSimple.test(email)
  181. )
  182. return new Toast("Invalid email format.");
  183. this.$refs.saveButton.saveStatus = "disabled";
  184. return this.socket.dispatch(
  185. "users.updateEmail",
  186. this.userId,
  187. email,
  188. res => {
  189. if (res.status !== "success") {
  190. new Toast(res.message);
  191. this.$refs.saveButton.handleFailedSave();
  192. } else {
  193. new Toast("Successfully changed email address");
  194. this.updateOriginalUser({
  195. property: "email.address",
  196. value: email
  197. });
  198. this.$refs.saveButton.handleSuccessfulSave();
  199. }
  200. }
  201. );
  202. },
  203. changeUsername() {
  204. const { username } = this.modifiedUser;
  205. if (!validation.isLength(username, 2, 32))
  206. return new Toast(
  207. "Username must have between 2 and 32 characters."
  208. );
  209. if (!validation.regex.azAZ09_.test(username))
  210. return new Toast(
  211. "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _."
  212. );
  213. this.$refs.saveButton.saveStatus = "disabled";
  214. return this.socket.dispatch(
  215. "users.updateUsername",
  216. this.userId,
  217. username,
  218. res => {
  219. if (res.status !== "success") {
  220. new Toast(res.message);
  221. this.$refs.saveButton.handleFailedSave();
  222. } else {
  223. new Toast("Successfully changed username");
  224. this.updateOriginalUser({
  225. property: "username",
  226. value: username
  227. });
  228. this.$refs.saveButton.handleSuccessfulSave();
  229. }
  230. }
  231. );
  232. },
  233. removeAccount() {
  234. return this.socket.dispatch("users.remove", res => {
  235. if (res.status === "success") {
  236. return this.socket.dispatch("users.logout", () => {
  237. return lofig.get("cookie").then(cookie => {
  238. document.cookie = `${cookie.SIDname}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
  239. return window.location.reload();
  240. });
  241. });
  242. }
  243. return new Toast(res.message);
  244. });
  245. },
  246. removeActivities() {
  247. this.socket.dispatch("activities.removeAllForUser", res => {
  248. new Toast(res.message);
  249. });
  250. },
  251. ...mapActions("settings", ["updateOriginalUser"])
  252. }
  253. };
  254. </script>