Revert "Track dist"

This reverts commit 3299e3e77e352106842f631db8ca51e86a084468
This commit is contained in:
Joel Collins 2020-06-01 12:58:43 +00:00
parent b81cbcc038
commit e46334e7dd
48 changed files with 205 additions and 4765 deletions

View file

@ -4,9 +4,11 @@
class="uk-height-1-1 uk-margin-remove uk-padding-remove"
:class="handleTheme"
>
<div id="tour-header"></div>
<appContent />
<loadingContent v-if="!$store.getters.ready" />
<div v-if="$store.getters.ready" id="tour-header"></div>
<appContent v-if="$store.getters.ready" />
<v-tour
v-if="$store.getters.ready"
name="guidedTour"
:steps="tourSteps"
:callbacks="tourCallbacks"
@ -16,9 +18,11 @@
</template>
<script>
import isElectron from "./modules/isElectron";
// Import components
import appContent from "./components/appContent.vue";
import loadingContent from "./components/loadingContent.vue";
import axios from "axios";
// Key Codes
const keyCodes = {
@ -40,15 +44,16 @@ export default {
name: "App",
components: {
appContent
appContent,
loadingContent
},
data: function() {
return {
appAvailable: false,
keysDown: {},
systemDark: undefined,
themeObserver: undefined,
isElectron: isElectron(),
tourCallbacks: {
onStop: () => {
this.setLocalStorageObj("completedTour", true);
@ -94,33 +99,6 @@ export default {
placement: "bottom"
}
},
{
target: "#new-connection-card",
header: {
title: "New connection"
},
content: `Connect locally if you're running on a microscope, \nor open a new remote connection to a microscope`
},
...(this.isElectron
? [
{
target: "#nearby-connections-grid",
header: {
title: "Nearby microscopes"
},
content: `Connect to microscopes found on your network`
}
]
: []),
{
target: "#saved-connections-grid",
header: {
title: "Saved microscopes"
},
content: `Connect to your saved microscopes for faster access`
},
{
target: "#gallery-tab-icon",
header: {
@ -175,8 +153,11 @@ export default {
this.systemDark = false;
}
});
// Check connection to API
this.checkConnection();
// Handle guided tour
// If the user has already completed or skipped the guided tour
// TODO: Only run this if connected to the API
var completedTour = this.getLocalStorageObj("completedTour") || false;
if (!completedTour) {
this.$tours["guidedTour"].start();
@ -189,6 +170,16 @@ export default {
window.addEventListener("keydown", this.keyDownMonitor);
window.addEventListener("keyup", this.keyUpMonitor);
window.addEventListener("wheel", this.wheelMonitor);
// Watch for origin changes
this.unwatchOriginFunction = this.$store.watch(
(state, getters) => {
return getters.uriV2;
},
uriV2 => {
this.checkConnection();
console.log(uriV2);
}
);
},
beforeDestroy: function() {
@ -200,9 +191,28 @@ export default {
window.removeEventListener("keydown", this.keyDownMonitor);
window.removeEventListener("keyup", this.keyUpMonitor);
window.removeEventListener("wheel", this.wheelMonitor);
// Remove origin watcher
this.unwatchOriginFunction();
},
methods: {
checkConnection: function() {
var uriV2 = this.$store.getters.uriV2;
this.$store.commit("changeWaiting", true);
axios
.get(uriV2)
.then(() => {
this.$store.commit("setConnected");
this.$store.commit("setErrorMessage", null);
})
.catch(error => {
this.$store.commit("setErrorMessage", error);
})
.finally(() => {
this.$store.commit("changeWaiting", false);
});
},
handleExit: function() {
console.log("Triggered beforeunload");
this.$root.$emit("globalTogglePreview", false);

View file

@ -16,14 +16,15 @@
class="uk-flex uk-flex-column uk-padding-remove uk-width-auto uk-height-1-1 uk-text-center"
>
<tabIcon
id="connect-tab-icon"
tab-i-d="connect"
:require-connection="false"
id="view-tab-icon"
tab-i-d="view"
:require-connection="true"
:current-tab="currentTab"
@set-tab="setTab"
>
<i class="material-icons">bug_report</i>
<i class="material-icons">visibility</i>
</tabIcon>
<tabIcon
id="gallery-tab-icon"
tab-i-d="gallery"
@ -36,15 +37,6 @@
<hr />
<tabIcon
id="view-tab-icon"
tab-i-d="view"
:require-connection="true"
:current-tab="currentTab"
@set-tab="setTab"
>
<i class="material-icons">visibility</i>
</tabIcon>
<tabIcon
id="navigate-tab-icon"
tab-i-d="navigate"
@ -95,11 +87,11 @@
class="uk-padding-remove uk-height-1-1 uk-width-expand"
>
<tabContent
tab-i-d="connect"
:require-connection="false"
tab-i-d="view"
:require-connection="true"
:current-tab="currentTab"
>
<connectContent />
<viewContent />
</tabContent>
<tabContent
tab-i-d="gallery"
@ -108,13 +100,6 @@
>
<galleryContent />
</tabContent>
<tabContent
tab-i-d="view"
:require-connection="true"
:current-tab="currentTab"
>
<viewContent />
</tabContent>
<tabContent
tab-i-d="navigate"
:require-connection="true"
@ -164,7 +149,6 @@ import tabIcon from "./genericComponents/tabIcon";
import tabContent from "./genericComponents/tabContent";
// Import new content components
import connectContent from "./tabContentComponents/connectContent.vue";
import navigateContent from "./tabContentComponents/navigateContent.vue";
import captureContent from "./tabContentComponents/captureContent.vue";
import viewContent from "./tabContentComponents/viewContent.vue";
@ -182,7 +166,6 @@ export default {
components: {
tabIcon,
tabContent,
connectContent,
navigateContent,
captureContent,
viewContent,
@ -195,7 +178,7 @@ export default {
data: function() {
return {
plugins: [],
currentTab: "connect",
currentTab: "view",
unwatchStoreFunction: null
};
},

View file

@ -0,0 +1,33 @@
<template>
<div class="uk-flex uk-flex-column uk-flex-center uk-height-1-1">
<div
v-if="$store.state.waiting"
class="uk-align-center"
uk-spinner="ratio: 3"
></div>
<div v-if="$store.state.waiting" class="uk-align-center">Loading...</div>
<i class="material-icons uk-align-center error-icon">error_outline</i>
<div v-if="$store.state.error" class="uk-align-center">
{{ $store.state.error }}
</div>
</div>
</template>
<script>
// Export main app
export default {
name: "LoadingContent",
components: {},
data: function() {
return {};
}
};
</script>
<style scoped lang="less">
.error-icon {
font-size: 120px;
}
</style>

View file

@ -1,24 +0,0 @@
<template>
<!-- Grid managing tab content -->
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove">
<div class="view-component uk-width-expand">
<connectDisplay />
</div>
</div>
</template>
<script>
import connectDisplay from "../viewComponents/connectDisplay.vue";
export default {
name: "ConnectContent",
components: {
connectDisplay
},
data: function() {
return {};
}
};
</script>

View file

@ -1,410 +0,0 @@
<template>
<div class="connectDisplay uk-padding uk-padding-remove-left">
<div
uk-grid
class="uk-height-1-1 uk-margin-remove uk-padding-remove uk-flex-column"
margin="0"
>
<div class="uk-width-auto">
<div
id="new-connection-card"
class="uk-card uk-card-default uk-padding-remove uk-width-medium connect-card-align-top"
>
<div class="uk-card-body uk-padding-small">
<form id="formConnectToHost" @submit.prevent="handleSubmit">
<div class="uk-form-controls uk-margin">
<label
><input
v-model="computedLocalMode"
class="uk-radio"
type="radio"
name="radio_local"
:value="true"
/>
Connect locally</label
><br />
<label
><input
v-model="computedLocalMode"
class="uk-radio"
type="radio"
name="radio_local"
:value="false"
checked
/>
Connect remotely</label
>
</div>
<div v-if="!localMode">
<div class="uk-inline uk-width-1-1">
<span class="uk-form-icon"
><i class="material-icons">dns</i></span
>
<input
v-model="hostname"
:class="IpFormClasses"
class="uk-input uk-form-small"
type="text"
placeholder="Hostname or IP address"
/>
</div>
<label class="uk-form-label" for="form-stacked-text"
>Port</label
>
<div class="uk-form-controls">
<input
id="form-stacked-text"
v-model="computedPort"
class="uk-input uk-form-small"
type="number"
value="5000"
/>
</div>
</div>
</form>
</div>
<div class="uk-card-footer uk-padding-small">
<button
type="submit"
form="formConnectToHost"
class="uk-button uk-button-primary uk-form-small uk-float-right uk-margin uk-margin-remove-top uk-width-1-1"
@click="handleSubmit"
>
Connect
</button>
<button
v-if="$store.getters.ready"
class="uk-button uk-button-default uk-form-small uk-margin-small uk-width-1-1"
@click="saveHost()"
>
Save Current
</button>
<button
v-if="$store.getters.ready"
class="uk-button uk-button-danger uk-form-small uk-float-right uk-margin uk-margin-remove-top uk-width-1-1"
@click="$store.commit('resetState')"
>
Disconnect
</button>
</div>
</div>
</div>
<div class="uk-width-expand">
<ul uk-accordion="multiple: true; animation: false">
<li v-show="isElectron" id="nearby-connections-grid" class="uk-open">
<a class="uk-accordion-title" href="#">Nearby devices</a>
<div class="uk-accordion-content">
<div class="uk-grid-medium uk-grid-match uk-margin-top" uk-grid>
<div v-for="host in foundHostArray" :key="host.name">
<hostCard
:name="host.name"
:hostname="host.hostname"
:port="host.port"
:deletable="false"
@connect="handleConnectButton(host)"
></hostCard>
</div>
</div>
</div>
</li>
<li id="saved-connections-grid" class="uk-open">
<a class="uk-accordion-title" href="#">Saved devices</a>
<div class="uk-accordion-content">
<div class="uk-grid-medium uk-grid-match uk-margin-top" uk-grid>
<div v-for="host in savedHosts" :key="host.name">
<hostCard
:name="host.name"
:hostname="host.hostname"
:port="host.port"
@connect="handleConnectButton(host)"
@delete="delSavedHost(host)"
></hostCard>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
// Import mDNS package if running in Electron
import isElectron from "../../modules/isElectron";
if (isElectron()) {
var mdns = require("mdns-js");
}
import axios from "axios";
import hostCard from "./connectComponents/hostCard.vue";
// Export main app
export default {
name: "ConnectDisplay",
components: {
hostCard
},
data: function() {
return {
isElectron: isElectron(),
localMode: true,
hostname: "",
port: 5000,
selectedHost: "",
savedHosts: [],
foundHosts: {}
};
},
computed: {
foundHostArray: function() {
return Object.values(this.foundHosts);
},
// Stylises the hostname input box based on connection status
IpFormClasses: function() {
return {
"uk-form-danger": !this.$store.state.available,
"uk-form-success": this.$store.getters.ready
};
},
computedLocalMode: {
get: function() {
return this.localMode;
},
set: function(state) {
this.localMode = state;
this.setLocalMode(state);
}
},
computedPort: {
get: function() {
if (this.hostname.includes(":")) {
var port = parseInt(this.hostname.split(":")[1]);
return !isNaN(port) ? port : this.port;
} else {
return this.port;
}
},
set: function(newPort) {
this.port = newPort;
if (this.hostname.includes(":")) {
this.hostname = this.hostname.split(":")[0] + ":" + this.port;
}
}
}
},
// When savedHosts changes, serialise to JSON and save to localStorage
watch: {
savedHosts() {
this.setLocalStorageObj("savedHosts", this.savedHosts);
}
},
mounted() {
// Propagate localMode settings
this.setLocalMode(this.localMode);
// Try loading savedHosts from localStorage. If null, don't change.
this.savedHosts = this.getLocalStorageObj("savedHosts") || this.savedHosts;
// Handle mDNS browsing
if (mdns) {
// TODO: Have this run periodically on a timer
// Create a browser to search for 'openflexure' type services
var browser = mdns.createBrowser(mdns.tcp("openflexure"));
// Start discovering services when ready
browser.on("ready", function() {
browser.discover();
});
browser.on("update", data => {
console.log(data);
// We will be checking if it's a valid openflexure device
var validDevice = false;
// Go through each type of the host, make valid if 'openflexure' appears
data.type.forEach(element => {
console.log(element);
if (element.name === "openflexure") {
validDevice = true;
}
});
var hostData = {
hostname: data.addresses[0],
name: `${data.host} on ${data.networkInterface}`,
port: data.port
};
if (typeof data.port !== "undefined" && validDevice === true) {
this.$set(this.foundHosts, hostData.hostname, hostData);
}
console.log(this.foundHosts);
});
}
},
methods: {
connectToHost: function(host) {
console.log(host);
// Set the global form values
this.hostname = host.hostname;
this.port = host.port;
// Commit the hostname and port to store
this.$store.commit("resetState");
this.$store.commit("changeHost", [host.hostname, host.port]);
this.$store.commit("changeWaiting", true);
// Try to get list of available routes
axios
.get(`${this.$store.getters.baseUri}/routes`)
.then(response => {
console.log(response.data);
// If the /api/v2 route is available
if (Object.keys(response.data).includes("/api/v2/")) {
console.log("API v2 available");
// Tell Vuex store that we're connected
this.$store.commit("setConnected");
// Check client and server match
this.checkServerVersion();
} else {
// Error and no-connect if API v2 is missing
var ApiVersionError = `Your microscope is running an old API version.\
You must update your microscope software to continue.\
<br><br>
For API upgrades, burning the latest Raspbian-OpenFlexure to your SD card is recommended.\
<br><br>
<b>Downloads and instructions can be found <a target="_blank" href="https://openflexure.org/projects/microscope/install">here</a></b>`;
this.modalDialog("API upgrade required", ApiVersionError);
this.$store.commit("setError");
}
})
.catch(error => {
this.$store.commit("setError");
this.modalError(error); // Let mixin handle error
});
},
handleSubmit: function() {
// Split host and port if needed
if (this.hostname.includes(":")) {
this.port = this.computedPort;
this.hostname = this.hostname.split(":")[0];
}
// Handle auto-local-connect
var hostname;
if (this.localMode) {
hostname = "localhost";
} else {
hostname = this.hostname;
}
var selectedHost = {
hostname: hostname,
port: this.port
};
this.connectToHost(selectedHost);
},
handleConnectButton: function(host) {
this.computedLocalMode = false;
this.connectToHost(host);
},
checkServerVersion: function() {
var versionUri = `${this.$store.getters.baseUri}/api/v2/instrument/configuration/application/version`;
axios
.get(versionUri)
.then(response => {
var serverVersion = response.data;
var clientVersion = process.env.PACKAGE.version;
var clientVersionMajor = clientVersion.substring(0, 3);
console.log(clientVersionMajor);
if (serverVersion != undefined) {
var serverVersionMajor = serverVersion.substring(0, 3);
}
console.log(serverVersionMajor);
if (
serverVersion == undefined ||
serverVersionMajor != clientVersionMajor
) {
var versionWarning = `Client and microscope versions do not match.\
Please update your microscope software.\
<b>Client version:</b> ${clientVersion}<br> \
<b>Server version:</b> ${serverVersion}<br><br>`;
if (serverVersion < 1.1) {
versionWarning =
versionWarning +
"You will need to install a newer server version on a clean SD card.";
} else {
versionWarning =
versionWarning +
"Run <b><tt>ofm update</tt></b> and then <b><tt>ofm upgrade</tt></b> on your microscope.";
}
this.modalDialog("Version mismatch", versionWarning);
}
})
.catch(error => {
this.modalError(error); // Let mixin handle error
});
},
setLocalMode: function(state) {
this.$store.commit("changeSetting", ["trackWindow", state]);
this.$store.commit("changeSetting", ["disableStream", state]);
this.$store.commit("changeSetting", ["autoGpuPreview", state]);
},
delSavedHost: function(host) {
console.log(host);
var index = this.savedHosts.indexOf(host);
if (index > -1) {
this.savedHosts.splice(index, 1);
}
},
saveHost: function() {
// URI to get hostname directly from settings
var hostnameUri = `${this.$store.getters.baseUri}/api/v2/instrument/settings/name`;
axios
.get(hostnameUri)
.then(response => {
// Get device name from response
var deviceName = response.data;
// We use unshift instead of push to add the entry to the beginning of the array
this.savedHosts.unshift({
name: deviceName,
hostname: this.$store.state.host,
port: this.$store.state.port
});
})
.catch(error => {
this.modalError(error); // Let mixin handle error
});
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less"></style>

View file

@ -1,15 +0,0 @@
// Load settings store from main process
const { store } = require("electron").remote.require("./store");
// Load extra modules we'll be using
const { Titlebar, Color } = require("custom-electron-titlebar");
// Only show custom menubar if current window has no frame
if (store.get("drawCustomTitleBar") == true) {
new Titlebar({
backgroundColor: Color.fromHex("#c5247f"),
icon: "./titleicon.svg"
});
}
console.log("Loaded main.app.js for electron-renderer functionality");

View file

@ -76,7 +76,15 @@ Vue.mixin({
},
modalError: function(error) {
console.log(error);
var errormsg = this.getErrorMessage(error);
this.$store.commit("setErrorMessage", errormsg);
UIkit.notification({
message: `${errormsg}`,
status: "danger"
});
},
getErrorMessage: function(error) {
var errormsg = "";
// If a response was obtained
@ -100,11 +108,7 @@ Vue.mixin({
errormsg = `${error.message}`;
console.log(errormsg);
}
this.$store.commit("setError", errormsg);
UIkit.notification({
message: `${errormsg}`,
status: "danger"
});
return errormsg;
},
showModalElement: function(element) {

View file

@ -1,32 +0,0 @@
function isElectron() {
// Renderer process
if (
typeof window !== "undefined" &&
typeof window.process === "object" &&
window.process.type === "renderer"
) {
return true;
}
// Main process
if (
typeof process !== "undefined" &&
typeof process.versions === "object" &&
!!process.versions.electron
) {
return true;
}
// Detect the user agent when the `nodeIntegration` option is set to true
if (
typeof navigator === "object" &&
typeof navigator.userAgent === "string" &&
navigator.userAgent.indexOf("Electron") >= 0
) {
return true;
}
return false;
}
export default isElectron;

View file

@ -5,8 +5,7 @@ Vue.use(Vuex);
export default new Vuex.Store({
state: {
host: "",
port: 5000,
origin: window.location.origin,
available: false,
waiting: false,
error: "",
@ -20,9 +19,8 @@ export default new Vuex.Store({
},
mutations: {
changeHost(state, [host, port]) {
state.host = host;
state.port = port;
changeOrigin(state, origin) {
state.origin = origin;
},
changeWaiting(state, waiting) {
state.waiting = waiting;
@ -39,8 +37,7 @@ export default new Vuex.Store({
state.waiting = false;
state.available = true;
},
setError(state, msg) {
state.waiting = false;
setErrorMessage(state, msg) {
state.error = msg;
},
addStream(state, id) {
@ -54,8 +51,8 @@ export default new Vuex.Store({
actions: {},
getters: {
uriV2: state => `http://${state.host}:${state.port}/api/v2`,
baseUri: state => `http://${state.host}:${state.port}`,
uriV2: state => `${state.origin}/api/v2`,
baseUri: state => state.origin,
ready: state => state.available
}
});