Ask for confirmation for nodes outside of community bounds.

This commit is contained in:
baldo 2022-09-01 14:38:41 +02:00
parent 9387df8dd3
commit 4c6556de3f
6 changed files with 222 additions and 14 deletions

View file

@ -19,6 +19,7 @@
}, },
"dependencies": { "dependencies": {
"fork-awesome": "^1.2.0", "fork-awesome": "^1.2.0",
"geolib": "^3.3.3",
"leaflet": "^1.8.0", "leaflet": "^1.8.0",
"pinia": "^2.0.21", "pinia": "^2.0.21",
"sparkson": "^1.3.6", "sparkson": "^1.3.6",

View file

@ -0,0 +1,36 @@
<script setup lang="ts">
import type { ComponentVariant } from "@/types";
interface Props {
icon: string;
variant: ComponentVariant;
}
const props = defineProps<Props>();
</script>
<template>
<span :class="['floating-icon', props.variant]">
<i :class="['fa', `fa-${props.icon}`]" aria-hidden="true" />
</span>
</template>
<style scoped lang="scss">
@import "../scss/variables";
.floating-icon {
display: block;
float: left;
margin: 0 0.5em 0.25em 0;
i {
font-size: 3em;
}
}
@each $variant, $color in $variant-colors {
.floating-icon.#{$variant} {
color: $color;
}
}
</style>

View file

@ -133,6 +133,14 @@ function centerOnCoordinates() {
function renderMap() { function renderMap() {
const { layers, defaultLayers } = getLayers(); const { layers, defaultLayers } = getLayers();
createMap(defaultLayers, layers); createMap(defaultLayers, layers);
if (
map &&
configStore.getConfig.otherCommunityInfo.showBorderForDebugging
) {
new L.Polygon(
configStore.getConfig.otherCommunityInfo.localCommunityPolygon
).addTo(map);
}
centerOnCoordinates(); centerOnCoordinates();
updateMarker(); updateMarker();
} }

View file

@ -19,6 +19,7 @@ import {
ComponentVariant, ComponentVariant,
hasOwnProperty, hasOwnProperty,
} from "@/types"; } from "@/types";
import FloatingIcon from "@/components/FloatingIcon.vue";
import ErrorCard from "@/components/ErrorCard.vue"; import ErrorCard from "@/components/ErrorCard.vue";
import ButtonGroup from "@/components/form/ButtonGroup.vue"; import ButtonGroup from "@/components/form/ButtonGroup.vue";
import ValidationForm from "@/components/form/ValidationForm.vue"; import ValidationForm from "@/components/form/ValidationForm.vue";
@ -27,8 +28,12 @@ import { route, RouteName } from "@/router";
import RouteButton from "@/components/form/RouteButton.vue"; import RouteButton from "@/components/form/RouteButton.vue";
import { ApiError } from "@/utils/Api"; import { ApiError } from "@/utils/Api";
import NodeCoordinatesInput from "@/components/nodes/NodeCoordinatesInput.vue"; import NodeCoordinatesInput from "@/components/nodes/NodeCoordinatesInput.vue";
import OutsideOfCommunityConfirmationForm from "@/components/nodes/OutsideOfCommunityConfirmationForm.vue";
import CheckboxInput from "@/components/form/CheckboxInput.vue"; import CheckboxInput from "@/components/form/CheckboxInput.vue";
import InfoCard from "@/components/InfoCard.vue"; import InfoCard from "@/components/InfoCard.vue";
import { isPointInPolygon } from "geolib";
import { parseToFloat } from "@/utils/Numbers";
import { forConstraint } from "../../shared/validation/validator";
const configStore = useConfigStore(); const configStore = useConfigStore();
const nodeStore = useNodeStore(); const nodeStore = useNodeStore();
@ -63,6 +68,9 @@ const nicknameModel = ref("" as Nickname);
const emailModel = ref("" as EmailAddress); const emailModel = ref("" as EmailAddress);
const monitoringModel = ref(false); const monitoringModel = ref(false);
const showOutsideOfCommunityForm = ref(false);
const confirmedOutsideOfCommunity = ref(false);
onMounted(() => { onMounted(() => {
if (props.hostname) { if (props.hostname) {
hostnameModel.value = props.hostname; hostnameModel.value = props.hostname;
@ -75,6 +83,30 @@ onMounted(() => {
} }
}); });
function isOutsideCommunity(): boolean {
if (!forConstraint(CONSTRAINTS.node.coords, false)(coordsModel.value)) {
return false;
}
const [lat, lng] = coordsModel.value.split(" ").map(parseToFloat);
return !isPointInPolygon(
{ lat, lng },
configStore.getConfig.otherCommunityInfo.localCommunityPolygon
);
}
async function onConfirmOutsideOfCommunity() {
showOutsideOfCommunityForm.value = false;
confirmedOutsideOfCommunity.value = true;
await onSubmit();
}
async function onCancelOutsideOfCommunity() {
showOutsideOfCommunityForm.value = false;
window.scrollTo(0, 0);
}
async function onSubmit() { async function onSubmit() {
generalError.value = false; generalError.value = false;
conflictErrorMessage.value = undefined; conflictErrorMessage.value = undefined;
@ -82,6 +114,18 @@ async function onSubmit() {
// Make sure to re-render error message to trigger scrolling into view. // Make sure to re-render error message to trigger scrolling into view.
await nextTick(); await nextTick();
if (
configStore.getConfig.otherCommunityInfo.showInfo &&
isOutsideCommunity() &&
!confirmedOutsideOfCommunity.value
) {
showOutsideOfCommunityForm.value = true;
} else {
await createNode();
}
}
async function createNode(): Promise<void> {
try { try {
const node = await nodeStore.create({ const node = await nodeStore.create({
hostname: hostnameModel.value, hostname: hostnameModel.value,
@ -115,7 +159,17 @@ async function onSubmit() {
</script> </script>
<template> <template>
<ValidationForm novalidate="" ref="form" @submit="onSubmit"> <OutsideOfCommunityConfirmationForm
v-if="showOutsideOfCommunityForm"
@confirm="onConfirmOutsideOfCommunity"
@cancel="onCancelOutsideOfCommunity"
/>
<ValidationForm
v-if="!showOutsideOfCommunityForm"
novalidate=""
ref="form"
@submit="onSubmit"
>
<h2>Neuen Knoten anmelden</h2> <h2>Neuen Knoten anmelden</h2>
<div> <div>
@ -216,7 +270,10 @@ async function onSubmit() {
<fieldset v-if="configStore.getConfig.monitoring.enabled"> <fieldset v-if="configStore.getConfig.monitoring.enabled">
<h3>Möchtest Du automatisiert Status-E-Mails bekommen?</h3> <h3>Möchtest Du automatisiert Status-E-Mails bekommen?</h3>
<i class="monitoring-icon fa fa-heartbeat" aria-hidden="true" /> <FloatingIcon
icon="heartbeat"
:variant="ComponentVariant.PRIMARY"
/>
<p class="help-block"> <p class="help-block">
Du kannst Dich automatisiert benachrichtigen lassen, sobald Du kannst Dich automatisiert benachrichtigen lassen, sobald
@ -266,8 +323,6 @@ async function onSubmit() {
</InfoCard> </InfoCard>
</fieldset> </fieldset>
<h1>TODO: Check community bounds</h1>
<ButtonGroup <ButtonGroup
:align="ComponentAlignment.RIGHT" :align="ComponentAlignment.RIGHT"
:button-size="ButtonSize.SMALL" :button-size="ButtonSize.SMALL"
@ -294,13 +349,4 @@ async function onSubmit() {
</ValidationForm> </ValidationForm>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped></style>
@import "../../scss/variables";
.monitoring-icon {
float: left;
margin: 0 0.25em 0.125em 0;
font-size: 3em;
color: $variant-color-primary;
}
</style>

View file

@ -0,0 +1,112 @@
<script setup lang="ts">
import { useConfigStore } from "@/stores/config";
import ButtonGroup from "@/components/form/ButtonGroup.vue";
import { ButtonSize, ComponentAlignment, ComponentVariant } from "@/types";
import ActionButton from "@/components/form/ActionButton.vue";
import ValidationForm from "@/components/form/ValidationForm.vue";
import FloatingIcon from "@/components/FloatingIcon.vue";
import { onMounted } from "vue";
const configStore = useConfigStore();
const emit = defineEmits<{
(e: "cancel"): void;
(e: "confirm"): void;
}>();
onMounted(() => {
window.scrollTo(0, 0);
});
function onCancel() {
emit("cancel");
}
function onSubmit() {
emit("confirm");
}
</script>
<template>
<ValidationForm class="outside-of-community-form" @submit="onSubmit">
<h2>
Dein Router steht außerhalb des
{{ configStore.getConfig.community.name }} Gebiets
</h2>
<p>
<strong
>Erst mal großartig, dass du Freifunk machen möchtest!</strong
>
</p>
<FloatingIcon icon="community" :variant="ComponentVariant.SECONDARY" />
<p>
Freifunk steht u.a. für Denzentralität. Deshalb wäre es klasse wenn
Du Dich einer Community in Deiner Nähe anschließt oder vielleicht
sogar
<a
href="https://freifunk.net/wie-mache-ich-mit/lokale-gruppe-gruenden/"
target="_blank"
>Deine eigene gründest</a
>. Da aller Anfang schwer ist, macht es für eben diesen vielleicht
Sinn sich erst mal einer
<a
href="https://freifunk.net/wie-mache-ich-mit/community-finden/"
target="_blank"
>nahegelegen Gruppe</a
>
anzuschließen. Auch wir von
{{ configStore.getConfig.community.name }} stehen Dir natürlich
gerne mit Rat und Tat zur Seite und mit den existierenden
Hilfsmitteln ist die technische Hürde auch nicht mehr ganz so
gigantisch.
</p>
<p>
Es gibt auch technische Gründe, die gegen eine Erweiterung über die
Grenzen von
{{ configStore.getConfig.community.name }} hinaus sprechen. Das
Problem mit Mesh-Netzen heute ist, dass sie nicht besonders gut
skalieren und mit jedem weiteren Router die Leistung nach unten
geht.
</p>
<p>
Im Moment kommen ständig neue Freifunk-Communities dazu. Welche
Communities es schon gibt, siehts Du
<a
href="https://freifunk.net/wie-mache-ich-mit/community-finden/"
target="_blank"
>in dieser Übersicht</a
>.
</p>
<ButtonGroup
:align="ComponentAlignment.CENTER"
:button-size="ButtonSize.MEDIUM"
>
<ActionButton
type="reset"
icon="times"
:variant="ComponentVariant.SECONDARY"
:size="ButtonSize.SMALL"
@click="onCancel"
>
Abbrechen
</ActionButton>
<ActionButton
type="submit"
icon="dot-circle-o"
:variant="ComponentVariant.INFO"
:size="ButtonSize.SMALL"
>
Knoten dennoch anmelden
</ActionButton>
</ButtonGroup>
</ValidationForm>
</template>
<style lang="scss"></style>

View file

@ -1291,6 +1291,11 @@ functional-red-black-tree@^1.0.1:
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
geolib@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/geolib/-/geolib-3.3.3.tgz#17f5a0dcdc0b051bd631b66f7131d2c14c54a15b"
integrity sha512-YO704pzdB/8QQekQuDmFD5uv5RAwAf4rOUPdcMhdEOz+HoPWD0sC7Qqdwb+LAvwIjXVRawx0QgZlocKYh8PFOQ==
get-func-name@^2.0.0: get-func-name@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"