diff --git a/README.md b/README.md index 231f55c5a..4404b06ed 100644 --- a/README.md +++ b/README.md @@ -25,17 +25,18 @@ ## Set up an instance To set up a Träwelling instance you'll need: + * [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) * [MariaDB](https://mariadb.org/download) (SQLite is used for running tests) * A local instance of [db-rest v5](https://github.com/derhuerst/db-rest/tree/5) * [Composer](https://getcomposer.org/download/) * PHP 8.0 and the following extensions: - * gd - * sodium - * exif - * pdo_mysql - * pdo_sqlite - + * gd + * sodium + * exif + * pdo_mysql + * pdo_sqlite + After setting up these, you can clone the repository and install the project's dependencies: ```sh @@ -65,8 +66,8 @@ php artisan passport:install Last, but not least, you can run `npm run dev` to build the frontend and watch for changes in the `resources/` folder. -Use your webserver of choice or the in php included dev server (`php artisan serve`) to boot the application. You should see the Träwelling -homepage at http://localhost:8000. +Use your webserver of choice or the in php included dev server (`php artisan serve`) to boot the application. You should +see the Träwelling homepage at http://localhost:8000. ## Contributing diff --git a/app/Http/Resources/StatusResource.php b/app/Http/Resources/StatusResource.php index 9a1aeb6b9..1d7ac3157 100644 --- a/app/Http/Resources/StatusResource.php +++ b/app/Http/Resources/StatusResource.php @@ -15,17 +15,18 @@ class StatusResource extends JsonResource */ public function toArray($request): array { return [ - "id" => (int) $this->id, - "body" => (string) $this->body, - "type" => (string) $this->type, - "createdAt" => (string) $this->created_at, - "user" => (int) $this->user->id, - "username" => (string) $this->user->username, - "business" => (int) $this->business, - "visibility" => (int) $this->visibility, - "likes" => (int) $this->likes->count(), - "liked" => (bool) $this->favorited, - "train" => [ + "id" => (int) $this->id, + "body" => (string) $this->body, + "type" => (string) $this->type, + "createdAt" => (string) $this->created_at, + "user" => (int) $this->user->id, + "username" => (string) $this->user->username, + "preventIndex" => (bool) $this->user->prevent_index, + "business" => (int) $this->business, + "visibility" => (int) $this->visibility, + "likes" => (int) $this->likes->count(), + "liked" => (bool) $this->favorited, + "train" => [ "trip" => (int) $this->trainCheckin->HafasTrip->id, "category" => (string) $this->trainCheckin->HafasTrip->category, "number" => (string) $this->trainCheckin->HafasTrip->number, @@ -39,7 +40,7 @@ public function toArray($request): array { "destination" => new StopoverResource($this->trainCheckin->destination_stopover) ], //ToDo: Custom Resource for event - "event" => empty($this->event) ? null : $this->event + "event" => empty($this->event) ? null : $this->event ]; } } diff --git a/app/Http/Resources/StopoverResource.php b/app/Http/Resources/StopoverResource.php index af883d760..79da17ecb 100644 --- a/app/Http/Resources/StopoverResource.php +++ b/app/Http/Resources/StopoverResource.php @@ -17,6 +17,7 @@ public function toArray($request): array { return [ "id" => (int) $this->train_station_id, "name" => $this->trainStation->name, + "rilIdentifier" => $this->trainStation->rilIdentifier ?? null, "arrival" => $this->arrival, "arrivalPlanned" => $this->arrival_planned, "arrivalReal" => $this->arrival_real, diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 6df52c8d0..d52591f77 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -25,7 +25,8 @@ public function toArray($request): array { 'twitterUrl' => $this->twitterUrl ?? null, 'mastodonUrl' => $this->mastodonUrl ?? null, 'privateProfile' => (bool) $this->private_profile, - 'userInvisibleToMe' => (bool) $this->userInvisibleToMe + 'userInvisibleToMe' => (bool) $this->userInvisibleToMe, + 'preventIndex' => (bool) $this->prevent_index, ]; } } diff --git a/package-lock.json b/package-lock.json index 3fffaf664..f83519e41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "vue-axios": "^3.2.4", "vue-i18n": "^8.24.4", "vue-localstorage": "^0.6.2", + "vue-meta": "^2.4.0", "vue-router": "^3.5.1" }, "devDependencies": { @@ -4429,6 +4430,14 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -10648,6 +10657,14 @@ "resolved": "https://registry.npmjs.org/vue-localstorage/-/vue-localstorage-0.6.2.tgz", "integrity": "sha512-29YQVVkIdoS6BZBCJAyu9d0OR0eKSm5gk5OjsLssV1+NM4zJnf9cxhN1AVeXkUHJLqOonECweuaR8PZ2x307dw==" }, + "node_modules/vue-meta": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/vue-meta/-/vue-meta-2.4.0.tgz", + "integrity": "sha512-XEeZUmlVeODclAjCNpWDnjgw+t3WA6gdzs6ENoIAgwO1J1d5p1tezDhtteLUFwcaQaTtayRrsx7GL6oXp/m2Jw==", + "dependencies": { + "deepmerge": "^4.2.2" + } + }, "node_modules/vue-router": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.2.tgz", @@ -14689,6 +14706,11 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, "default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -19483,6 +19505,14 @@ "resolved": "https://registry.npmjs.org/vue-localstorage/-/vue-localstorage-0.6.2.tgz", "integrity": "sha512-29YQVVkIdoS6BZBCJAyu9d0OR0eKSm5gk5OjsLssV1+NM4zJnf9cxhN1AVeXkUHJLqOonECweuaR8PZ2x307dw==" }, + "vue-meta": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/vue-meta/-/vue-meta-2.4.0.tgz", + "integrity": "sha512-XEeZUmlVeODclAjCNpWDnjgw+t3WA6gdzs6ENoIAgwO1J1d5p1tezDhtteLUFwcaQaTtayRrsx7GL6oXp/m2Jw==", + "requires": { + "deepmerge": "^4.2.2" + } + }, "vue-router": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.2.tgz", diff --git a/package.json b/package.json index a712afe20..838c72a2e 100644 --- a/package.json +++ b/package.json @@ -26,22 +26,23 @@ "vue-template-compiler": "^2.6.12" }, "dependencies": { - "@fortawesome/fontawesome-free": "^5.15.2", - "@websanova/vue-auth": "^4.1.2", - "admin-lte": "^3.0.5", - "awesomplete": "^1.1.5", - "bootstrap-cookie-alert": "^1.2.1", - "chart.js": "^2.9.4", - "croppie": "^2.6.5", - "font-awesome": "^4.7.0", - "lang.js": "^1.1.14", - "leaflet": "^1.7.1", - "mdb-ui-kit": "^3.4.0", - "moment": "^2.29.1", - "momentjs": "^2.0.0", - "vue-axios": "^3.2.4", - "vue-i18n": "^8.24.4", - "vue-localstorage": "^0.6.2", - "vue-router": "^3.5.1" + "@fortawesome/fontawesome-free": "^5.15.2", + "@websanova/vue-auth": "^4.1.2", + "admin-lte": "^3.0.5", + "awesomplete": "^1.1.5", + "bootstrap-cookie-alert": "^1.2.1", + "chart.js": "^2.9.4", + "croppie": "^2.6.5", + "font-awesome": "^4.7.0", + "lang.js": "^1.1.14", + "leaflet": "^1.7.1", + "mdb-ui-kit": "^3.4.0", + "moment": "^2.29.1", + "momentjs": "^2.0.0", + "vue-axios": "^3.2.4", + "vue-i18n": "^8.24.4", + "vue-localstorage": "^0.6.2", + "vue-meta": "^2.4.0", + "vue-router": "^3.5.1" } } diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 88af49a2f..e34704fca 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,10 +1,10 @@ { - "/js/app.js": "/js/app.js?id=873068844483db334a96", - "/js/stats.js": "/js/stats.js?id=a283d9f470f87191f0a4", - "/js/admin.js": "/js/admin.js?id=85cc565cce84bf48d125", - "/js/vue.js": "/js/vue.js?id=5be8c56f457ea4e93bff", - "/css/vue.css": "/css/vue.css?id=08121888397de7c72404", - "/css/app.css": "/css/app.css?id=64d37218f5603a068e3a", - "/css/welcome.css": "/css/welcome.css?id=2ae9a4575e032f1d920d", - "/css/admin.css": "/css/admin.css?id=4630aa46d4953b7c6858" + "/js/app.js": "/js/app.js", + "/js/stats.js": "/js/stats.js", + "/js/admin.js": "/js/admin.js", + "/js/vue.js": "/js/vue.js", + "/css/vue.css": "/css/vue.css", + "/css/app.css": "/css/app.css", + "/css/welcome.css": "/css/welcome.css", + "/css/admin.css": "/css/admin.css" } diff --git a/resources/components/Dashboard.vue b/resources/components/Dashboard.vue index 2ad12a70f..52b18988a 100644 --- a/resources/components/Dashboard.vue +++ b/resources/components/Dashboard.vue @@ -44,6 +44,11 @@ export default { moment: moment }; }, + metaInfo() { + return { + title: this.i18n.get("_.menu.dashboard") + }; + }, components: { StationForm, Status diff --git a/resources/components/Login.vue b/resources/components/Login.vue index 647332e46..393e3ca88 100644 --- a/resources/components/Login.vue +++ b/resources/components/Login.vue @@ -36,15 +36,15 @@ export default { // get the redirect object var redirect = this.$auth.redirect(); this.$auth.login({ - data: { - email: this.email, - password: this.password - }, - redirect: {name: "dashboard"}, - staySignedIn: true, - fetchUser: true, + data: { + email: this.email, + password: this.password + }, + redirect: {name: "dashboard"}, + staySignedIn: true, + fetchUser: true, }).then(() => { - this.$auth.fetch(); + this.$auth.fetch(); }); } } diff --git a/resources/components/ModalConfirm.vue b/resources/components/ModalConfirm.vue index 15cd355a2..ee4109cc6 100644 --- a/resources/components/ModalConfirm.vue +++ b/resources/components/ModalConfirm.vue @@ -2,21 +2,21 @@ @@ -36,11 +36,11 @@ export default { this.modal = new Modal(this.$refs.deleteModal); }, props: { - titleText: null, - abortText: null, - confirmText: null, - confirmButtonColor: null, - bodyText: null + titleText: null, + abortText: null, + confirmText: null, + confirmButtonColor: null, + bodyText: null }, methods: { show() { diff --git a/resources/components/StationForm.vue b/resources/components/StationForm.vue index 111d46bc4..56adbbe9a 100644 --- a/resources/components/StationForm.vue +++ b/resources/components/StationForm.vue @@ -29,28 +29,28 @@ -
- - {{ $auth.user().home.name }} - - +
+ + {{ $auth.user().home.name }} + + {{ i18n.get("_.user.home-not-set") }} - - + + {{ i18n.get("_.stationboard.last-stations") }} - - - - - - - - -
+ + + + + + + + +
diff --git a/resources/js/APImodels.js b/resources/js/APImodels.js index 49ce71ecf..7fc91ed7f 100644 --- a/resources/js/APImodels.js +++ b/resources/js/APImodels.js @@ -22,7 +22,9 @@ export let EventModel = { }; export let Stopover = { + id: 0, name: "", + rilIdentifier: null, trainStationId: 0, arrival: "", arrivalPlanned: "", @@ -45,6 +47,7 @@ export let StatusModel = { type: "", createdAt: "", user: 0, + preventIndex: true, username: "", business: 0, visibility: 0, @@ -81,6 +84,7 @@ export let ProfileModel = { mastodonUrl: null, privateProfile: false, userInvisibleToMe: true, + preventIndex: true, }; export let LeaderboardUserModel = { diff --git a/resources/js/vue.js b/resources/js/vue.js index 4c44c7172..6ae32544b 100644 --- a/resources/js/vue.js +++ b/resources/js/vue.js @@ -3,13 +3,6 @@ */ import Vue from "vue"; - -require("jquery"); - -require("./bootstrap"); -require("awesomplete/awesomplete"); -require("leaflet/dist/leaflet.js"); - import VueRouter from "vue-router"; import {router} from "../routes"; import App from "../views/App"; @@ -23,6 +16,13 @@ import auth from "@websanova/vue-auth/dist/v2/vue-auth.esm.js"; import driverAuthBearer from "@websanova/vue-auth/dist/drivers/auth/bearer.esm.js"; import driverHttpAxios from "@websanova/vue-auth/dist/drivers/http/axios.1.x.esm.js"; import driverRouterVueRouter from "@websanova/vue-auth/dist/drivers/router/vue-router.2.x.esm.js"; +import VueMeta from "vue-meta"; + +require("jquery"); + +require("./bootstrap"); +require("awesomplete/awesomplete"); +require("leaflet/dist/leaflet.js"); window.Vue = require("vue"); @@ -70,6 +70,11 @@ Vue.use(auth, { // refreshData: {url: "auth/refresh", method: "GET", enabled: true, interval: 30} }); +Vue.use(VueMeta, { + tagIDKeyName: "vmid", + refreshOnceOnNavigation: true +}); + new Vue({ el: "#app", components: {App}, diff --git a/resources/routes.js b/resources/routes.js index d8e6b86f6..3c54c260f 100644 --- a/resources/routes.js +++ b/resources/routes.js @@ -94,7 +94,6 @@ export const router = new VueRouter({ auth: true } } - ], base: "/", }); diff --git a/resources/views/ActiveStatuses.vue b/resources/views/ActiveStatuses.vue index 8fd9852ce..3c7cde2e0 100644 --- a/resources/views/ActiveStatuses.vue +++ b/resources/views/ActiveStatuses.vue @@ -37,6 +37,7 @@ import Map from "../components/Map"; import {StatusModel} from "../js/APImodels"; export default { + name: "ActiveStatuses", data() { return { loading: true, @@ -47,6 +48,16 @@ export default { polylines: null //ToDo Typedef }; }, + metaInfo() { + return { + title: this.i18n.get("_.menu.active"), + meta: [ + {name: "robots", content: "index", vmid: "robots"}, + {name: "description", content: this.i18n.get("_.description.en-route"), vmid: "description"}, + {name: "DC.Description", content: this.i18n.get("_.description.en-route"), vmid: "DC.Description"} + ] + }; + }, components: { Status, Map diff --git a/resources/views/App.vue b/resources/views/App.vue index ef972035e..fd74a7211 100644 --- a/resources/views/App.vue +++ b/resources/views/App.vue @@ -156,12 +156,46 @@ import Vue from "vue"; import {languages} from "../js/translations"; export default { + name: "App", data() { return { notificationsCount: 1, langs: languages }; }, + metaInfo() { + return { + title: "Träwelling", + titleTemplate: "%s - Träwelling",//ToDo get name from .env + htmlAttrs: { + lang: this.i18n.getLocale() + }, + meta: [ + {name: "charset", "content": "utf-8"}, + {name: "viewport", content: "width=device-width, initial-scale=1"}, + {name: "apple-mobile-web-app-capable", content: "yes"}, + {name: "apple-mobile-web-app-status-bar-style", content: "#c72730"}, + {name: "mobile-web-app-capable", content: "yes"}, + {name: "theme-color", content: "#c72730"}, + {name: "name", content: "Träwelling"}, //ToDo get name from .env + + {name: "copyright", content: "Träwelling Team"}, + {name: "description", content: this.i18n.get("_.about.block1"), vmid: "description"}, + { + name: "keywords", + content: "Träwelling, Twitter, Deutsche, Bahn, Travel, Check-In, Zug, Bus, Tram, Mastodon" + }, + {name: "audience", conent: "Travellers"}, + {name: "DC.Rights", content: "Träwelling Team"}, + {name: "DC.Description", content: this.i18n.get("_.about.block1"), vmid: "DC.Description"}, + {name: "DC.Language", content: this.i18n.getLocale()}, + {property: "og:title", content: "Träwelling", vmid: "og:title"}, //ToDo get name from .env + {property: "og:site_name", content: "Träwelling"}, //ToDo get name from .env + {property: "og:type", content: "website"}, + {name: "robots", content: "index,follow", vmid: "robots"} + ] + }; + }, components: { NotificationsButton, NotificationsModal diff --git a/resources/views/Event.vue b/resources/views/Event.vue index dc13f2974..d51d7075d 100644 --- a/resources/views/Event.vue +++ b/resources/views/Event.vue @@ -63,6 +63,7 @@ import {EventModel, StatusModel} from "../js/APImodels"; export default { name: "Event", + //ToDo add Meta Tags data() { return { username: this.$route.params.username, diff --git a/resources/views/Leaderboard.vue b/resources/views/Leaderboard.vue index 2cd5a0cb5..06bf50225 100644 --- a/resources/views/Leaderboard.vue +++ b/resources/views/Leaderboard.vue @@ -59,7 +59,6 @@ import moment from "moment"; import LeaderboardTable from "../components/LeaderboardTable"; import axios from "axios"; -import {LeaderboardUserModel} from "../js/APImodels"; export default { //ToDo format numbers correctly for languages, etc. @@ -73,6 +72,15 @@ export default { loading: false }; }, + metaInfo() { + return { + title: this.i18n.get("_.menu.leaderboard"), + meta: [ + {name: "description", content: this.i18n.get("_.description.leaderboard.main"), vmid: "description"}, + {name: "DC.Description", content: this.i18n.get("_.description.leaderboard.main"), vmid: "DC.Description"} + ] + }; + }, components: { LeaderboardTable }, diff --git a/resources/views/LeaderboardMonth.vue b/resources/views/LeaderboardMonth.vue index c8b2661ea..a7673f872 100644 --- a/resources/views/LeaderboardMonth.vue +++ b/resources/views/LeaderboardMonth.vue @@ -126,7 +126,19 @@ export default { return { moment: moment, users: [LeaderboardUserModel], - loading: false + loading: false, + metaData: { + description: undefined + } + }; + }, + metaInfo() { + return { + title: this.i18n.get("_.menu.leaderboard"), + meta: [ + {name: "description", content: this.metaData.description, vmid: "description"}, + {name: "DC.Description", content: this.metaData.description, vmid: "DC.Description"} + ] }; }, computed: { @@ -158,6 +170,12 @@ export default { this.error = error.data.message || error.message; }); }, + updateMetadata() { + this.metaData.description = this.i18n.choice("_.description.leaderboard.monthly", 1, { + "month": this.month.format("MMMM"), + "year": this.month.format("YYYY") + }); + } }, watch: { month() { @@ -165,6 +183,7 @@ export default { } }, created() { + this.updateMetadata(); this.fetchData(); } }; diff --git a/resources/views/Profile.vue b/resources/views/Profile.vue index 439e8caea..815cef984 100644 --- a/resources/views/Profile.vue +++ b/resources/views/Profile.vue @@ -88,14 +88,26 @@ import Status from "../components/Status"; import {ProfileModel, StatusModel} from "../js/APImodels"; export default { - name: "Profile", + name: "ProfilePage", data() { return { username: this.$route.params.username, loading: false, statusesLoading: false, user: ProfileModel, - statuses: [StatusModel] + statuses: [StatusModel], + description: undefined, + robots: "noindex", + }; + }, + metaInfo() { + return { + title: this.user.displayName, + meta: [ + {name: "robots", content: this.robots, vmid: "robots"}, + {name: "description", content: this.description, vmid: "description"}, + {name: "DC.Description", content: this.description, vmid: "DC.Description"} + ] }; }, components: { @@ -121,6 +133,16 @@ export default { } return moment(item.train.origin.departure).date() !== moment(statuses[index - 1].train.origin.departure).date(); }, + updateMetadata() { + this.description = this.i18n.choice("_.description.profile", 1, { + "username": this.user.username, + "kmAmount": this.user.trainDistance.toFixed(2), + "hourAmount": this.duration + }); + if (this.user.preventIndex) { + this.robots = "noindex"; + } + }, fetchData() { this.error = null; this.loading = true; @@ -129,6 +151,7 @@ export default { .then((response) => { this.loading = false; this.user = response.data.data; + this.updateMetadata(); if (!this.user.userInvisibleToMe) { this.fetchStatuses(); } diff --git a/resources/views/SingleStatus.vue b/resources/views/SingleStatus.vue index 891dc94fc..a687b3213 100644 --- a/resources/views/SingleStatus.vue +++ b/resources/views/SingleStatus.vue @@ -3,7 +3,7 @@
- {{ i18n.get("_.vue.loading") }} + {{ i18n.get("_.vue.loading") }}
@@ -28,7 +28,7 @@ import axios from "axios"; import Status from "../components/Status"; import moment from "moment"; -import {ProfileModel, StatusModel} from "../js/APImodels"; +import {StatusModel} from "../js/APImodels"; export default { name: "SingleStatus", @@ -40,7 +40,33 @@ export default { polyline: null, //ToDo Typedef stopovers: null, //ToDo Typedef likes: null, - moment: moment + moment: moment, + metaData: { + title: undefined, + url: undefined, + image: undefined, + description: undefined, + robots: undefined + } + }; + }, + metaInfo() { + return { + title: this.metaData.title, + meta: [ + {name: "robots", content: this.metaData.robots, vmid: "robots"}, + {name: "description", content: this.metaData.description, vmid: "description"}, + {name: "DC.Description", content: this.metaData.description, vmid: "DC.Description"}, + {name: "og:title", content: this.metaData.title, vmid: "og:title"}, + {name: "og:url", content: this.metaData.url, vmid: "og:url"}, + {name: "og:image", content: this.metaData.image, vmid: "og:image"}, + {name: "og:description", content: this.metaData.description, vmid: "og:description"}, + {name: "twitter:card", content: "summary", vmid: "twitter:card"}, + {name: "twitter:site", content: "@traewelling", vmid: "twitter:site"}, + {name: "twitter:title", content: this.metaData.title, vmid: "twitter:title"}, + {name: "twitter:description", content: this.metaData.description, vmid: "twitter:description"}, + {name: "twitter:image", content: this.metaData.image, vmid: "twitter:image"} + ] }; }, created() { @@ -48,6 +74,7 @@ export default { this.fetchData(); } else { this.status = this.statusData; + this.updateMetadata(); this.fetchPolyline(); this.fetchLikes(); } @@ -58,6 +85,20 @@ export default { props: { statusData: null }, + computed: { + rilIdentifierOrigin() { + if (this.status.train.origin.rilIdentifier) { + return " (" + this.status.train.origin.rilIdentifier + ")"; + } + return ""; + }, + rilIdentifierDestination() { + if (this.status.train.destination.rilIdentifier) { + return " (" + this.status.train.destination.rilIdentifier + ")"; + } + return ""; + }, + }, methods: { fetchData() { this.error = null; @@ -67,6 +108,7 @@ export default { .then((response) => { this.loading = false; this.status = response.data.data; + this.updateMetadata(); this.fetchPolyline(); this.fetchStopovers(); this.fetchLikes(); @@ -105,6 +147,24 @@ export default { .catch((error) => { console.error(error); }) + }, + updateMetadata() { + if (this.status.preventIndex) { + this.metaData.robots = "noindex"; + } + this.metaData.description = this.i18n.choice("_.description.status", 1, { + "username": this.status.username, + "origin": this.status.train.origin.name + this.rilIdentifierOrigin, + "destination": this.status.train.destination.name + this.rilIdentifierDestination, + "date": this.moment(this.status.train.origin.departure).format("LLL"), + "lineName": this.status.train.lineName + }); + this.metaData.url = window.location.origin + this.$router.resolve({ + name: "singleStatus", + params: {id: this.status.id} + }).href; //ToDo combine all window.location.origin...-methods to one single method + this.metaData.title = this.i18n.choice("_.status.ogp-title", 1, {"name": this.status.username}); + this.metaData.image = "/profile/" + this.status.username + "/profilepicture"; } } } diff --git a/resources/views/Stationboard.vue b/resources/views/Stationboard.vue index 048271d00..8a90e44c3 100644 --- a/resources/views/Stationboard.vue +++ b/resources/views/Stationboard.vue @@ -7,10 +7,10 @@
{{ station.name }} @@ -69,9 +69,9 @@ {{ departure.line.fahrtNr }} - - {{ departure.direction }} - + + {{ departure.direction }} + @@ -79,15 +79,15 @@
- +
@@ -99,21 +99,21 @@ import moment from "moment"; import {travelImages} from "../js/APImodels"; export default { - name: "Stationboard", - components: { - StationForm, - ModalConfirm - }, - data() { - return { - station: null, - departures: null, - times: { - now: 0, - prev: 0, - next: 0 - }, - loading: false, + name: "Stationboard", + components: { + StationForm, + ModalConfirm + }, + data() { + return { + station: null, + departures: null, + times: { + now: 0, + prev: 0, + next: 0 + }, + loading: false, images: travelImages, moment: moment }; @@ -143,33 +143,33 @@ export default { goToTrip(departure) { if (departure.cancelled) { console.error("stop cancelled"); - return; + return; } - this.$router.push({ - name: "trains.trip", query: { - tripID: departure.tripId, - lineName: departure.line.name ?? departure.line.fahrtNr, - start: departure.station.id, - departure: departure.plannedWhen - } - }); + this.$router.push({ + name: "trains.trip", query: { + tripID: departure.tripId, + lineName: departure.line.name ?? departure.line.fahrtNr, + start: departure.station.id, + departure: departure.plannedWhen + } + }); }, - toggleSetHomeModal() { - this.$refs.confirmHomeModal.show(); - }, - setHome() { - axios - .put("/trains/station/" + this.station.name + "/home") - .then((result) => { - this.result = result.data.data; - //ToDo add a confirm popup or sth - this.$auth.fetch(); - alert(this.i18n.choice("_.user.home-set", 1, {"station": this.result.name})); - }) - .catch((error) => { - console.error(error); - }); - } + toggleSetHomeModal() { + this.$refs.confirmHomeModal.show(); + }, + setHome() { + axios + .put("/trains/station/" + this.station.name + "/home") + .then((result) => { + this.result = result.data.data; + //ToDo add a confirm popup or sth + this.$auth.fetch(); + alert(this.i18n.choice("_.user.home-set", 1, {"station": this.result.name})); + }) + .catch((error) => { + console.error(error); + }); + } } }; diff --git a/resources/views/landing.blade.php b/resources/views/landing.blade.php index d6a411b4b..6e105cc4f 100644 --- a/resources/views/landing.blade.php +++ b/resources/views/landing.blade.php @@ -1,42 +1,27 @@ - + - - - + {{ config('app.name', 'Träwelling') }} - - @yield('title') - {{ config('app.name', 'Träwelling') }} - - - - + + + + - - - - - - - - -
- +
- - diff --git a/routes/api.php b/routes/api.php index a6d13c890..99f71fc2b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -11,6 +11,7 @@ | */ +use App\Http\Controllers\API\v1\AuthController as v1Auth; use App\Http\Controllers\API\v1\EventController; use App\Http\Controllers\API\v1\LikesController; use App\Http\Controllers\API\v1\NotificationController; @@ -18,7 +19,6 @@ use App\Http\Controllers\API\v1\StatusController; use App\Http\Controllers\API\v1\TransportController; use App\Http\Controllers\API\v1\UserController; -use App\Http\Controllers\API\v1\AuthController as v1Auth; use Illuminate\Support\Facades\Route; Route::group(['prefix' => 'v1', 'middleware' => 'return-json'], function() {