mirror of
https://github.com/owntracks/frontend.git
synced 2026-02-25 21:53:49 +00:00
Compare commits
12 Commits
v2.0.0-bet
...
v2.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dc9611a77 | ||
|
|
c1f58c992e | ||
|
|
6631929d6f | ||
|
|
36281db2e3 | ||
|
|
5a8d261943 | ||
|
|
7b83349dc8 | ||
|
|
bc3670df99 | ||
|
|
95613753a9 | ||
|
|
cfa3052a0a | ||
|
|
0bd84f4de5 | ||
|
|
85e51643bf | ||
|
|
6cbdf30580 |
@@ -7,6 +7,12 @@ module.exports = {
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "warn",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn",
|
||||
"max-len": [
|
||||
"error",
|
||||
{
|
||||
ignoreUrls: true,
|
||||
},
|
||||
],
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
liberapay: owntracks.org
|
||||
44
CHANGELOG.md
44
CHANGELOG.md
@@ -1,5 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
## 2.0.0-beta.9 (2020-02-06)
|
||||
|
||||
- Support locale with language and region part (`en-GB`)
|
||||
- Update docs (screenshot, changelog improvements, typo fix)
|
||||
- Add funding information
|
||||
|
||||
## 2.0.0-beta.8 (2020-01-26)
|
||||
|
||||
- Add friendly device name and face images to location history popups
|
||||
- Add missing `alt`/`title` to device face image
|
||||
- Fix all JSDoc `@return` directives to `@returns`
|
||||
- Use computed prop for device name in location popup
|
||||
- Enable ESLint `max-len` rule
|
||||
|
||||
## 2.0.0-beta.7 (2020-01-24)
|
||||
|
||||
This release doesn't really affect end-users but greatly improves the development experience.
|
||||
@@ -14,19 +28,19 @@ This release doesn't really affect end-users but greatly improves the developmen
|
||||
|
||||
## 2.0.0-beta.6 (2019-12-14)
|
||||
|
||||
- Fix heatmap - the upgrade of `vue2-leaflet` from 2.2.1 to 2.3.0 added an `activated` attribute to layers causing the heatmap to not show
|
||||
- Fix heatmap - the upgrade of `vue2-leaflet` from 2.2.1 to 2.3.0 added an `activated` attribute to layers causing the heatmap to not show ([#18](https://github.com/owntracks/frontend/issues/18))
|
||||
|
||||
## 2.0.0-beta.5 (2019-12-14)
|
||||
|
||||
- Add Leaflet popup close button background color transition
|
||||
- Add `$config` Vue instance property
|
||||
- Improve accessibility
|
||||
- Improve accessibility ([#9](https://github.com/owntracks/frontend/issues/9))
|
||||
- Use configured locale for timestamp formatting
|
||||
- Upgrade dependencies
|
||||
|
||||
## 2.0.0-beta.4 (2019-12-14)
|
||||
|
||||
- Add support for time selection
|
||||
- Add support for time selection ([#10](https://github.com/owntracks/frontend/issues/10))
|
||||
- New date/time picker component is properly translated/localised and keyboard accessible
|
||||
- Config options are now `startDateTime`/`endDateTime` and format of URL parameters changed
|
||||
- Changed default start/end date and time to use local timezone
|
||||
@@ -37,7 +51,7 @@ This release doesn't really affect end-users but greatly improves the developmen
|
||||
|
||||
- Add i18 support (currently English and German, `locale` config option)
|
||||
- Add custom checkbox focus style
|
||||
- Fix layer dropdown issues
|
||||
- Fix layer dropdown issues ([#1](https://github.com/owntracks/frontend/issues/1))
|
||||
- Fix checkbox style issues
|
||||
- Fix hover/focus inconsistencies
|
||||
- Fix Docker image labels
|
||||
@@ -69,21 +83,21 @@ This release doesn't really affect end-users but greatly improves the developmen
|
||||
- Custom checkbox styles
|
||||
- Switch from Font Awesome 4 to Feather Icons
|
||||
- Application now uses Vuex and Vue Router
|
||||
- Add URL query parameters to load and preserve application state: `lat`, `lng`, `zoom`, `start`, `end`, `user`, `device` and `layers`.
|
||||
- Add a loading indicator.
|
||||
- Add 'download data' modal, currently supporting formatted and minified JSON.
|
||||
- Add a verbose mode.
|
||||
- Add CORS proxy script toeasily use a production instance of the OwnTracks recorder in development.
|
||||
- Add unit tests for util and API functions.
|
||||
- Add URL query parameters to load and preserve application state: `lat`, `lng`, `zoom`, `start`, `end`, `user`, `device` and `layers`
|
||||
- Add a loading indicator
|
||||
- Add 'download data' modal, currently supporting formatted and minified JSON
|
||||
- Add a verbose mode
|
||||
- Add CORS proxy script to easily use a production instance of the OwnTracks recorder in development
|
||||
- Add unit tests for util and API functions
|
||||
- Add documentation for all public funtions
|
||||
- Add documentation for all configuration options.
|
||||
- Add more configuration options, including setting the API base URL ([#4](https://github.com/owntracks/frontend/issues/4)) and hiding the `ping/ping` location ([#12](https://github.com/owntracks/frontend/issues/12)).
|
||||
- Add documentation for all configuration options
|
||||
- Add more configuration options, including setting the API base URL ([#4](https://github.com/owntracks/frontend/issues/4)) and hiding the `ping/ping` location ([#12](https://github.com/owntracks/frontend/issues/12))
|
||||
|
||||
## 1.1.0 (2019-10-26)
|
||||
|
||||
- Add support for Docker. [#7](https://github.com/owntracks/frontend/pull/7), [@sharkoz](https://github.com/sharkoz)
|
||||
- Move project to the OwnTracks organisation on GitHub. [#8](https://github.com/owntracks/frontend/pull/8), [@jpmens](https://github.com/jpmens)
|
||||
- Enable compression in nginx configuration used in Docker image. [#11](https://github.com/owntracks/frontend/pull/11), [@sharkoz](https://github.com/sharkoz)
|
||||
- Add support for Docker ([#7](https://github.com/owntracks/frontend/pull/7), [@sharkoz](https://github.com/sharkoz))
|
||||
- Move project to the OwnTracks organisation on GitHub ([#8](https://github.com/owntracks/frontend/pull/8), [@jpmens](https://github.com/jpmens))
|
||||
- Enable compression in nginx configuration used in Docker image ([#11](https://github.com/owntracks/frontend/pull/11), [@sharkoz](https://github.com/sharkoz))
|
||||
|
||||
## 1.0.0 (2019-06-18)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ COPY . ./
|
||||
RUN yarn build
|
||||
|
||||
FROM nginx:1.17-alpine
|
||||
LABEL version="2.0.0-beta.7"
|
||||
LABEL version="2.0.0-beta.9"
|
||||
LABEL description="OwnTracks UI"
|
||||
LABEL maintainer="Linus Groh <mail@linusgroh.de>"
|
||||
ENV LISTEN_PORT=80 \
|
||||
|
||||
@@ -123,7 +123,9 @@ Remove the `ping/ping` location from the fetched data. This is useful when using
|
||||
|
||||
### `locale`
|
||||
|
||||
The language to use for the user interface. Available: `de` (German), `en` (English).
|
||||
The locale to use for the user interface, this affects the language and date/time
|
||||
formats. Available languages are `de` (German), `en` (English). You can use formats
|
||||
like `en-GB`, `en-US`, `de-DE`.
|
||||
|
||||
- Type: [`String`]
|
||||
- Default: `"en"`
|
||||
@@ -400,7 +402,7 @@ Primary color for the user interface (navigation bar and various map elements).
|
||||
### `selectedDevice`
|
||||
|
||||
Initial selected device. All devices will be shown by default if `null`. Will be ignored
|
||||
if [`selectedUser`](#selectedUser) is `null`;
|
||||
if [`selectedUser`](#selectedUser) is `null`.
|
||||
|
||||
Only data for the selected user/device will be fetched, so you can use this to limit the
|
||||
amount of data fetched after page load.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 452 KiB After Width: | Height: | Size: 653 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "owntracks-ui",
|
||||
"version": "2.0.0-beta.7",
|
||||
"version": "2.0.0-beta.9",
|
||||
"author": {
|
||||
"name": "Linus Groh",
|
||||
"email": "mail@linusgroh.de"
|
||||
|
||||
22
src/api.js
22
src/api.js
@@ -18,7 +18,7 @@ import { getApiUrl } from "@/util";
|
||||
*
|
||||
* @param {String} path API resource path
|
||||
* @param {QueryParams} [params] Query parameters
|
||||
* @return {Promise} Promise returned by the fetch function
|
||||
* @returns {Promise} Promise returned by the fetch function
|
||||
*/
|
||||
const fetchApi = (path, params = {}) => {
|
||||
const url = getApiUrl(path);
|
||||
@@ -30,7 +30,7 @@ const fetchApi = (path, params = {}) => {
|
||||
/**
|
||||
* Get the recorder's version.
|
||||
*
|
||||
* @return {String} Version
|
||||
* @returns {String} Version
|
||||
*/
|
||||
export const getVersion = async () => {
|
||||
const response = await fetchApi("/api/0/version");
|
||||
@@ -42,7 +42,7 @@ export const getVersion = async () => {
|
||||
/**
|
||||
* Get all users.
|
||||
*
|
||||
* @return {Array.<User>} Array of usernames
|
||||
* @returns {Array.<User>} Array of usernames
|
||||
*/
|
||||
export const getUsers = async () => {
|
||||
const response = await fetchApi("/api/0/list");
|
||||
@@ -55,7 +55,8 @@ export const getUsers = async () => {
|
||||
* Get all devices for the provided users.
|
||||
*
|
||||
* @param {Array.<User>} users Array of usernames
|
||||
* @return {Object.<User, Array.<Device>>} Object mapping each username to an array of device names
|
||||
* @returns {Object.<User, Array.<Device>>}
|
||||
* Object mapping each username to an array of device names
|
||||
*/
|
||||
export const getDevices = async users => {
|
||||
const devices = {};
|
||||
@@ -75,7 +76,7 @@ export const getDevices = async users => {
|
||||
*
|
||||
* @param {User} [user] Get last locations of all devices from this user
|
||||
* @param {Device} [device] Get last location of specific device
|
||||
* @return {Array.<LastLocation>} Array of last location objects
|
||||
* @returns {Array.<LastLocation>} Array of last location objects
|
||||
*/
|
||||
export const getLastLocations = async (user, device) => {
|
||||
const params = {};
|
||||
@@ -97,7 +98,7 @@ export const getLastLocations = async (user, device) => {
|
||||
* @param {Device} device Device name
|
||||
* @param {String} start Start date and time in UTC
|
||||
* @param {String} end End date and time in UTC
|
||||
* @return {LocationHistory} Array of location history objects
|
||||
* @returns {LocationHistory} Array of location history objects
|
||||
*/
|
||||
export const getUserDeviceLocationHistory = async (
|
||||
user,
|
||||
@@ -119,10 +120,12 @@ export const getUserDeviceLocationHistory = async (
|
||||
/**
|
||||
* Get the location history of multiple devices.
|
||||
*
|
||||
* @param {Object.<User, Array.<Device>>} devices Devices of which the history should be fetched
|
||||
* @param {Object.<User, Array.<Device>>} devices
|
||||
* Devices of which the history should be fetched
|
||||
* @param {String} start Start date and time in UTC
|
||||
* @param {String} end End date and time in UTC
|
||||
* @return {Object.<User, Object.<Device, LocationHistory>>} Array of location history objects
|
||||
* @returns {Object.<User, Object.<Device, LocationHistory>>}
|
||||
* Array of location history objects
|
||||
*/
|
||||
export const getLocationHistory = async (devices, start, end) => {
|
||||
const locationHistory = {};
|
||||
@@ -145,7 +148,8 @@ export const getLocationHistory = async (devices, start, end) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to the WebSocket API, reconnect when necessary and handle received messages.
|
||||
* Connect to the WebSocket API, reconnect when necessary and handle received
|
||||
* messages.
|
||||
*
|
||||
* @param {webSocketLocationCallback} [callback] Callback for location messages
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<LPopup>
|
||||
<div v-if="name" class="device">{{ name }}</div>
|
||||
<div v-else class="device">{{ user }}/{{ device }}</div>
|
||||
<div class="device">{{ deviceName }}</div>
|
||||
<div class="wrapper">
|
||||
<img v-if="face" :src="faceImageDataURI" alt="" />
|
||||
<img
|
||||
v-if="face"
|
||||
:src="faceImageDataURI"
|
||||
:alt="$t('Image of {deviceName}', { deviceName })"
|
||||
:title="$t('Image of {deviceName}', { deviceName })"
|
||||
/>
|
||||
<ul class="info-list">
|
||||
<li :title="$t('Timestamp')">
|
||||
<ClockIcon size="1x" aria-hidden="true" role="img" />
|
||||
@@ -122,13 +126,23 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Return the face image as a data URI string which can be used for an image's src attribute
|
||||
* Return the face image as a data URI string which can be used for an
|
||||
* image's src attribute.
|
||||
*
|
||||
* @return {String} base64-encoded face image data URI
|
||||
* @returns {String} base64-encoded face image data URI
|
||||
*/
|
||||
faceImageDataURI() {
|
||||
return `data:image/png;base64,${this.face}`;
|
||||
},
|
||||
/**
|
||||
* Return the device name for displaying with <user identifier>/<device
|
||||
* identifier> as fallback.
|
||||
*
|
||||
* @returns {String} device name for displaying
|
||||
*/
|
||||
deviceName() {
|
||||
return this.name ? this.name : `${this.user}/${this.device}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,7 @@ locales.keys().forEach(key => {
|
||||
});
|
||||
|
||||
export default new VueI18n({
|
||||
locale: config.locale,
|
||||
locale: config.locale.split("-")[0],
|
||||
fallbackLocale: "en",
|
||||
formatFallbackMessages: true,
|
||||
messages,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"OwnTracks documentation": "OwnTracks Dokumentation",
|
||||
"OwnTracks on Twitter": "OwnTracks auf Twitter",
|
||||
"Loading data, please wait...": "Daten werden geladen, bitte warten...",
|
||||
"Image of {deviceName}": "Bild von {deviceName}",
|
||||
"Timestamp": "Zeitstempel",
|
||||
"Location": "Standort",
|
||||
"Address": "Adresse",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"OwnTracks documentation": "OwnTracks documentation",
|
||||
"OwnTracks on Twitter": "OwnTracks on Twitter",
|
||||
"Loading data, please wait...": "Loading data, please wait...",
|
||||
"Image of {deviceName}": "Image of {deviceName}",
|
||||
"Timestamp": "Timestamp",
|
||||
"Location": "Location",
|
||||
"Address": "Address",
|
||||
|
||||
@@ -12,8 +12,9 @@ import { distanceBetweenCoordinates } from "@/util";
|
||||
* array of all coordinates.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {MultiLocationHistory} state.locationHistory Location history of selected users and devices
|
||||
* @return {Array.<L.LatLng>} All coordinates
|
||||
* @param {MultiLocationHistory} state.locationHistory
|
||||
* Location history of selected users and devices
|
||||
* @returns {Array.<L.LatLng>} All coordinates
|
||||
*/
|
||||
const locationHistoryLatLngs = state => {
|
||||
const latLngs = [];
|
||||
@@ -33,8 +34,9 @@ const locationHistoryLatLngs = state => {
|
||||
* coordinates does not exceed `config.map.maxPointDistance`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {MultiLocationHistory} state.locationHistory Location history of selected users and devices
|
||||
* @return {Array.<Array.<L.LatLng>>} Groups of coherent coordinates
|
||||
* @param {MultiLocationHistory} state.locationHistory
|
||||
* Location history of selected users and devices
|
||||
* @returns {Array.<Array.<L.LatLng>>} Groups of coherent coordinates
|
||||
*/
|
||||
const locationHistoryLatLngGroups = state => {
|
||||
const groups = [];
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint max-len: 0 */
|
||||
|
||||
/**
|
||||
* A coordinate with latitude and longitude.
|
||||
*
|
||||
|
||||
@@ -10,7 +10,7 @@ import { DATE_TIME_FORMAT, EARTH_RADIUS_IN_KM } from "@/constants";
|
||||
* base URL configuration into account.
|
||||
*
|
||||
* @param {String} path Path to the API resource
|
||||
* @return {URL} Final API URL
|
||||
* @returns {URL} Final API URL
|
||||
*/
|
||||
export const getApiUrl = path => {
|
||||
const normalizedBaseUrl = config.api.baseUrl.endsWith("/")
|
||||
@@ -24,7 +24,7 @@ export const getApiUrl = path => {
|
||||
* Check if the given string is an ISO 8601 YYYY-MM-DDTHH:MM:SS datetime.
|
||||
*
|
||||
* @param {String} s Input value to be tested
|
||||
* @return {Boolean} Whether the input matches the expected format
|
||||
* @returns {Boolean} Whether the input matches the expected format
|
||||
*/
|
||||
export const isIsoDateTime = s => moment(s, DATE_TIME_FORMAT, true).isValid();
|
||||
|
||||
@@ -32,7 +32,7 @@ export const isIsoDateTime = s => moment(s, DATE_TIME_FORMAT, true).isValid();
|
||||
* Convert degrees to radians.
|
||||
*
|
||||
* @param {Number} degrees Angle in degrees
|
||||
* @return {Number} Angle in radians
|
||||
* @returns {Number} Angle in radians
|
||||
*/
|
||||
export const degreesToRadians = degrees => (degrees * Math.PI) / 180;
|
||||
|
||||
@@ -44,7 +44,7 @@ export const degreesToRadians = degrees => (degrees * Math.PI) / 180;
|
||||
*
|
||||
* @param {Coordinate} c1 First coordinate
|
||||
* @param {Coordinate} c2 Second coordinate
|
||||
* @return {Number} Distance in meters
|
||||
* @returns {Number} Distance in meters
|
||||
*/
|
||||
export const distanceBetweenCoordinates = (c1, c2) => {
|
||||
const r = EARTH_RADIUS_IN_KM * 1000;
|
||||
|
||||
@@ -66,7 +66,11 @@
|
||||
<template v-for="(userDevices, user) in locationHistory">
|
||||
<template v-for="(deviceLocations, device) in userDevices">
|
||||
<LCircleMarker
|
||||
v-for="(l, n) in deviceLocations"
|
||||
v-for="(l, n) in deviceLocationsWithNameAndFace(
|
||||
user,
|
||||
device,
|
||||
deviceLocations
|
||||
)"
|
||||
:key="`${user}-${device}-${n}`"
|
||||
:lat-lng="[l.lat, l.lon]"
|
||||
v-bind="circleMarker"
|
||||
@@ -74,6 +78,8 @@
|
||||
<LDeviceLocationPopup
|
||||
:user="user"
|
||||
:device="device"
|
||||
:name="l.name"
|
||||
:face="l.face"
|
||||
:timestamp="l.tst"
|
||||
:lat="l.lat"
|
||||
:lon="l.lon"
|
||||
@@ -202,6 +208,29 @@ export default {
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Find a the last location object for a user/device combination from the
|
||||
* local cache and backfill name and face attributes to each item from the
|
||||
* passed array of location objects.
|
||||
*
|
||||
* @param {User} user Username
|
||||
* @param {Device} device Device name
|
||||
* @param {LocationHistory} deviceLocations Device name
|
||||
* @returns {LocationHistory} Updated location history
|
||||
*/
|
||||
deviceLocationsWithNameAndFace(user, device, deviceLocations) {
|
||||
const lastLocation = this.lastLocations.find(
|
||||
l => l.username === user && l.device === device
|
||||
);
|
||||
if (!lastLocation) {
|
||||
return deviceLocations;
|
||||
}
|
||||
return deviceLocations.map(l => ({
|
||||
...l,
|
||||
name: lastLocation.name,
|
||||
face: lastLocation.face,
|
||||
}));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user