App: prepare for embedding in native app (#12490)
|
|
@ -384,6 +384,10 @@ small {
|
|||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>");
|
||||
}
|
||||
|
||||
.dark .text-muted {
|
||||
color: var(--bs-gray-medium) !important;
|
||||
}
|
||||
|
||||
.dark .form-select:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
|
@ -474,3 +478,17 @@ input[type="time"]::-webkit-calendar-picker-indicator {
|
|||
width: 50% !important;
|
||||
}
|
||||
}
|
||||
|
||||
html.app .hide-in-app {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.safe-area-inset {
|
||||
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom)
|
||||
env(safe-area-inset-left);
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
margin-top: max(var(--bs-modal-margin), env(safe-area-inset-top));
|
||||
margin-bottom: max(var(--bs-modal-margin), env(safe-area-inset-bottom));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-title" content="evcc" />
|
||||
<meta name="application-name" content="evcc" />
|
||||
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, viewport-fit=cover" />
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="meta/apple-touch-icon.png?[[.Version]]" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="meta/favicon-32x32.png?[[.Version]]" />
|
||||
|
|
@ -18,6 +19,7 @@
|
|||
<meta name="msapplication-TileColor" content="#1C2445" />
|
||||
<meta name="msapplication-config" content="meta/browserconfig.xml?[[.Version]]" />
|
||||
<meta name="theme-color" content="#020318" />
|
||||
<meta name="evcc-app-compatible" content="true" />
|
||||
|
||||
<title>evcc</title>
|
||||
</head>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import setupRouter from "./router";
|
|||
import setupI18n from "./i18n";
|
||||
import featureflags from "./featureflags";
|
||||
import { watchThemeChanges } from "./theme";
|
||||
import { appDetection, sendToApp } from "./utils/native";
|
||||
|
||||
// lazy load smoothscroll polyfill. mainly for safari < 15.4
|
||||
if (!window.CSS.supports("scroll-behavior", "smooth")) {
|
||||
|
|
@ -23,6 +24,7 @@ const app = createApp({
|
|||
watch: {
|
||||
offline: function (value) {
|
||||
console.log(`we are ${value ? "offline" : "online"}`);
|
||||
sendToApp({ type: value ? "offline" : "online" });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -70,3 +72,4 @@ app.mixin(VueHeadMixin);
|
|||
window.app = app.mount("#app");
|
||||
|
||||
watchThemeChanges();
|
||||
appDetection();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="d-flex flex-column site">
|
||||
<div class="d-flex flex-column site safe-area-inset">
|
||||
<div class="container px-4 top-area">
|
||||
<div class="d-flex justify-content-between align-items-center my-3">
|
||||
<h1 class="d-block my-0">
|
||||
|
|
|
|||
|
|
@ -51,16 +51,6 @@
|
|||
{{ $t("batterySettings.modalTitle") }}
|
||||
</button>
|
||||
</li>
|
||||
<li v-if="gridModalAvailable">
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item"
|
||||
data-testid="topnavigation-grid"
|
||||
@click="openGridSettingsModal"
|
||||
>
|
||||
{{ $t("gridSettings.modalTitle") }}
|
||||
</button>
|
||||
</li>
|
||||
<li v-if="$hiddenFeatures()">
|
||||
<router-link class="dropdown-item" to="/config">
|
||||
Device Configuration 🧪
|
||||
|
|
@ -101,6 +91,11 @@
|
|||
></shopicon-regular-newtab>
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="isApp">
|
||||
<button type="button" class="dropdown-item" @click="openNativeSettings">
|
||||
{{ $t("header.nativeSettings") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -115,6 +110,7 @@ import "@h2d2/shopicons/es/regular/newtab";
|
|||
import collector from "../mixins/collector";
|
||||
|
||||
import baseAPI from "../baseapi";
|
||||
import { isApp, sendToApp } from "../utils/native";
|
||||
|
||||
export default {
|
||||
name: "TopNavigation",
|
||||
|
|
@ -130,6 +126,11 @@ export default {
|
|||
sponsorTokenExpires: Number,
|
||||
batteryConfigured: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isApp: isApp(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
logoutCount() {
|
||||
return this.providerLogins.filter((login) => !login.loggedIn).length;
|
||||
|
|
@ -186,6 +187,9 @@ export default {
|
|||
);
|
||||
modal.show();
|
||||
},
|
||||
openNativeSettings() {
|
||||
sendToApp({ type: "settings" });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
14
assets/js/utils/native.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export function isApp() {
|
||||
return navigator.userAgent.includes("evcc/");
|
||||
}
|
||||
|
||||
export function appDetection() {
|
||||
if (isApp()) {
|
||||
const $html = document.querySelector("html");
|
||||
$html.classList.add("app");
|
||||
}
|
||||
}
|
||||
|
||||
export function sendToApp(data) {
|
||||
window.ReactNativeWebView?.postMessage(JSON.stringify(data));
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
<template>
|
||||
<div class="container px-4">
|
||||
<div class="container px-4 safe-area-inset">
|
||||
<TopHeader :title="$t('sessions.title')" />
|
||||
<div class="row">
|
||||
<main class="col-12">
|
||||
<div class="d-flex align-items-baseline justify-content-between my-3 my-md-5">
|
||||
<div
|
||||
class="d-flex align-items-baseline justify-content-between my-3 my-md-5 month-header"
|
||||
>
|
||||
<router-link
|
||||
class="d-flex text-decoration-none align-items-center"
|
||||
:class="{ 'pe-none': !hasPrev, 'text-muted': !hasPrev }"
|
||||
|
|
@ -630,6 +632,21 @@ export default {
|
|||
.sticky-bottom {
|
||||
z-index: 1;
|
||||
}
|
||||
.sticky-top th {
|
||||
padding-top: max(0.5rem, env(safe-area-inset-top));
|
||||
}
|
||||
.table-outer {
|
||||
position: relative;
|
||||
top: calc(max(0.5rem, env(safe-area-inset-top)) * -1);
|
||||
}
|
||||
.month-header {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.sticky-bottom th {
|
||||
padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
|
||||
border-bottom: none;
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.table td,
|
||||
.table th {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="root">
|
||||
<div class="root safe-area-inset">
|
||||
<div class="container px-4">
|
||||
<TopHeader title="Configuration 🧪" />
|
||||
<div class="wrapper">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 15 KiB |
|
|
@ -170,6 +170,7 @@ blog = "Blog"
|
|||
docs = "Dokumentation"
|
||||
github = "GitHub"
|
||||
login = "Fahrzeug-Logins"
|
||||
nativeSettings = "Server ändern"
|
||||
needHelp = "Hilfe benötigt?"
|
||||
sessions = "Ladevorgänge"
|
||||
|
||||
|
|
|
|||
|
|
@ -167,7 +167,8 @@ blog = "Blog"
|
|||
docs = "Documentation"
|
||||
github = "GitHub"
|
||||
login = "Vehicle Logins"
|
||||
needHelp = "Need help?"
|
||||
nativeSettings = "Change Server"
|
||||
needHelp = "Need Help?"
|
||||
sessions = "Charging Sessions"
|
||||
|
||||
[help]
|
||||
|
|
|
|||