Merge branch 'master' into develop

# Conflicts:
#	package.json
#	src/App.vue
#	src/components/panels/ControlPanel.vue
#	src/components/panels/FarmPrinterPanel.vue
#	src/pages/Files.vue
#	src/store/farm/printer/getters.ts
#	src/store/farm/printer/mutations.ts
#	src/store/files/getters.ts
#	src/store/printer/mutations.ts
#	src/store/server/history/getters.ts
#	src/store/socket/getters.ts
This commit is contained in:
Stefan Dej 2021-09-09 00:27:19 +02:00
commit 7ae0fe2209
14 changed files with 117 additions and 72 deletions

View File

@ -23,7 +23,7 @@
<the-sidebar></the-sidebar>
<the-topbar></the-topbar>
<v-main id="content" :style="{ background: mainBackground }">
<v-main id="content" :style="mainStyle">
<v-container fluid id="page-container" class="container px-3 px-sm-6 py-sm-6 mx-auto">
<router-view></router-view>
</v-container>
@ -70,8 +70,18 @@ export default class App extends Mixins(BaseMixin) {
return this.$store.getters['files/getMainBackground']
}
get customStylesheet(): string | null {
return this.$store.getters['files/getCustomStylesheet']
get mainStyle() {
let style = ''
if (this.mainBackground !== null) {
style = 'background-image: url('+this.mainBackground+');'
}
return style
}
get customStylesheet () {
return this.$store.getters["files/getCustomStylesheet"]
}
get customFavicons(): string | null {

View File

@ -8,6 +8,10 @@ export default class BaseMixin extends Vue {
return this.$store.getters['socket/getUrl']
}
get hostUrl(): boolean {
return this.$store.getters['socket/getHostUrl']
}
get remoteMode() {
return this.$store.state.socket.remoteMode
}

View File

@ -31,7 +31,7 @@
<span class="subheading"><v-icon left>mdi-printer-3d</v-icon>{{ printer_name }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-item-group v-if="this.printer_webcams.length">
<v-item-group v-if="printer.socket.isConnected && this.printer_webcams.length">
<v-menu :offset-y="true" title="Webcam">
<template v-slot:activator="{ on, attrs }">
<v-btn small class="px-2 minwidth-0" color="grey darken-3" v-bind="attrs" v-on="on">
@ -64,11 +64,12 @@
<template v-slot:default="{ hover }">
<div>
<v-img
height="200px"
:height="imageHeight"
:src="printer_image"
class="d-flex align-end"
ref="imageDiv"
>
<div v-if="currentCamName !== 'off' && currentWebcam" class="webcamContainer">
<div v-if="printer.socket.isConnected && currentCamName !== 'off' && currentWebcam" class="webcamContainer">
<template v-if="'service' in currentWebcam && currentWebcam.service === 'mjpegstreamer'">
<webcam-mjpegstreamer :cam-settings="currentWebcam"></webcam-mjpegstreamer>
</template>
@ -79,7 +80,12 @@
<v-card-title class="white--text py-2" style="background-color: rgba(0,0,0,0.3); backdrop-filter: blur(3px);">
<v-row>
<v-col class="col-auto pr-0 d-flex align-center" style="width: 58px">
<img class="my-auto" :src="printer_logo" style="width: 100%;" />
<template v-if="printer_logo">
<img :src="printer_logo" style="width: 100%;" class="my-auto" alt="Logo" />
</template>
<template v-else>
<mainsail-logo :color="printerLogoColor" style="width: 100%;" class="my-auto"></mainsail-logo>
</template>
</v-col>
<v-col class="col" style="width: 100px">
<h3 class="font-weight-regular">{{ printer_status }}</h3>
@ -100,7 +106,7 @@
</v-card-text>
<v-fade-transition>
<v-overlay v-if="hover" absolute :z-index="4" >
<v-btn color="primary" @click="clickPrinter">{{ $t("Panels.FarmPrinterPanel.SwitchToPrinter") }}</v-btn>
<v-btn color="primary" @click="clickPrinter">{{ printer.socket.isConnected ? $t("Panels.FarmPrinterPanel.SwitchToPrinter") : $t("Panels.FarmPrinterPanel.ReconnectToPrinter") }}</v-btn>
</v-overlay>
</v-fade-transition>
</div>
@ -110,23 +116,25 @@
</template>
<script lang="ts">
import { Component, Mixins, Prop } from 'vue-property-decorator'
import {Component, Mixins, Prop, Ref, Vue} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { FarmPrinterState } from '@/store/farm/printer/types'
import Mjpegstreamer from '@/components/webcams/Mjpegstreamer.vue'
import MjpegstreamerAdaptive from '@/components/webcams/MjpegstreamerAdaptive.vue'
import MainsailLogo from '@/components/ui/MainsailLogo.vue'
@Component({
components: {
'webcam-mjpegstreamer': Mjpegstreamer,
'webcam-mjpegstreamer-adaptive': MjpegstreamerAdaptive,
'mainsail-logo': MainsailLogo
}
})
export default class FarmPrinterPanel extends Mixins(BaseMixin) {
private imageHeight = 200;
@Prop({ type: Object, required: true }) printer!: FarmPrinterState
@Ref() readonly imageDiv!: Vue
get printerUrl() {
const thisUrl = window.location.href.split('/')
@ -170,6 +178,10 @@ export default class FarmPrinterPanel extends Mixins(BaseMixin) {
return this.$store.getters['farm/'+this.printer._namespace+'/getLogo']
}
get printerLogoColor() {
return this.$store.getters["farm/"+this.printer._namespace+"/getLogoColor"]
}
get printer_position() {
return this.$store.getters['farm/'+this.printer._namespace+'/getPosition']
}
@ -196,5 +208,20 @@ export default class FarmPrinterPanel extends Mixins(BaseMixin) {
this.$store.dispatch('farm/'+this.printer._namespace+'/reconnect')
}
mounted() {
window.addEventListener("resize", this.resize)
this.resize()
}
beforeDestroy() {
window.addEventListener("resize", this.resize)
}
resize() {
if (this.imageDiv?.$el?.clientWidth) {
this.imageHeight = Math.round(this.imageDiv.$el.clientWidth / 3 * 2)
} else this.imageHeight = 200
}
}
</script>

View File

@ -35,9 +35,7 @@ export default class Mjpegstreamer extends Mixins(BaseMixin) {
get url() {
const baseUrl = this.camSettings.url
const hostUrl = new URL(this.printerUrl === undefined ? document.URL : this.printerUrl)
const url = new URL(baseUrl, hostUrl.origin)
const url = new URL(baseUrl, this.printerUrl === undefined ? this.hostUrl.toString() : this.printerUrl)
url.searchParams.append('bypassCache', this.refresh.toString())
return decodeURIComponent(url.toString())

View File

@ -16,7 +16,7 @@
<v-progress-circular indeterminate color="primary"></v-progress-circular>
</div>
<canvas ref="mjpegstreamerAdaptive" width="600" height="400" :style="webcamStyle" :class="'webcamImage '+(isLoaded ? '' : 'hiddenWebcam')"></canvas>
<span class="webcamFpsOutput" v-if="isLoaded && showFps">{{ $t('Panels.WebcamPanel.FPS')}}: {{ currentFPS }}</span>
<span class="webcamFpsOutput" v-if="isLoaded && showFps">{{ $t('Panels.WebcamPanel.FPS')}}: {{ fpsOutput }}</span>
</div>
</template>
@ -57,6 +57,10 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
return ''
}
get fpsOutput() {
return (this.currentFPS < 10) ? '0'+this.currentFPS.toString() : this.currentFPS
}
visibilityChanged(isVisible: boolean) {
this.isVisible = isVisible
if (isVisible) this.refreshFrame()
@ -95,15 +99,13 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
async setFrame() {
const baseUrl = this.camSettings.url
const hostUrl = new URL(this.printerUrl === undefined ? document.URL : this.printerUrl)
const url = new URL(baseUrl, hostUrl.origin)
const url = new URL(baseUrl, this.printerUrl === undefined ? this.hostUrl.toString() : this.printerUrl)
url.searchParams.append('bypassCache', this.refresh.toString())
url.searchParams.set('action', 'snapshot')
this.request_start_time = performance.now()
this.currentFPS = Math.round(1000 / this.time)
this.currentFPS = (this.time > 0) ? Math.round(1000 / this.time) : 0
let canvas = this.$refs.mjpegstreamerAdaptive
if (canvas) {

View File

@ -32,9 +32,7 @@ export default class Uv4lMjpeg extends Mixins(BaseMixin) {
get url() {
const baseUrl = this.camSettings.url
const hostUrl = new URL(this.printerUrl === null ? document.URL : this.printerUrl)
const url = new URL(baseUrl, hostUrl.origin)
const url = new URL(baseUrl, this.printerUrl === null ? this.hostUrl.toString() : this.printerUrl)
return decodeURIComponent(url.toString())
}

View File

@ -256,7 +256,8 @@
},
"FarmPrinterPanel": {
"WebcamOff": "Off",
"SwitchToPrinter": "Switch to Printer"
"SwitchToPrinter": "Switch to Printer",
"ReconnectToPrinter": "Reconnect"
},
"KlippyStatePanel": {
"KlippyState": "Klippy-State",

View File

@ -496,7 +496,7 @@ export default class PageFiles extends Mixins(BaseMixin) {
}
private input_rules = [
(value: string) => value.indexOf(' ') === -1 || 'Name contain spaces!'
(value: string) => value.indexOf(' ') === -1 || 'Name contains spaces!'
]
get headers() {
@ -792,11 +792,11 @@ export default class PageFiles extends Mixins(BaseMixin) {
}
created() {
this.$socket.emit('server.files.get_directory', { path: this.currentPath }, { action: 'files/getDirectory' })
this.loadPath()
}
loadPath() {
this.$socket.emit('server.files.get_directory', { path: this.currentPath }, { action: 'files/getDirectory' })
let dirArray = this.currentPath.split('/')
this.files = findDirectory(this.filetree, dirArray)
if (this.files !== null) {
@ -804,46 +804,27 @@ export default class PageFiles extends Mixins(BaseMixin) {
this.files = this.files.filter(file => file.filename !== 'thumbs' && file.filename.substr(0, 1) !== '.')
}
if (!this.showPrintedFiles) {
this.files = this.files.filter(file => this.$store.getters['server/history/getPrintStatus']({
filename: (this.currentPath+'/'+file.filename).substr(7),
modified: file.modified.getTime()
}) !== 'completed')
this.files = this.files.filter(file => {
if (file.isDirectory) return true
else {
return (this.$store.getters["server/history/getPrintStatusByFilename"](
(this.currentPath+"/"+file.filename).substr(7),
file.modified.getTime()
) !== 'completed')
}
})
}
}
}
@Watch('filetree', { deep: true })
filetreeChanged(newVal: FileStateFile[]) {
let dirArray = this.currentPath.split('/')
this.files = findDirectory(newVal, dirArray)
if (this.files?.length && !this.showHiddenFiles) {
this.files = this.files.filter(file => file.filename !== 'thumbs' && file.filename.substr(0, 1) !== '.')
}
if (this.files?.length && !this.showPrintedFiles) {
this.files = this.files.filter(file => this.$store.getters['server/history/getPrintStatus']({
filename: (this.currentPath+'/'+file.filename).substr(7),
modified: new Date(file.modified).getTime()
}) !== 'completed')
}
filetreeChanged() {
this.loadPath()
}
@Watch('currentPath')
currentPathChanged(newVal: string) {
let dirArray = newVal.split('/')
this.files = findDirectory(this.filetree, dirArray)
if (this.files?.length && !this.showHiddenFiles) {
this.files = this.files.filter(file => file.filename !== 'thumbs' && file.filename.substr(0, 1) !== '.')
}
if (this.files?.length && !this.showPrintedFiles) {
this.files = this.files.filter(file => this.$store.getters['server/history/getPrintStatus']({
filename: (this.currentPath+'/'+file.filename).substr(7),
modified: new Date(file.modified).getTime()
}) !== 'completed')
}
currentPathChanged() {
this.loadPath()
}
formatPrintTime(totalSeconds: number) {

View File

@ -1,4 +1,4 @@
import { themeDir } from '@/store/variables'
import {defaultLogoColor, themeDir} from '@/store/variables'
import {convertName} from '@/plugins/helpers'
import {GetterTree} from 'vuex'
import {FarmPrinterState} from '@/store/farm/printer/types'
@ -37,6 +37,10 @@ export const getters: GetterTree<FarmPrinterState, any> = {
return state.socket.port !== 80 ? state.socket.hostname+':'+state.socket.port : state.socket.hostname
},
getLogoColor: (state) => {
return state.data.gui?.theme?.logo ?? defaultLogoColor
},
getStatus: (state, getters) => {
if (!state.socket.isConnected) {
return state.socket.isConnecting ? 'Connecting...' : 'Disconnected'
@ -99,9 +103,9 @@ export const getters: GetterTree<FarmPrinterState, any> = {
getLogo: (state, getters) => {
const acceptName = 'sidebar-logo'
const acceptExtensions = ['gif', 'jpg', 'png', 'gif']
const acceptExtensions = ['gif', 'jpg', 'png', 'gif', 'svg']
return getters['getThemeFileUrl'](acceptName, acceptExtensions) ?? '/img/logo.svg'
return getters['getThemeFileUrl'](acceptName, acceptExtensions)
},
getPosition: state => {

View File

@ -58,13 +58,11 @@ export const mutations: MutationTree<FarmPrinterState> = {
setConfigDir(state, payload) {
// eslint-disable-next-line
Object.values(payload).forEach((file: any) => {
if ('filename' in file) {
if (file.filename.startsWith('.theme/')) {
state.theme_files.push(file.filename)
}
}
})
},
if (file.path?.startsWith(".theme/")) {
state.theme_files.push(file.path)
}
})
},
setDatabases(state, payload) {
Vue.set(state, 'databases', payload.namespaces)

View File

@ -36,8 +36,8 @@ export const getters: GetterTree<FileState, any> = {
const acceptName = 'main-background'
const acceptExtensions = ['jpg', 'jpeg', 'png', 'gif', 'svg']
return getters['getThemeFileUrl'](acceptName, acceptExtensions) ?? 'transparent'
},
return getters['getThemeFileUrl'](acceptName, acceptExtensions)
},
getCustomStylesheet: (state, getters) => {
const acceptName = 'custom'

View File

@ -25,7 +25,13 @@ export const mutations: MutationTree<PrinterState> = {
Object.keys(payload).forEach((key: string) => {
const value = payload[key]
if (typeof value === 'object' && !Array.isArray(value) && key in currentState && value !== null) {
if (
typeof value === 'object' &&
!Array.isArray(value) &&
key in currentState &&
value !== null &&
currentState[key] !== null
) {
setDataDeep(currentState[key], value)
} else if (key === 'temperature') {
const newValue = Math.round(value * 10) / 10

View File

@ -183,7 +183,19 @@ export const getters: GetterTree<ServerHistoryState, any> = {
return ''
},
getPrintStatusChipColor: () => (status: string) => {
getPrintStatusByFilename: (state) => (filename: string, modified: number) => {
if (state.jobs.length) {
const job = state.jobs.find((job) => {
return job.filename === filename && Math.round(job.metadata?.modified*1000) === modified
})
return job?.status ?? ""
}
return ""
},
getPrintStatusChipColor: () => (status: string) => {
switch(status) {
case 'in_progress': return 'blue accent-3' //'blue-grey darken-1'
case 'completed': return 'green' //'green'

View File

@ -8,7 +8,11 @@ export const getters: GetterTree<SocketState, RootState> = {
return '//' + state.hostname + (state.port !== 80 ? ':'+state.port : '')
},
getWebsocketUrl: (state, getters) => {
getHostUrl: (state) => {
return (state.protocol === 'wss' ? 'https' : 'http')+"://" + state.hostname + '/'
},
getWebsocketUrl: (state, getters) => {
return state.protocol + ':' + getters['getUrl'] + '/websocket'
},
}