main.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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", res => {
  171. const { loggedIn, role, username, userId, email } = res.data;
  172. store.dispatch("user/auth/authData", {
  173. loggedIn,
  174. role,
  175. username,
  176. email,
  177. userId
  178. });
  179. });
  180. ws.socket.on("keep.event:banned", res =>
  181. store.dispatch("user/auth/banUser", res.data.ban)
  182. );
  183. ws.socket.on("event:user.username.changed", res =>
  184. store.dispatch("user/auth/updateUsername", res.data.username)
  185. );
  186. ws.socket.on("keep.event:user.preferences.changed", res => {
  187. const { preferences } = res.data;
  188. store.dispatch(
  189. "user/preferences/changeAutoSkipDisliked",
  190. preferences.autoSkipDisliked
  191. );
  192. store.dispatch(
  193. "user/preferences/changeNightmode",
  194. preferences.nightmode
  195. );
  196. store.dispatch(
  197. "user/preferences/changeActivityLogPublic",
  198. preferences.activityLogPublic
  199. );
  200. store.dispatch(
  201. "user/preferences/changeAnonymousSongRequests",
  202. preferences.anonymousSongRequests
  203. );
  204. store.dispatch(
  205. "user/preferences/changeActivityWatch",
  206. preferences.activityWatch
  207. );
  208. });
  209. router.beforeEach((to, from, next) => {
  210. if (window.stationInterval) {
  211. clearInterval(window.stationInterval);
  212. window.stationInterval = 0;
  213. }
  214. if (window.socket) ws.removeAllListeners();
  215. ws.clear();
  216. if (
  217. to.meta.loginRequired ||
  218. to.meta.adminRequired ||
  219. to.meta.guestsOnly
  220. ) {
  221. const gotData = () => {
  222. if (to.meta.loginRequired && !store.state.user.auth.loggedIn)
  223. next({ path: "/login" });
  224. else if (
  225. to.meta.adminRequired &&
  226. store.state.user.auth.role !== "admin"
  227. )
  228. next({ path: "/" });
  229. else if (to.meta.guestsOnly && store.state.user.auth.loggedIn)
  230. next({ path: "/" });
  231. else next();
  232. };
  233. if (store.state.user.auth.gotData) gotData();
  234. else {
  235. const watcher = store.watch(
  236. state => state.user.auth.gotData,
  237. () => {
  238. watcher();
  239. gotData();
  240. }
  241. );
  242. }
  243. } else next();
  244. });
  245. Vue.directive("click-outside", {
  246. bind(element, binding) {
  247. window.handleOutsideClick = event => {
  248. if (
  249. !(
  250. element === event.target ||
  251. element.contains(event.target)
  252. )
  253. ) {
  254. binding.value();
  255. }
  256. };
  257. document.body.addEventListener("click", window.handleOutsideClick);
  258. },
  259. unbind() {
  260. document.body.removeEventListener(
  261. "click",
  262. window.handleOutsideClick
  263. );
  264. }
  265. });
  266. // eslint-disable-next-line no-new
  267. new Vue({
  268. router,
  269. store,
  270. el: "#root",
  271. render: wrapper => wrapper(App)
  272. });
  273. })();