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 @@
-
+
@@ -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() {