index.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <script setup lang="ts">
  2. import { useRoute } from "vue-router";
  3. import { onMounted, defineAsyncComponent } from "vue";
  4. import Toast from "toasters";
  5. import { useSettingsStore } from "@/stores/settings";
  6. import { useWebsocketsStore } from "@/stores/websockets";
  7. import { useTabQueryHandler } from "@/composables/useTabQueryHandler";
  8. const MainHeader = defineAsyncComponent(
  9. () => import("@/components/MainHeader.vue")
  10. );
  11. const MainFooter = defineAsyncComponent(
  12. () => import("@/components/MainFooter.vue")
  13. );
  14. const SecuritySettings = defineAsyncComponent(
  15. () => import("./Tabs/Security.vue")
  16. );
  17. const AccountSettings = defineAsyncComponent(
  18. () => import("./Tabs/Account.vue")
  19. );
  20. const ProfileSettings = defineAsyncComponent(
  21. () => import("./Tabs/Profile.vue")
  22. );
  23. const PreferencesSettings = defineAsyncComponent(
  24. () => import("./Tabs/Preferences.vue")
  25. );
  26. const settingsStore = useSettingsStore();
  27. const route = useRoute();
  28. const { tab, showTab } = useTabQueryHandler("");
  29. const { socket } = useWebsocketsStore();
  30. const { setUser, updateOriginalUser } = settingsStore;
  31. onMounted(() => {
  32. if (
  33. route.query.tab === "profile" ||
  34. route.query.tab === "security" ||
  35. route.query.tab === "account" ||
  36. route.query.tab === "preferences"
  37. )
  38. tab.value = route.query.tab;
  39. else tab.value = "profile";
  40. // this.localNightmode = this.nightmode;
  41. socket.onConnect(() => {
  42. socket.dispatch("users.findBySession", res => {
  43. if (res.status === "success") setUser(res.data.user);
  44. else new Toast("You're not currently signed in.");
  45. });
  46. });
  47. socket.on("event:user.password.linked", () =>
  48. updateOriginalUser({
  49. property: "password",
  50. value: true
  51. })
  52. );
  53. socket.on("event:user.password.unlinked", () =>
  54. updateOriginalUser({
  55. property: "password",
  56. value: false
  57. })
  58. );
  59. socket.on("event:user.github.linked", () =>
  60. updateOriginalUser({
  61. property: "github",
  62. value: true
  63. })
  64. );
  65. socket.on("event:user.github.unlinked", () =>
  66. updateOriginalUser({
  67. property: "github",
  68. value: false
  69. })
  70. );
  71. });
  72. </script>
  73. <template>
  74. <div>
  75. <page-metadata title="Settings" />
  76. <main-header />
  77. <div class="container">
  78. <h1 id="page-title">Settings</h1>
  79. <div id="sidebar-with-content">
  80. <div class="nav-links">
  81. <a
  82. :class="{ active: tab === 'profile' }"
  83. @click="showTab('profile')"
  84. >
  85. Profile
  86. </a>
  87. <a
  88. :class="{ active: tab === 'account' }"
  89. @click="showTab('account')"
  90. >
  91. Account
  92. </a>
  93. <a
  94. :class="{ active: tab === 'security' }"
  95. @click="showTab('security')"
  96. >
  97. Security
  98. </a>
  99. <a
  100. :class="{ active: tab === 'preferences' }"
  101. @click="showTab('preferences')"
  102. >
  103. Preferences
  104. </a>
  105. </div>
  106. <profile-settings v-if="tab === 'profile'"></profile-settings>
  107. <account-settings v-if="tab === 'account'"></account-settings>
  108. <security-settings
  109. v-if="tab === 'security'"
  110. ></security-settings>
  111. <preferences-settings
  112. v-if="tab === 'preferences'"
  113. ></preferences-settings>
  114. </div>
  115. </div>
  116. <main-footer />
  117. </div>
  118. </template>
  119. <style lang="less" scoped>
  120. .night-mode {
  121. .container .content {
  122. box-shadow: 0 !important;
  123. }
  124. }
  125. :deep(.character-counter) {
  126. display: flex;
  127. justify-content: flex-end;
  128. height: 0;
  129. }
  130. .container {
  131. margin-top: 32px;
  132. padding: 24px;
  133. :deep(.row) {
  134. *:not(:last-child) {
  135. margin-right: 5px;
  136. }
  137. }
  138. .content {
  139. background-color: var(--white);
  140. padding: 30px 50px;
  141. border-radius: @border-radius;
  142. box-shadow: @box-shadow;
  143. }
  144. #sidebar-with-content {
  145. display: flex;
  146. flex-direction: column;
  147. }
  148. @media only screen and (min-width: 700px) {
  149. #sidebar-with-content {
  150. width: 962px;
  151. max-width: 100%;
  152. margin: 0 auto;
  153. flex-direction: row;
  154. .nav-links {
  155. margin-left: 0;
  156. margin-right: 64px;
  157. }
  158. .content {
  159. width: 600px;
  160. margin-top: 0px !important;
  161. }
  162. }
  163. }
  164. .nav-links {
  165. width: 250px;
  166. margin-left: auto;
  167. margin-right: auto;
  168. a {
  169. outline: none;
  170. border: none;
  171. box-shadow: 0;
  172. color: var(--primary-color);
  173. font-size: 22px;
  174. line-height: 26px;
  175. padding: 7px 0 7px 12px;
  176. width: 100%;
  177. text-align: left;
  178. cursor: pointer;
  179. border-radius: @border-radius;
  180. background-color: transparent;
  181. display: inline-block;
  182. &.active {
  183. color: var(--white);
  184. background-color: var(--primary-color);
  185. }
  186. }
  187. }
  188. :deep(.content) {
  189. margin: 24px 0;
  190. height: fit-content;
  191. .control:not(:first-of-type) {
  192. margin: 10px 0;
  193. }
  194. label {
  195. font-size: 14px;
  196. color: var(--dark-grey-2);
  197. }
  198. textarea {
  199. height: 96px;
  200. }
  201. button {
  202. width: 100%;
  203. margin-top: 30px;
  204. }
  205. }
  206. }
  207. </style>