Account.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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({
  170. content: "Please make a change before saving.",
  171. timeout: 8000
  172. });
  173. }
  174. },
  175. changeEmail() {
  176. const email = this.modifiedUser.email.address;
  177. if (!validation.isLength(email, 3, 254))
  178. return new Toast({
  179. content: "Email must have between 3 and 254 characters.",
  180. timeout: 8000
  181. });
  182. if (
  183. email.indexOf("@") !== email.lastIndexOf("@") ||
  184. !validation.regex.emailSimple.test(email)
  185. )
  186. return new Toast({
  187. content: "Invalid email format.",
  188. timeout: 8000
  189. });
  190. this.$refs.saveButton.saveStatus = "disabled";
  191. return this.socket.dispatch(
  192. "users.updateEmail",
  193. this.userId,
  194. email,
  195. res => {
  196. if (res.status !== "success") {
  197. new Toast({ content: res.message, timeout: 8000 });
  198. this.$refs.saveButton.handleFailedSave();
  199. } else {
  200. new Toast({
  201. content: "Successfully changed email address",
  202. timeout: 4000
  203. });
  204. this.updateOriginalUser({
  205. property: "email.address",
  206. value: email
  207. });
  208. this.$refs.saveButton.handleSuccessfulSave();
  209. }
  210. }
  211. );
  212. },
  213. changeUsername() {
  214. const { username } = this.modifiedUser;
  215. if (!validation.isLength(username, 2, 32))
  216. return new Toast({
  217. content: "Username must have between 2 and 32 characters.",
  218. timeout: 8000
  219. });
  220. if (!validation.regex.azAZ09_.test(username))
  221. return new Toast({
  222. content:
  223. "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.",
  224. timeout: 8000
  225. });
  226. this.$refs.saveButton.saveStatus = "disabled";
  227. return this.socket.dispatch(
  228. "users.updateUsername",
  229. this.userId,
  230. username,
  231. res => {
  232. if (res.status !== "success") {
  233. new Toast({ content: res.message, timeout: 8000 });
  234. this.$refs.saveButton.handleFailedSave();
  235. } else {
  236. new Toast({
  237. content: "Successfully changed username",
  238. timeout: 4000
  239. });
  240. this.updateOriginalUser({
  241. property: "username",
  242. value: username
  243. });
  244. this.$refs.saveButton.handleSuccessfulSave();
  245. }
  246. }
  247. );
  248. },
  249. removeAccount() {
  250. return this.socket.dispatch("users.remove", res => {
  251. if (res.status === "success") {
  252. return this.socket.dispatch("users.logout", () => {
  253. return lofig.get("cookie").then(cookie => {
  254. document.cookie = `${cookie.SIDname}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
  255. return window.location.reload();
  256. });
  257. });
  258. }
  259. return new Toast({ content: res.message, timeout: 8000 });
  260. });
  261. },
  262. removeActivities() {
  263. this.socket.dispatch("activities.removeAllForUser", res => {
  264. new Toast({ content: res.message, timeout: 4000 });
  265. });
  266. },
  267. ...mapActions("settings", ["updateOriginalUser"])
  268. }
  269. };
  270. </script>