main.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import Vue from "vue";
  2. import VueTippy, { TippyComponent } from "vue-tippy";
  3. import VueRouter from "vue-router";
  4. import ws from "@/ws";
  5. import store from "./store";
  6. import App from "./App.vue";
  7. const REQUIRED_CONFIG_VERSION = 2;
  8. const handleMetadata = attrs => {
  9. document.title = `Musare | ${attrs.title}`;
  10. };
  11. Vue.use(VueTippy, {
  12. directive: "tippy", // => v-tippy
  13. flipDuration: 0,
  14. popperOptions: {
  15. modifiers: {
  16. preventOverflow: {
  17. enabled: true
  18. }
  19. }
  20. },
  21. allowHTML: true,
  22. animation: "scale",
  23. theme: "dark",
  24. arrow: true
  25. });
  26. Vue.component("Tippy", TippyComponent);
  27. Vue.component("Metadata", {
  28. watch: {
  29. $attrs: {
  30. // eslint-disable-next-line vue/no-arrow-functions-in-watch
  31. handler: attrs => {
  32. handleMetadata(attrs);
  33. },
  34. deep: true,
  35. immediate: true
  36. }
  37. },
  38. render() {
  39. return null;
  40. }
  41. });
  42. Vue.use(VueRouter);
  43. Vue.directive("scroll", {
  44. inserted(el, binding) {
  45. const f = evt => {
  46. clearTimeout(window.scrollDebounceId);
  47. window.scrollDebounceId = setTimeout(() => {
  48. if (binding.value(evt, el)) {
  49. window.removeEventListener("scroll", f);
  50. }
  51. }, 200);
  52. };
  53. window.addEventListener("scroll", f);
  54. }
  55. });
  56. Vue.directive("focus", {
  57. inserted(el) {
  58. window.focusedElementBefore = document.activeElement;
  59. el.focus();
  60. }
  61. });
  62. const router = new VueRouter({
  63. mode: "history",
  64. routes: [
  65. {
  66. path: "/",
  67. component: () => import("@/pages/Home.vue")
  68. },
  69. {
  70. path: "/404",
  71. alias: ["*"],
  72. component: () => import("@/pages/404.vue")
  73. },
  74. {
  75. path: "/terms",
  76. component: () => import("@/pages/Terms.vue")
  77. },
  78. {
  79. path: "/privacy",
  80. component: () => import("@/pages/Privacy.vue")
  81. },
  82. {
  83. path: "/team",
  84. component: () => import("@/pages/Team.vue")
  85. },
  86. {
  87. path: "/news",
  88. component: () => import("@/pages/News.vue")
  89. },
  90. {
  91. path: "/about",
  92. component: () => import("@/pages/About.vue")
  93. },
  94. {
  95. name: "profile",
  96. path: "/u/:username",
  97. component: () => import("@/pages/Profile/index.vue")
  98. },
  99. {
  100. path: "/settings",
  101. component: () => import("@/pages/Settings/index.vue"),
  102. meta: {
  103. loginRequired: true
  104. }
  105. },
  106. {
  107. path: "/reset_password",
  108. component: () => import("@/pages/ResetPassword.vue")
  109. },
  110. {
  111. path: "/set_password",
  112. props: { mode: "set" },
  113. component: () => import("@/pages/ResetPassword.vue"),
  114. meta: {
  115. loginRequired: true
  116. }
  117. },
  118. {
  119. path: "/login",
  120. component: () => import("@/components/modals/Login.vue"),
  121. meta: {
  122. guestsOnly: true
  123. }
  124. },
  125. {
  126. path: "/register",
  127. component: () => import("@/components/modals/Register.vue"),
  128. meta: {
  129. guestsOnly: true
  130. }
  131. },
  132. {
  133. path: "/admin",
  134. component: () => import("@/pages/Admin/index.vue"),
  135. meta: {
  136. adminRequired: true
  137. }
  138. },
  139. {
  140. path: "/admin/:page",
  141. component: () => import("@/pages//Admin/index.vue"),
  142. meta: {
  143. adminRequired: true
  144. }
  145. },
  146. {
  147. name: "station",
  148. path: "/:id",
  149. component: () => import("@/pages//Station/index.vue")
  150. }
  151. ]
  152. });
  153. lofig.folder = "../config/default.json";
  154. (async () => {
  155. lofig.fetchConfig().then(config => {
  156. const { configVersion, skipConfigVersionCheck } = config;
  157. if (
  158. configVersion !== REQUIRED_CONFIG_VERSION &&
  159. !skipConfigVersionCheck
  160. ) {
  161. // eslint-disable-next-line no-alert
  162. alert(
  163. "CONFIG VERSION IS WRONG. PLEASE UPDATE YOUR CONFIG WITH THE HELP OF THE TEMPLATE FILE AND THE README FILE."
  164. );
  165. window.stop();
  166. }
  167. });
  168. const websocketsDomain = await lofig.get("websocketsDomain");
  169. ws.init(websocketsDomain);
  170. ws.socket.on("ready", (loggedIn, role, username, userId) =>
  171. store.dispatch("user/auth/authData", {
  172. loggedIn,
  173. role,
  174. username,
  175. userId
  176. })
  177. );
  178. ws.socket.on("keep.event:banned", ban =>
  179. store.dispatch("user/auth/banUser", ban)
  180. );
  181. ws.socket.on("event:user.username.changed", username =>
  182. store.dispatch("user/auth/updateUsername", username)
  183. );
  184. ws.socket.on("keep.event:user.preferences.changed", preferences => {
  185. store.dispatch(
  186. "user/preferences/changeAutoSkipDisliked",
  187. preferences.autoSkipDisliked
  188. );
  189. store.dispatch(
  190. "user/preferences/changeNightmode",
  191. preferences.nightmode
  192. );
  193. store.dispatch(
  194. "user/preferences/changeActivityLogPublic",
  195. preferences.activityLogPublic
  196. );
  197. store.dispatch(
  198. "user/preferences/changeActivityWatch",
  199. preferences.activityWatch
  200. );
  201. });
  202. router.beforeEach((to, from, next) => {
  203. if (window.stationInterval) {
  204. clearInterval(window.stationInterval);
  205. window.stationInterval = 0;
  206. }
  207. if (window.socket) ws.removeAllListeners();
  208. ws.clear();
  209. if (
  210. to.meta.loginRequired ||
  211. to.meta.adminRequired ||
  212. to.meta.guestsOnly
  213. ) {
  214. const gotData = () => {
  215. if (to.meta.loginRequired && !store.state.user.auth.loggedIn)
  216. next({ path: "/login" });
  217. else if (
  218. to.meta.adminRequired &&
  219. store.state.user.auth.role !== "admin"
  220. )
  221. next({ path: "/" });
  222. else if (to.meta.guestsOnly && store.state.user.auth.loggedIn)
  223. next({ path: "/" });
  224. else next();
  225. };
  226. if (store.state.user.auth.gotData) gotData();
  227. else {
  228. const watcher = store.watch(
  229. state => state.user.auth.gotData,
  230. () => {
  231. watcher();
  232. gotData();
  233. }
  234. );
  235. }
  236. } else next();
  237. });
  238. Vue.directive("click-outside", {
  239. bind(element, binding) {
  240. window.handleOutsideClick = event => {
  241. if (
  242. !(
  243. element === event.target ||
  244. element.contains(event.target)
  245. )
  246. ) {
  247. binding.value();
  248. }
  249. };
  250. document.body.addEventListener("click", window.handleOutsideClick);
  251. },
  252. unbind() {
  253. document.body.removeEventListener(
  254. "click",
  255. window.handleOutsideClick
  256. );
  257. }
  258. });
  259. // eslint-disable-next-line no-new
  260. new Vue({
  261. router,
  262. store,
  263. el: "#root",
  264. render: wrapper => wrapper(App)
  265. });
  266. })();