Merge branch 'frame' into 'master'
Only build locally-served interface See merge request openflexure/openflexure-microscope-jsclient!54
|
|
@ -1,3 +0,0 @@
|
||||||
NODE_ENV=production
|
|
||||||
VUE_APP_PLATFORM=web
|
|
||||||
VUE_APP_TARGET=electron-renderer
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
stages:
|
|
||||||
- build
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
|
|
||||||
# Cache modules in between jobs
|
|
||||||
cache:
|
|
||||||
key: ${CI_COMMIT_REF_SLUG}
|
|
||||||
paths:
|
|
||||||
- node_modules/
|
|
||||||
|
|
||||||
|
|
||||||
# Electron app build
|
|
||||||
build:
|
|
||||||
stage: build
|
|
||||||
image: electronuserland/builder:wine
|
|
||||||
|
|
||||||
script:
|
|
||||||
# Install node dependencies
|
|
||||||
- npm install
|
|
||||||
# Create build directory
|
|
||||||
- mkdir -p "release-builds"
|
|
||||||
# Build electron app
|
|
||||||
- npm run build:app
|
|
||||||
- npm run release
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
name: "dist"
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- "./release-builds/*.AppImage"
|
|
||||||
- "./release-builds/*.exe"
|
|
||||||
- "./release-builds/*.exe.blockmap"
|
|
||||||
- "./release-builds/latest*.yml"
|
|
||||||
- "./release-builds/beta*.yml"
|
|
||||||
|
|
||||||
only:
|
|
||||||
- merge_requests
|
|
||||||
- master
|
|
||||||
- web
|
|
||||||
- tags
|
|
||||||
|
|
||||||
|
|
||||||
# Deploy to builds.openflexure.org
|
|
||||||
deploy:
|
|
||||||
stage: deploy
|
|
||||||
dependencies:
|
|
||||||
- build
|
|
||||||
image: ubuntu:latest
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
|
|
||||||
- eval $(ssh-agent -s)
|
|
||||||
- ssh-add <(echo "$SSH_PRIVATE_KEY_BATH_OPENFLEXURE_BASE64" | base64 --decode)
|
|
||||||
- mkdir -p ~/.ssh
|
|
||||||
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
|
|
||||||
|
|
||||||
script:
|
|
||||||
# Install rsync if not already installed
|
|
||||||
- 'which rsync || ( apt-get update -y && apt-get install rsync -y )'
|
|
||||||
|
|
||||||
# Upload the builds folder to openflexure-microscope builds
|
|
||||||
- rsync -hrvz -e ssh release-builds/ ci-user@openflexure.bath.ac.uk:/var/www/build/openflexure-ev/
|
|
||||||
|
|
||||||
# Run update-latest.py on the build server
|
|
||||||
- ssh -t ci-user@openflexure.bath.ac.uk "/var/www/build/update-latest.py"
|
|
||||||
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
const electron = require("electron");
|
|
||||||
const contextMenu = require("electron-context-menu");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
const app = electron.app;
|
|
||||||
const BrowserWindow = electron.BrowserWindow;
|
|
||||||
|
|
||||||
let url = "http://localhost:8080/";
|
|
||||||
|
|
||||||
// Set the application menu
|
|
||||||
require("./menu.js");
|
|
||||||
|
|
||||||
app.on("ready", () => {
|
|
||||||
let window = new BrowserWindow({
|
|
||||||
width: 1124,
|
|
||||||
height: 800,
|
|
||||||
icon: path.join(__dirname, "/icons/png/64x64.png")
|
|
||||||
});
|
|
||||||
|
|
||||||
contextMenu({
|
|
||||||
showCopyImageAddress: true,
|
|
||||||
showSaveImageAs: true,
|
|
||||||
showInspectElement: true
|
|
||||||
});
|
|
||||||
|
|
||||||
window.loadURL(url);
|
|
||||||
});
|
|
||||||
197
app/app.js
|
|
@ -1,197 +0,0 @@
|
||||||
// Required packages
|
|
||||||
const electron = require("electron");
|
|
||||||
const { dialog } = require("electron");
|
|
||||||
const updater = require("electron-updater");
|
|
||||||
const autoUpdater = updater.autoUpdater;
|
|
||||||
const contextMenu = require("electron-context-menu");
|
|
||||||
const ProgressBar = require("electron-progressbar");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
// Attach settings store
|
|
||||||
const { store } = require("./store");
|
|
||||||
|
|
||||||
// Auto upadater
|
|
||||||
autoUpdater.autoDownload = false;
|
|
||||||
|
|
||||||
autoUpdater.on("checking-for-update", function() {});
|
|
||||||
|
|
||||||
autoUpdater.on("update-available", function(info) {
|
|
||||||
sendStatusToWindow("Update available." + info);
|
|
||||||
dialog.showMessageBox(
|
|
||||||
mainWindow,
|
|
||||||
{
|
|
||||||
type: "info",
|
|
||||||
title: "Update available",
|
|
||||||
message: "A new version of OpenFlexure eV is available.",
|
|
||||||
buttons: ["Download", "Later"]
|
|
||||||
},
|
|
||||||
buttonIndex => {
|
|
||||||
if (buttonIndex === 0) {
|
|
||||||
console.log("Downloading update selected");
|
|
||||||
handleDownloadUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Download, with a progress bar
|
|
||||||
function handleDownloadUpdate() {
|
|
||||||
console.log("Downloading update");
|
|
||||||
var progressBar = new ProgressBar({
|
|
||||||
indeterminate: false,
|
|
||||||
text: "Downloading update...",
|
|
||||||
detail: "Please wait...",
|
|
||||||
browserWindow: {
|
|
||||||
webPreferences: {
|
|
||||||
nodeIntegration: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
progressBar.on("ready", function() {
|
|
||||||
autoUpdater.on("download-progress", function(info) {
|
|
||||||
progressBar.value = info.percent;
|
|
||||||
progressBar.detail = `${Math.floor(info.percent)}% ${info.bytesPerSecond /
|
|
||||||
1000}kb/s`;
|
|
||||||
});
|
|
||||||
autoUpdater.on("update-downloaded", function() {
|
|
||||||
progressBar.close();
|
|
||||||
});
|
|
||||||
autoUpdater.on("error", function() {
|
|
||||||
progressBar.close();
|
|
||||||
});
|
|
||||||
autoUpdater.downloadUpdate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger update installation
|
|
||||||
autoUpdater.on("update-downloaded", function(info) {
|
|
||||||
sendStatusToWindow("Update downloaded." + info);
|
|
||||||
dialog.showMessageBox(
|
|
||||||
mainWindow,
|
|
||||||
{
|
|
||||||
type: "info",
|
|
||||||
title: "Update downloaded",
|
|
||||||
message: "Please restart the application to apply the update.",
|
|
||||||
buttons: ["Update", "Later"]
|
|
||||||
},
|
|
||||||
buttonIndex => {
|
|
||||||
if (buttonIndex === 0) {
|
|
||||||
autoUpdater.quitAndInstall();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("update-not-available", function() {
|
|
||||||
sendStatusToWindow("Update not available.");
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("error", function() {
|
|
||||||
sendStatusToWindow("Error in auto-updater.");
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("download-progress", function(progressObj) {
|
|
||||||
let log_message = "Download speed: " + progressObj.bytesPerSecond;
|
|
||||||
log_message =
|
|
||||||
log_message + " - Downloaded " + parseInt(progressObj.percent) + "%";
|
|
||||||
log_message =
|
|
||||||
log_message +
|
|
||||||
" (" +
|
|
||||||
progressObj.transferred +
|
|
||||||
"/" +
|
|
||||||
progressObj.total +
|
|
||||||
")";
|
|
||||||
sendStatusToWindow(log_message);
|
|
||||||
});
|
|
||||||
|
|
||||||
function sendStatusToWindow(message) {
|
|
||||||
console.log(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the app
|
|
||||||
const app = electron.app;
|
|
||||||
const BrowserWindow = electron.BrowserWindow;
|
|
||||||
|
|
||||||
// Set the window content
|
|
||||||
let url = `file://${path.join(__dirname, "/dist/index.html")}`;
|
|
||||||
let mainWindow;
|
|
||||||
|
|
||||||
// Set the application menu
|
|
||||||
require("./menu.js");
|
|
||||||
|
|
||||||
// Handle redrawing the mainWindow
|
|
||||||
let recreatingWindowInProgress = false;
|
|
||||||
|
|
||||||
function toggleCustomTitleBar() {
|
|
||||||
// Mark window as being recreated, to prevent stopping the application
|
|
||||||
recreatingWindowInProgress = true;
|
|
||||||
|
|
||||||
// Invert the drawCustomTitleBar setting
|
|
||||||
store.set("drawCustomTitleBar", !store.get("drawCustomTitleBar"));
|
|
||||||
|
|
||||||
// Destroy old window
|
|
||||||
mainWindow.destroy();
|
|
||||||
// Create new window
|
|
||||||
createWindow();
|
|
||||||
|
|
||||||
// Mark window as no longer being recreated
|
|
||||||
recreatingWindowInProgress = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWindow() {
|
|
||||||
// Create the browser window.
|
|
||||||
mainWindow = new BrowserWindow({
|
|
||||||
frame: !store.get("drawCustomTitleBar"),
|
|
||||||
width: 1200,
|
|
||||||
height: 900,
|
|
||||||
icon: path.join(__dirname, "/icons/png/64x64.png"),
|
|
||||||
webPreferences: {
|
|
||||||
nodeIntegration: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make a context menu
|
|
||||||
contextMenu({
|
|
||||||
showCopyImageAddress: true,
|
|
||||||
showSaveImageAs: true,
|
|
||||||
showInspectElement: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load window contents
|
|
||||||
mainWindow.loadURL(url);
|
|
||||||
|
|
||||||
// Check for updates
|
|
||||||
autoUpdater.checkForUpdates();
|
|
||||||
|
|
||||||
// Emitted when the window is closed.
|
|
||||||
mainWindow.on("closed", function() {
|
|
||||||
mainWindow = null; // Dereference the window object
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.webContents.on("new-window", function(event, url) {
|
|
||||||
event.preventDefault();
|
|
||||||
electron.shell.openExternal(url);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some APIs can only be used after this event occurs.
|
|
||||||
app.on("ready", () => {
|
|
||||||
createWindow();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Quit when all windows are closed.
|
|
||||||
app.on("window-all-closed", function() {
|
|
||||||
if (process.platform !== "darwin" && recreatingWindowInProgress != true) {
|
|
||||||
app.quit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.on("activate", function() {
|
|
||||||
if (mainWindow === null) {
|
|
||||||
createWindow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Export toggleCustomTitleBar for use in ./menu.js
|
|
||||||
module.exports.toggleCustomTitleBar = toggleCustomTitleBar;
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
extends: app/builder-config-base.yaml
|
|
||||||
win:
|
|
||||||
target:
|
|
||||||
- appx
|
|
||||||
icon: app/icons/win/icon.ico
|
|
||||||
appx:
|
|
||||||
applicationId: OpenFlexureEV
|
|
||||||
identityName: 60425J.T.Collins.OpenFlexureeV
|
|
||||||
publisher: CN=CC867BEF-0715-4D48-8035-8881993DBB68
|
|
||||||
publisherDisplayName: J. T. Collins
|
|
||||||
backgroundColor: '#C5247F'
|
|
||||||
electronVersion: 4.1.5
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
directories:
|
|
||||||
output: release-builds
|
|
||||||
buildResources: app/resources
|
|
||||||
appId: org.openflexure.ev
|
|
||||||
artifactName: ${name}-${version}-${os}-${arch}.${ext}
|
|
||||||
asar: true
|
|
||||||
publish:
|
|
||||||
- provider: generic
|
|
||||||
url: https://build.openflexure.org/openflexure-ev/
|
|
||||||
files:
|
|
||||||
- filter:
|
|
||||||
- '**/*'
|
|
||||||
- '!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}'
|
|
||||||
- '!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}'
|
|
||||||
- '!**/node_modules/*.d.ts'
|
|
||||||
- '!**/node_modules/.bin'
|
|
||||||
- '!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}'
|
|
||||||
- '!.editorconfig'
|
|
||||||
- '!**/._*'
|
|
||||||
- '!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}'
|
|
||||||
- '!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}'
|
|
||||||
- '!**/{appveyor.yml,.travis.yml,circle.yml}'
|
|
||||||
- '!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}'
|
|
||||||
- '!**/.env.*'
|
|
||||||
- '!**/{.idea,.vscode}${/*}'
|
|
||||||
- '!**/app/installers${/*}'
|
|
||||||
- '!**/hooks${/*}'
|
|
||||||
- '!**/platforms${/*}'
|
|
||||||
- '!**/release-builds${/*}'
|
|
||||||
- '!src${/*}'
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
extends: app/builder-config-base.yaml
|
|
||||||
linux:
|
|
||||||
target:
|
|
||||||
- target: deb
|
|
||||||
arch:
|
|
||||||
- armv7l
|
|
||||||
- target: appImage
|
|
||||||
arch:
|
|
||||||
- armv7l
|
|
||||||
icon: app/icons/png/
|
|
||||||
category: Science
|
|
||||||
deb:
|
|
||||||
fpm: ['--architecture', 'armhf']
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
extends: app/builder-config-base.yaml
|
|
||||||
win:
|
|
||||||
target:
|
|
||||||
- NSIS
|
|
||||||
icon: app/icons/win/icon.ico
|
|
||||||
linux:
|
|
||||||
target:
|
|
||||||
- target: deb
|
|
||||||
arch:
|
|
||||||
- x64
|
|
||||||
- target: appImage
|
|
||||||
arch:
|
|
||||||
- x64
|
|
||||||
icon: app/icons/png/
|
|
||||||
category: Science
|
|
||||||
mac:
|
|
||||||
icon: app/icons/mac/icon.icns
|
|
||||||
category: public.app-category.utilities
|
|
||||||
dmg:
|
|
||||||
contents:
|
|
||||||
- x: 110
|
|
||||||
y: 150
|
|
||||||
- x: 240
|
|
||||||
y: 150
|
|
||||||
type: link
|
|
||||||
path: /Applications
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
provider: generic
|
|
||||||
url: https://build.openflexure.org/openflexure-ev/
|
|
||||||
channel: latest
|
|
||||||
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 885 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 733 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 353 KiB |
|
|
@ -1,103 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
packageName='openflexure-ev'
|
|
||||||
|
|
||||||
scriptPath=$(dirname "$0")
|
|
||||||
packagePath="$(dirname "$scriptPath")"
|
|
||||||
packageJson="$packagePath/package.json"
|
|
||||||
|
|
||||||
installerPath="$packagePath/openflexure-ev-win.exe"
|
|
||||||
installerHash=`sha256sum ${installerPath} | awk '{ print $1 }'`
|
|
||||||
echo "$installerHash"
|
|
||||||
|
|
||||||
outpath="$packagePath/release-builds/choco"
|
|
||||||
toolspath="$outpath/tools"
|
|
||||||
mkdir -p "$toolspath"
|
|
||||||
|
|
||||||
echo "$scriptPath"
|
|
||||||
echo "$packageJson"
|
|
||||||
echo "$outpath"
|
|
||||||
|
|
||||||
# Get package version from package.json
|
|
||||||
packageVersion=$(grep version < "$packageJson" | head -1 \
|
|
||||||
| awk -F: '{ print $2 }' \
|
|
||||||
| sed 's/[",]//g' \
|
|
||||||
| tr -d ':space:')
|
|
||||||
|
|
||||||
echo "Package version: $packageVersion"
|
|
||||||
|
|
||||||
# Convert into a nupkg-safe version string
|
|
||||||
majVer="$(echo "$packageVersion" | cut -d'-' -f1)"
|
|
||||||
minVer="$(echo "$packageVersion" | cut -d'-' -f2)"
|
|
||||||
|
|
||||||
if [ "$majVer" = "$minVer" ]; then
|
|
||||||
version="$majVer"
|
|
||||||
else
|
|
||||||
minVer="${minVer//.}"
|
|
||||||
version="$majVer-$minVer"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Nupkg version: $version"
|
|
||||||
|
|
||||||
# Build installer URL
|
|
||||||
pipelineURL="$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_REF_NAME/raw"
|
|
||||||
instURL="$pipelineURL/openflexure-ev-win.exe?job=package"
|
|
||||||
nuspecURL="$CI_JOB_URL/artifacts/browse"
|
|
||||||
|
|
||||||
echo "$instURL"
|
|
||||||
|
|
||||||
# Build nuspec
|
|
||||||
cat > "$outpath/openflexure-ev.nuspec" <<- EOL
|
|
||||||
<?xml version="1.0"?>
|
|
||||||
<package xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
|
||||||
<metadata>
|
|
||||||
<id>$packageName</id>
|
|
||||||
<title>OpenFlexure eV</title>
|
|
||||||
<version>$version</version>
|
|
||||||
<authors>OpenFlexure</authors>
|
|
||||||
<owners>Joel Collins</owners>
|
|
||||||
<summary>OpenFlexure Microscope client</summary>
|
|
||||||
<description>An electron-based user client for the OpenFlexure Microscope Server</description>
|
|
||||||
|
|
||||||
<projectUrl>https://www.openflexure.org/</projectUrl>
|
|
||||||
<docsUrl>https://www.openflexure.org/projects/microscope/</docsUrl>
|
|
||||||
<bugTrackerUrl>https://gitlab.com/openflexure/openflexure-microscope-jsclient/issues</bugTrackerUrl>
|
|
||||||
|
|
||||||
<projectSourceUrl>https://gitlab.com/openflexure/openflexure-microscope-jsclient/</projectSourceUrl>
|
|
||||||
<packageSourceUrl>$nuspecURL</packageSourceUrl>
|
|
||||||
|
|
||||||
<tags>openflexure microscope ev</tags>
|
|
||||||
<licenseUrl>https://gitlab.com/openflexure/openflexure-microscope-jsclient/raw/master/LICENSE</licenseUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<iconUrl>https://glcdn.githack.com/openflexure/openflexure-microscope-jsclient/raw/v1.1.0/app/icons/png/512x512.png</iconUrl>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<file src="tools/**" target="tools" />
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
EOL
|
|
||||||
|
|
||||||
# Build installer PS1 content
|
|
||||||
cat > "$toolspath/chocolateyInstall.ps1" <<- EOL
|
|
||||||
\$ErrorActionPreference = "Stop";
|
|
||||||
\$toolsDir = "\$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
|
|
||||||
\$url = "$instURL"
|
|
||||||
|
|
||||||
\$packageArgs = @{
|
|
||||||
packageName = "openflexure-ev"
|
|
||||||
unzipLocation = \$toolsDir
|
|
||||||
fileType = "exe"
|
|
||||||
url = \$url
|
|
||||||
url64bit = \$url
|
|
||||||
|
|
||||||
softwareName = "OpenFlexure eV"
|
|
||||||
|
|
||||||
checksum = "${installerHash}"
|
|
||||||
checksumType = "sha256"
|
|
||||||
|
|
||||||
silentArgs = "/S"
|
|
||||||
}
|
|
||||||
|
|
||||||
Install-ChocolateyPackage @packageArgs
|
|
||||||
|
|
||||||
EOL
|
|
||||||
122
app/menu.js
|
|
@ -1,122 +0,0 @@
|
||||||
const { app, shell, Menu } = require("electron");
|
|
||||||
const updater = require("electron-updater");
|
|
||||||
const autoUpdater = updater.autoUpdater;
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
const openAboutWindow = require("about-window").default;
|
|
||||||
|
|
||||||
const main = require("./app");
|
|
||||||
const { store } = require("./store");
|
|
||||||
|
|
||||||
const template = [
|
|
||||||
{
|
|
||||||
label: "Edit",
|
|
||||||
submenu: [
|
|
||||||
{ role: "undo" },
|
|
||||||
{ role: "redo" },
|
|
||||||
{ type: "separator" },
|
|
||||||
{ role: "cut" },
|
|
||||||
{ role: "copy" },
|
|
||||||
{ role: "paste" },
|
|
||||||
{ role: "delete" },
|
|
||||||
{ role: "selectall" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "View",
|
|
||||||
submenu: [
|
|
||||||
{ role: "reload" },
|
|
||||||
{ role: "forcereload" },
|
|
||||||
{ role: "toggledevtools" },
|
|
||||||
{ type: "separator" },
|
|
||||||
{ role: "togglefullscreen" },
|
|
||||||
{ type: "separator" },
|
|
||||||
{
|
|
||||||
type: "checkbox",
|
|
||||||
checked: store.get("drawCustomTitleBar"),
|
|
||||||
label: "Custom titlebar",
|
|
||||||
click() {
|
|
||||||
main.toggleCustomTitleBar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "window",
|
|
||||||
submenu: [{ role: "minimize" }, { role: "close" }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "help",
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: "About",
|
|
||||||
click() {
|
|
||||||
openAboutWindow({
|
|
||||||
package_json_dir: path.join(__dirname, ".."),
|
|
||||||
icon_path: path.join(__dirname, "/icons/png/512x512.png"),
|
|
||||||
bug_report_url:
|
|
||||||
"https://gitlab.com/openflexure/openflexure-microscope-jsclient/issues",
|
|
||||||
homepage:
|
|
||||||
"https://gitlab.com/openflexure/openflexure-microscope-jsclient"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Homepage",
|
|
||||||
click() {
|
|
||||||
shell.openExternal("https://openflexure.org");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ type: "separator" },
|
|
||||||
{
|
|
||||||
label: "Check for Updates",
|
|
||||||
click() {
|
|
||||||
autoUpdater.checkForUpdates();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
if (process.platform === "darwin") {
|
|
||||||
template.unshift({
|
|
||||||
label: app.getName(),
|
|
||||||
submenu: [
|
|
||||||
{ role: "about" },
|
|
||||||
{ type: "separator" },
|
|
||||||
{ role: "services" },
|
|
||||||
{ type: "separator" },
|
|
||||||
{ role: "hide" },
|
|
||||||
{ role: "hideothers" },
|
|
||||||
{ role: "unhide" },
|
|
||||||
{ type: "separator" },
|
|
||||||
{ role: "quit" }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Edit menu
|
|
||||||
template[1].submenu.push(
|
|
||||||
{ type: "separator" },
|
|
||||||
{
|
|
||||||
label: "Speech",
|
|
||||||
submenu: [{ role: "startspeaking" }, { role: "stopspeaking" }]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Window menu
|
|
||||||
template[3].submenu = [
|
|
||||||
{ role: "close" },
|
|
||||||
{ role: "minimize" },
|
|
||||||
{ role: "zoom" },
|
|
||||||
{ type: "separator" },
|
|
||||||
{ role: "front" }
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
template.unshift({
|
|
||||||
label: "File",
|
|
||||||
submenu: [{ role: "quit" }]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const menu = Menu.buildFromTemplate(template);
|
|
||||||
Menu.setApplicationMenu(menu);
|
|
||||||
|
Before Width: | Height: | Size: 7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
|
@ -1,9 +0,0 @@
|
||||||
const Store = require("electron-store");
|
|
||||||
const store = new Store();
|
|
||||||
|
|
||||||
if (store.has("drawCustomTitleBar") !== true) {
|
|
||||||
// Default to false if on MacOS, otherwise true
|
|
||||||
store.set("drawCustomTitleBar", process.platform === "win32" ? true : false);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.store = store;
|
|
||||||
3641
package-lock.json
generated
28
package.json
|
|
@ -2,34 +2,18 @@
|
||||||
"name": "openflexure-ev",
|
"name": "openflexure-ev",
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "An electron-based user client for the OpenFlexure Microscope Server",
|
"description": "User client for the OpenFlexure Microscope Server",
|
||||||
"author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)",
|
"author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)",
|
||||||
"homepage": "https://gitlab.com/openflexure/openflexure-microscope-jsclient",
|
"homepage": "https://gitlab.com/openflexure/openflexure-microscope-jsclient",
|
||||||
"productName": "OpenFlexure eV",
|
"productName": "OpenFlexure eV",
|
||||||
"publisher": "Bath Open Instrumentation Group",
|
"publisher": "Bath Open Instrumentation Group",
|
||||||
"displayName": "OpenFlexure eV",
|
"displayName": "OpenFlexure eV",
|
||||||
"license": "GNU General Public License v3.0 ",
|
"license": "GNU General Public License v3.0 ",
|
||||||
"main": "./app/app.js",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:web": "vue-cli-service build --mode production.web",
|
"build": "vue-cli-service build --mode production",
|
||||||
"build:app": "vue-cli-service build --mode production.app",
|
"serve": "vue-cli-service serve --mode development"
|
||||||
"serve:web": "vue-cli-service serve --mode development.web",
|
|
||||||
"start": "npm run build:app && electron app/app.js",
|
|
||||||
"dist:linux": "electron-builder --linux --config app/builder-config-x86_64.yaml",
|
|
||||||
"dist:raspi": "electron-builder --linux --config app/builder-config-raspi.yaml",
|
|
||||||
"dist:win": "electron-builder --win --config app/builder-config-x86_64.yaml",
|
|
||||||
"dist:dmg": "electron-builder --mac --config app/builder-config-x86_64.yaml",
|
|
||||||
"dist:appx": "electron-builder --win --config app/builder-config-appx.yaml",
|
|
||||||
"release": "electron-builder --linux --config app/builder-config-raspi.yaml & electron-builder --win --linux --config app/builder-config-x86_64.yaml",
|
|
||||||
"make-icons": "electron-icon-builder --input=./app/icons/icon_base.png --output=./app/"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"about-window": "^1.13.2",
|
|
||||||
"custom-electron-titlebar": "^3.2.2",
|
|
||||||
"electron-context-menu": "^0.15.2",
|
|
||||||
"electron-progressbar": "^1.2.0",
|
|
||||||
"electron-store": "^5.1.1",
|
|
||||||
"electron-updater": "^4.3.1",
|
|
||||||
"material-design-icons": "^3.0.1"
|
"material-design-icons": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -38,9 +22,6 @@
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"css-loader": "^3.5.3",
|
"css-loader": "^3.5.3",
|
||||||
"electron": "^6.1.12",
|
|
||||||
"electron-builder": "^21.2.0",
|
|
||||||
"electron-icon-builder": "^1.0.2",
|
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-prettier": "^6.11.0",
|
"eslint-config-prettier": "^6.11.0",
|
||||||
"eslint-plugin-prettier": "^3.1.3",
|
"eslint-plugin-prettier": "^3.1.3",
|
||||||
|
|
@ -56,9 +37,6 @@
|
||||||
"vue-tour": "^1.3.1",
|
"vue-tour": "^1.3.1",
|
||||||
"vuex": "^3.4.0"
|
"vuex": "^3.4.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
|
||||||
"electron-windows-store": "^2.1.0"
|
|
||||||
},
|
|
||||||
"postcss": {
|
"postcss": {
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"autoprefixer": {}
|
"autoprefixer": {}
|
||||||
|
|
|
||||||
74
src/App.vue
|
|
@ -4,9 +4,11 @@
|
||||||
class="uk-height-1-1 uk-margin-remove uk-padding-remove"
|
class="uk-height-1-1 uk-margin-remove uk-padding-remove"
|
||||||
:class="handleTheme"
|
:class="handleTheme"
|
||||||
>
|
>
|
||||||
<div id="tour-header"></div>
|
<loadingContent v-if="!$store.getters.ready" />
|
||||||
<appContent />
|
<div v-if="$store.getters.ready" id="tour-header"></div>
|
||||||
|
<appContent v-if="$store.getters.ready" />
|
||||||
<v-tour
|
<v-tour
|
||||||
|
v-if="$store.getters.ready"
|
||||||
name="guidedTour"
|
name="guidedTour"
|
||||||
:steps="tourSteps"
|
:steps="tourSteps"
|
||||||
:callbacks="tourCallbacks"
|
:callbacks="tourCallbacks"
|
||||||
|
|
@ -16,9 +18,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isElectron from "./modules/isElectron";
|
|
||||||
// Import components
|
// Import components
|
||||||
import appContent from "./components/appContent.vue";
|
import appContent from "./components/appContent.vue";
|
||||||
|
import loadingContent from "./components/loadingContent.vue";
|
||||||
|
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
// Key Codes
|
// Key Codes
|
||||||
const keyCodes = {
|
const keyCodes = {
|
||||||
|
|
@ -40,15 +44,16 @@ export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
appContent
|
appContent,
|
||||||
|
loadingContent
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
|
appAvailable: false,
|
||||||
keysDown: {},
|
keysDown: {},
|
||||||
systemDark: undefined,
|
systemDark: undefined,
|
||||||
themeObserver: undefined,
|
themeObserver: undefined,
|
||||||
isElectron: isElectron(),
|
|
||||||
tourCallbacks: {
|
tourCallbacks: {
|
||||||
onStop: () => {
|
onStop: () => {
|
||||||
this.setLocalStorageObj("completedTour", true);
|
this.setLocalStorageObj("completedTour", true);
|
||||||
|
|
@ -94,33 +99,6 @@ export default {
|
||||||
placement: "bottom"
|
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",
|
target: "#gallery-tab-icon",
|
||||||
header: {
|
header: {
|
||||||
|
|
@ -175,8 +153,11 @@ export default {
|
||||||
this.systemDark = false;
|
this.systemDark = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Check connection to API
|
||||||
|
this.checkConnection();
|
||||||
// Handle guided tour
|
// Handle guided tour
|
||||||
// If the user has already completed or skipped the 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;
|
var completedTour = this.getLocalStorageObj("completedTour") || false;
|
||||||
if (!completedTour) {
|
if (!completedTour) {
|
||||||
this.$tours["guidedTour"].start();
|
this.$tours["guidedTour"].start();
|
||||||
|
|
@ -189,6 +170,16 @@ export default {
|
||||||
window.addEventListener("keydown", this.keyDownMonitor);
|
window.addEventListener("keydown", this.keyDownMonitor);
|
||||||
window.addEventListener("keyup", this.keyUpMonitor);
|
window.addEventListener("keyup", this.keyUpMonitor);
|
||||||
window.addEventListener("wheel", this.wheelMonitor);
|
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() {
|
beforeDestroy: function() {
|
||||||
|
|
@ -200,9 +191,28 @@ export default {
|
||||||
window.removeEventListener("keydown", this.keyDownMonitor);
|
window.removeEventListener("keydown", this.keyDownMonitor);
|
||||||
window.removeEventListener("keyup", this.keyUpMonitor);
|
window.removeEventListener("keyup", this.keyUpMonitor);
|
||||||
window.removeEventListener("wheel", this.wheelMonitor);
|
window.removeEventListener("wheel", this.wheelMonitor);
|
||||||
|
// Remove origin watcher
|
||||||
|
this.unwatchOriginFunction();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
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() {
|
handleExit: function() {
|
||||||
console.log("Triggered beforeunload");
|
console.log("Triggered beforeunload");
|
||||||
this.$root.$emit("globalTogglePreview", false);
|
this.$root.$emit("globalTogglePreview", false);
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,15 @@
|
||||||
class="uk-flex uk-flex-column uk-padding-remove uk-width-auto uk-height-1-1 uk-text-center"
|
class="uk-flex uk-flex-column uk-padding-remove uk-width-auto uk-height-1-1 uk-text-center"
|
||||||
>
|
>
|
||||||
<tabIcon
|
<tabIcon
|
||||||
id="connect-tab-icon"
|
id="view-tab-icon"
|
||||||
tab-i-d="connect"
|
tab-i-d="view"
|
||||||
:require-connection="false"
|
:require-connection="true"
|
||||||
:current-tab="currentTab"
|
:current-tab="currentTab"
|
||||||
@set-tab="setTab"
|
@set-tab="setTab"
|
||||||
>
|
>
|
||||||
<i class="material-icons">bug_report</i>
|
<i class="material-icons">visibility</i>
|
||||||
</tabIcon>
|
</tabIcon>
|
||||||
|
|
||||||
<tabIcon
|
<tabIcon
|
||||||
id="gallery-tab-icon"
|
id="gallery-tab-icon"
|
||||||
tab-i-d="gallery"
|
tab-i-d="gallery"
|
||||||
|
|
@ -36,15 +37,6 @@
|
||||||
|
|
||||||
<hr />
|
<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
|
<tabIcon
|
||||||
id="navigate-tab-icon"
|
id="navigate-tab-icon"
|
||||||
tab-i-d="navigate"
|
tab-i-d="navigate"
|
||||||
|
|
@ -95,11 +87,11 @@
|
||||||
class="uk-padding-remove uk-height-1-1 uk-width-expand"
|
class="uk-padding-remove uk-height-1-1 uk-width-expand"
|
||||||
>
|
>
|
||||||
<tabContent
|
<tabContent
|
||||||
tab-i-d="connect"
|
tab-i-d="view"
|
||||||
:require-connection="false"
|
:require-connection="true"
|
||||||
:current-tab="currentTab"
|
:current-tab="currentTab"
|
||||||
>
|
>
|
||||||
<connectContent />
|
<viewContent />
|
||||||
</tabContent>
|
</tabContent>
|
||||||
<tabContent
|
<tabContent
|
||||||
tab-i-d="gallery"
|
tab-i-d="gallery"
|
||||||
|
|
@ -108,13 +100,6 @@
|
||||||
>
|
>
|
||||||
<galleryContent />
|
<galleryContent />
|
||||||
</tabContent>
|
</tabContent>
|
||||||
<tabContent
|
|
||||||
tab-i-d="view"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<viewContent />
|
|
||||||
</tabContent>
|
|
||||||
<tabContent
|
<tabContent
|
||||||
tab-i-d="navigate"
|
tab-i-d="navigate"
|
||||||
:require-connection="true"
|
:require-connection="true"
|
||||||
|
|
@ -164,7 +149,6 @@ import tabIcon from "./genericComponents/tabIcon";
|
||||||
import tabContent from "./genericComponents/tabContent";
|
import tabContent from "./genericComponents/tabContent";
|
||||||
|
|
||||||
// Import new content components
|
// Import new content components
|
||||||
import connectContent from "./tabContentComponents/connectContent.vue";
|
|
||||||
import navigateContent from "./tabContentComponents/navigateContent.vue";
|
import navigateContent from "./tabContentComponents/navigateContent.vue";
|
||||||
import captureContent from "./tabContentComponents/captureContent.vue";
|
import captureContent from "./tabContentComponents/captureContent.vue";
|
||||||
import viewContent from "./tabContentComponents/viewContent.vue";
|
import viewContent from "./tabContentComponents/viewContent.vue";
|
||||||
|
|
@ -182,7 +166,6 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
tabIcon,
|
tabIcon,
|
||||||
tabContent,
|
tabContent,
|
||||||
connectContent,
|
|
||||||
navigateContent,
|
navigateContent,
|
||||||
captureContent,
|
captureContent,
|
||||||
viewContent,
|
viewContent,
|
||||||
|
|
@ -195,7 +178,7 @@ export default {
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
plugins: [],
|
plugins: [],
|
||||||
currentTab: "connect",
|
currentTab: "view",
|
||||||
unwatchStoreFunction: null
|
unwatchStoreFunction: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
33
src/components/loadingContent.vue
Normal 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>
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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");
|
|
||||||
16
src/main.js
|
|
@ -76,7 +76,15 @@ Vue.mixin({
|
||||||
},
|
},
|
||||||
|
|
||||||
modalError: function(error) {
|
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 = "";
|
var errormsg = "";
|
||||||
|
|
||||||
// If a response was obtained
|
// If a response was obtained
|
||||||
|
|
@ -100,11 +108,7 @@ Vue.mixin({
|
||||||
errormsg = `${error.message}`;
|
errormsg = `${error.message}`;
|
||||||
console.log(errormsg);
|
console.log(errormsg);
|
||||||
}
|
}
|
||||||
this.$store.commit("setError", errormsg);
|
return errormsg;
|
||||||
UIkit.notification({
|
|
||||||
message: `${errormsg}`,
|
|
||||||
status: "danger"
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
showModalElement: function(element) {
|
showModalElement: function(element) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
15
src/store.js
|
|
@ -5,8 +5,7 @@ Vue.use(Vuex);
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
host: "",
|
origin: window.location.origin,
|
||||||
port: 5000,
|
|
||||||
available: false,
|
available: false,
|
||||||
waiting: false,
|
waiting: false,
|
||||||
error: "",
|
error: "",
|
||||||
|
|
@ -20,9 +19,8 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
changeHost(state, [host, port]) {
|
changeOrigin(state, origin) {
|
||||||
state.host = host;
|
state.origin = origin;
|
||||||
state.port = port;
|
|
||||||
},
|
},
|
||||||
changeWaiting(state, waiting) {
|
changeWaiting(state, waiting) {
|
||||||
state.waiting = waiting;
|
state.waiting = waiting;
|
||||||
|
|
@ -39,8 +37,7 @@ export default new Vuex.Store({
|
||||||
state.waiting = false;
|
state.waiting = false;
|
||||||
state.available = true;
|
state.available = true;
|
||||||
},
|
},
|
||||||
setError(state, msg) {
|
setErrorMessage(state, msg) {
|
||||||
state.waiting = false;
|
|
||||||
state.error = msg;
|
state.error = msg;
|
||||||
},
|
},
|
||||||
addStream(state, id) {
|
addStream(state, id) {
|
||||||
|
|
@ -54,8 +51,8 @@ export default new Vuex.Store({
|
||||||
actions: {},
|
actions: {},
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
uriV2: state => `http://${state.host}:${state.port}/api/v2`,
|
uriV2: state => `${state.origin}/api/v2`,
|
||||||
baseUri: state => `http://${state.host}:${state.port}`,
|
baseUri: state => state.origin,
|
||||||
ready: state => state.available
|
ready: state => state.available
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,15 @@
|
||||||
var webpack = require("webpack");
|
var webpack = require("webpack");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
configureWebpack: config => {
|
configureWebpack: {
|
||||||
(config.target = process.env.VUE_APP_TARGET || "web"),
|
plugins: [
|
||||||
(config.entry =
|
new webpack.DefinePlugin({
|
||||||
process.env.VUE_APP_TARGET == "electron-renderer"
|
"process.env": {
|
||||||
? {
|
PACKAGE: JSON.stringify(require("./package.json"))
|
||||||
main: "./src/main.js",
|
}
|
||||||
app: "./src/main.app.js"
|
})
|
||||||
}
|
]
|
||||||
: {
|
|
||||||
main: "./src/main.js"
|
|
||||||
}),
|
|
||||||
config.plugins.push(
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
"process.env": {
|
|
||||||
PACKAGE: JSON.stringify(require("./package.json"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
outputDir: (function() {
|
|
||||||
if (process.env.VUE_APP_TARGET == "electron-renderer") {
|
|
||||||
return "./app/dist";
|
|
||||||
} else {
|
|
||||||
return "./dist";
|
|
||||||
}
|
|
||||||
})(),
|
|
||||||
|
|
||||||
publicPath: ""
|
publicPath: ""
|
||||||
};
|
};
|
||||||
|
|
|
||||||