mirror of
https://github.com/Macrame-App/Macrame
synced 2025-12-29 15:29:26 +00:00
Compare commits
No commits in common. "c77233f07d997f602c26701655772b3e6a1cde07" and "3193127809bff116f53810f7f186be85e0dc7559" have entirely different histories.
c77233f07d
...
3193127809
16 changed files with 27 additions and 533 deletions
|
|
@ -10,7 +10,7 @@ bin = "tmp/main.exe"
|
||||||
[build]
|
[build]
|
||||||
cmd = "go build -o ./tmp/main.exe main.go"
|
cmd = "go build -o ./tmp/main.exe main.go"
|
||||||
include_ext = ["go"]
|
include_ext = ["go"]
|
||||||
exclude_dir = ["ui", "panels", "builds"]
|
exclude_dir = ["fe", "panels", "builds"]
|
||||||
|
|
||||||
# Restart on file changes
|
# Restart on file changes
|
||||||
[log]
|
[log]
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func configFileExists() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckUIDevDir() {
|
func CheckUIDevDir() {
|
||||||
log.Println("Checking UI dev directory...")
|
log.Println("Checking FE dev directory...")
|
||||||
_, err := os.Stat("ui")
|
_, err := os.Stat("ui")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -72,10 +72,10 @@ func CheckUIDevDir() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
copyConfigToUi()
|
copyConfigToFe()
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyConfigToUi() {
|
func copyConfigToFe() {
|
||||||
data, err := os.ReadFile(configPath)
|
data, err := os.ReadFile(configPath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -35,25 +35,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
<h4>Not authorized!</h4>
|
<h4>Not authorized!</h4>
|
||||||
<p>Click here to start authorization and open the "Devices" page on your PC.</p>
|
<p>Click here to start authorization and open the "Devices" page on your PC.</p>
|
||||||
</AlertComp>
|
</AlertComp>
|
||||||
<NotificationsComp />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import MainMenu from '@/components/base/MainMenu.vue'
|
import MainMenu from '@/components/base/MainMenu.vue'
|
||||||
import { onBeforeUnmount, onMounted, onUnmounted, onUpdated, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { RouterView, useRoute, useRouter } from 'vue-router'
|
import { RouterView, useRoute } from 'vue-router'
|
||||||
import { useDeviceStore } from './stores/device'
|
import { useDeviceStore } from './stores/device'
|
||||||
import { useSettingStore } from './stores/settings'
|
|
||||||
import { isLocal } from './services/ApiService'
|
import { isLocal } from './services/ApiService'
|
||||||
import AlertComp from './components/base/AlertComp.vue'
|
import AlertComp from './components/base/AlertComp.vue'
|
||||||
import NotificationsComp from './components/base/NotificationsComp.vue'
|
|
||||||
import { GetLocalPanel } from './services/PanelService'
|
|
||||||
|
|
||||||
const device = useDeviceStore()
|
const device = useDeviceStore()
|
||||||
const settings = useSettingStore()
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
|
||||||
const handshake = ref(false)
|
const handshake = ref(false)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
@ -61,38 +55,17 @@ onMounted(() => {
|
||||||
// If not present in LocalStorage a new uuidV4 will be generated
|
// If not present in LocalStorage a new uuidV4 will be generated
|
||||||
device.uuid()
|
device.uuid()
|
||||||
|
|
||||||
settings.loadSettings()
|
if (!isLocal) appHandshake()
|
||||||
|
|
||||||
if (!isLocal) {
|
|
||||||
appHandshake()
|
|
||||||
loadLastPanel()
|
|
||||||
}
|
|
||||||
|
|
||||||
device.$subscribe(() => {
|
device.$subscribe(() => {
|
||||||
if (device.key()) handshake.value = true
|
if (device.key()) handshake.value = true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
onUpdated(() => {
|
|
||||||
console.log('App updated')
|
|
||||||
|
|
||||||
// loadLastPanel()
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
settings.saveSettings()
|
|
||||||
})
|
|
||||||
|
|
||||||
async function appHandshake() {
|
async function appHandshake() {
|
||||||
const hsReq = await device.remoteHandshake()
|
const hsReq = await device.remoteHandshake()
|
||||||
handshake.value = hsReq
|
handshake.value = hsReq
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLastPanel() {
|
|
||||||
if (route.fullPath != GetLocalPanel()) {
|
|
||||||
router.push(GetLocalPanel())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
<IconDevices />{{ isLocal() ? 'Devices' : 'Server' }}
|
<IconDevices />{{ isLocal() ? 'Devices' : 'Server' }}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<RouterLink @click="menuOpen = false" to="/settings"> <IconSettings />Settings </RouterLink>
|
|
||||||
</li>
|
|
||||||
<!-- <li>
|
<!-- <li>
|
||||||
<RouterLink @click="menuOpen = false" to="/settings">
|
<RouterLink @click="menuOpen = false" to="/settings">
|
||||||
<IconSettings />Settings
|
<IconSettings />Settings
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="mcrm-notifications">
|
|
||||||
<ToastComp
|
|
||||||
v-for="(notification, id) in notificationList"
|
|
||||||
:key="id"
|
|
||||||
:id="id"
|
|
||||||
:title="notification.title"
|
|
||||||
:message="notification.message"
|
|
||||||
:variant="notification.variant"
|
|
||||||
:time="notification.time"
|
|
||||||
:closable="notification.closable"
|
|
||||||
:notification="id"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { useNoticationStore } from '@/stores/notifications'
|
|
||||||
import { computed, onMounted } from 'vue'
|
|
||||||
import ToastComp from './ToastComp.vue'
|
|
||||||
|
|
||||||
const notifications = useNoticationStore()
|
|
||||||
|
|
||||||
const notificationList = computed(() => notifications.list)
|
|
||||||
|
|
||||||
// onMounted(() => {
|
|
||||||
// notifications.$subscribe((mutation, state) => {
|
|
||||||
// console.log(mutation, state)
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@reference "@/assets/main.css";
|
|
||||||
|
|
||||||
.mcrm-notifications {
|
|
||||||
@apply grid
|
|
||||||
gap-4
|
|
||||||
fixed
|
|
||||||
bottom-0
|
|
||||||
right-0
|
|
||||||
p-4
|
|
||||||
z-50
|
|
||||||
w-[30ch];
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
<template>
|
|
||||||
<div :class="`mcrm-toast mcrm-block block__${toastOptions.variant}`" ref="toast">
|
|
||||||
<ButtonComp v-if="closable" variant="subtle" size="sm" @click="closeToast()">
|
|
||||||
<IconX />
|
|
||||||
</ButtonComp>
|
|
||||||
<h4>{{ title }}</h4>
|
|
||||||
<p>{{ message }}</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { useNoticationStore } from '@/stores/notifications'
|
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
|
||||||
import ButtonComp from './ButtonComp.vue'
|
|
||||||
import { IconX } from '@tabler/icons-vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
title: String,
|
|
||||||
message: String,
|
|
||||||
variant: String,
|
|
||||||
time: Number,
|
|
||||||
closable: Boolean,
|
|
||||||
notification: [String, Number],
|
|
||||||
})
|
|
||||||
|
|
||||||
const notifications = useNoticationStore()
|
|
||||||
|
|
||||||
const toast = ref(null)
|
|
||||||
|
|
||||||
const toastOptions = reactive({
|
|
||||||
variant: props.variant,
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (toastOptions.variant == 'info') toastOptions.variant = 'primary'
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
closeToast()
|
|
||||||
}, props.time)
|
|
||||||
})
|
|
||||||
|
|
||||||
const closeToast = () => {
|
|
||||||
toast.value.classList.add('closing')
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
toast.value.remove()
|
|
||||||
if (props.notification) notifications.remove(props.notification)
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@reference "@/assets/main.css";
|
|
||||||
|
|
||||||
.mcrm-toast {
|
|
||||||
@apply relative
|
|
||||||
grid
|
|
||||||
gap-2
|
|
||||||
p-4
|
|
||||||
transition-opacity
|
|
||||||
duration-400;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
@apply pr-6
|
|
||||||
text-base;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
@apply text-sm opacity-80;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.closing {
|
|
||||||
@apply opacity-0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.btn {
|
|
||||||
@apply absolute
|
|
||||||
top-2 right-2
|
|
||||||
p-2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -93,6 +93,8 @@ const server = reactive({
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const serverIP = await device.serverGetIP()
|
const serverIP = await device.serverGetIP()
|
||||||
server.ip = serverIP
|
server.ip = serverIP
|
||||||
|
// server.port = window.__CONFIG__.MCRM__PORT
|
||||||
|
// server.fullPath = `http://${server.ip}:${server.port}`
|
||||||
|
|
||||||
const remoteCount = await device.serverGetRemotes(true)
|
const remoteCount = await device.serverGetRemotes(true)
|
||||||
server.remoteCount = remoteCount
|
server.remoteCount = remoteCount
|
||||||
|
|
@ -102,6 +104,8 @@ onMounted(async () => {
|
||||||
|
|
||||||
const panelCount = await panel.getList(true)
|
const panelCount = await panel.getList(true)
|
||||||
server.panelCount = panelCount
|
server.panelCount = panelCount
|
||||||
|
|
||||||
|
console.log(server)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
<template>
|
|
||||||
<div :class="`form-input ${horizontal ? 'horizontal' : ''}`">
|
|
||||||
<template v-if="label">
|
|
||||||
<label :for="name">
|
|
||||||
{{ label }}
|
|
||||||
</label>
|
|
||||||
</template>
|
|
||||||
<template v-if="type != 'radio' && type != 'checkbox'">
|
|
||||||
<input
|
|
||||||
:type="type"
|
|
||||||
:name="name"
|
|
||||||
:id="name"
|
|
||||||
:value="value"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:disabled="disabled"
|
|
||||||
:readonly="readonly"
|
|
||||||
:required="required"
|
|
||||||
:autofocus="autofocus"
|
|
||||||
@change="$emit('onChange', $event.target.value)"
|
|
||||||
@input="$emit('onInput', $event.target.value)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="options">
|
|
||||||
<div class="boolean-group">
|
|
||||||
<template v-for="option in options" :key="option.value">
|
|
||||||
<label :for="`${name}-${option.value}`">
|
|
||||||
{{ option.label }}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
:type="type"
|
|
||||||
:name="name"
|
|
||||||
:id="`${name}-${option.value}`"
|
|
||||||
:disabled="disabled"
|
|
||||||
:readonly="readonly"
|
|
||||||
:required="required"
|
|
||||||
:checked="value == option.value"
|
|
||||||
:value="option.value"
|
|
||||||
@change="$emit('onChange', $event.target.value)"
|
|
||||||
@input="$emit('onInput', $event.target.value)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<input
|
|
||||||
:type="type"
|
|
||||||
:name="name"
|
|
||||||
:id="name"
|
|
||||||
:value="value"
|
|
||||||
:disabled="disabled"
|
|
||||||
:readonly="readonly"
|
|
||||||
:required="required"
|
|
||||||
:checked="value === true || value === 'true'"
|
|
||||||
@change="$emit('onChange', $event.target)"
|
|
||||||
@input="$emit('onInput', $event.target)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
defineProps({
|
|
||||||
type: String,
|
|
||||||
name: String,
|
|
||||||
value: [String, Number, Boolean],
|
|
||||||
options: Array,
|
|
||||||
label: String,
|
|
||||||
placeholder: String,
|
|
||||||
disabled: Boolean,
|
|
||||||
readonly: Boolean,
|
|
||||||
required: Boolean,
|
|
||||||
autofocus: Boolean,
|
|
||||||
horizontal: Boolean,
|
|
||||||
})
|
|
||||||
|
|
||||||
defineEmits(['onChange', 'onInput'])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@reference "@/assets/main.css";
|
|
||||||
|
|
||||||
.form-input {
|
|
||||||
@apply flex
|
|
||||||
flex-col
|
|
||||||
items-center
|
|
||||||
gap-2;
|
|
||||||
|
|
||||||
&.horizontal {
|
|
||||||
@apply flex-row
|
|
||||||
justify-between;
|
|
||||||
|
|
||||||
input {
|
|
||||||
@apply max-w-2/3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.boolean-group {
|
|
||||||
@apply flex
|
|
||||||
items-center
|
|
||||||
gap-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='checkbox'],
|
|
||||||
input[type='radio'] {
|
|
||||||
@apply size-5
|
|
||||||
cursor-pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[disabled] {
|
|
||||||
@apply opacity-60
|
|
||||||
cursor-not-allowed;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -21,48 +21,37 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="panel-view">
|
<div id="panel-view">
|
||||||
<div class="panel-container" ref="panelContainer" v-html="viewPanel.html"></div>
|
<div class="panel-preview__content" ref="panelView" v-html="viewPanel.html"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { isLocal } from '@/services/ApiService'
|
|
||||||
import { RunMacro } from '@/services/MacroService'
|
import { RunMacro } from '@/services/MacroService'
|
||||||
import {
|
import {
|
||||||
CheckLocalPanel,
|
|
||||||
PanelButtonListeners,
|
PanelButtonListeners,
|
||||||
PanelDialogListeners,
|
PanelDialogListeners,
|
||||||
RemovePanelScripts,
|
RemovePanelScripts,
|
||||||
RemovePanelStyle,
|
RemovePanelStyle,
|
||||||
SavePanelToLocal,
|
|
||||||
SetPanelStyle,
|
SetPanelStyle,
|
||||||
StripPanelHTML,
|
StripPanelHTML,
|
||||||
} from '@/services/PanelService'
|
} from '@/services/PanelService'
|
||||||
import { usePanelStore } from '@/stores/panel'
|
import { usePanelStore } from '@/stores/panel'
|
||||||
import { useSettingStore } from '@/stores/settings'
|
|
||||||
import { onMounted, onUnmounted, ref } from 'vue'
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
const panel = usePanelStore()
|
const panel = usePanelStore()
|
||||||
const settings = useSettingStore()
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dirname: String,
|
dirname: String,
|
||||||
})
|
})
|
||||||
|
|
||||||
const panelContainer = ref(null)
|
const panelView = ref(null)
|
||||||
|
|
||||||
const viewPanel = ref({})
|
const viewPanel = ref({})
|
||||||
|
|
||||||
const wakeLock = ref(null)
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
requestWakeLock()
|
|
||||||
|
|
||||||
const currentPanel = await panel.get(props.dirname)
|
const currentPanel = await panel.get(props.dirname)
|
||||||
viewPanel.value = currentPanel
|
viewPanel.value = currentPanel
|
||||||
|
|
||||||
if (!isLocal() && settings.get('openLastPanel') && !CheckLocalPanel()) SavePanelToLocal()
|
|
||||||
|
|
||||||
viewPanel.value.html = StripPanelHTML(viewPanel.value.html, viewPanel.value.aspectRatio)
|
viewPanel.value.html = StripPanelHTML(viewPanel.value.html, viewPanel.value.aspectRatio)
|
||||||
SetPanelStyle(viewPanel.value.style)
|
SetPanelStyle(viewPanel.value.style)
|
||||||
|
|
||||||
|
|
@ -78,8 +67,6 @@ onMounted(async () => {
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
RemovePanelStyle()
|
RemovePanelStyle()
|
||||||
RemovePanelScripts()
|
RemovePanelScripts()
|
||||||
|
|
||||||
wakeLock.value.release()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const viewPanelListeners = () => {
|
const viewPanelListeners = () => {
|
||||||
|
|
@ -87,20 +74,8 @@ const viewPanelListeners = () => {
|
||||||
RunMacro(viewPanel.value.macros[button.id])
|
RunMacro(viewPanel.value.macros[button.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelButtonListeners(panelContainer.value, callback)
|
PanelButtonListeners(panelView.value, callback)
|
||||||
PanelDialogListeners(panelContainer.value)
|
PanelDialogListeners(panelView.value)
|
||||||
}
|
|
||||||
|
|
||||||
const requestWakeLock = async () => {
|
|
||||||
try {
|
|
||||||
if ('wakeLock' in navigator) {
|
|
||||||
wakeLock.value = await navigator.wakeLock.request('screen')
|
|
||||||
} else {
|
|
||||||
console.warn('Wake Lock API not supported')
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`${err.name}, ${err.message}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -113,7 +88,7 @@ const requestWakeLock = async () => {
|
||||||
size-full
|
size-full
|
||||||
bg-black;
|
bg-black;
|
||||||
|
|
||||||
.panel-container {
|
.panel-preview__content {
|
||||||
@apply relative
|
@apply relative
|
||||||
grid
|
grid
|
||||||
justify-center
|
justify-center
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,7 @@ import router from '@/router'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(router)
|
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
router.isReady().then(() => {
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -60,11 +60,11 @@ const router = createRouter({
|
||||||
name: 'devices',
|
name: 'devices',
|
||||||
component: () => import('../views/DevicesView.vue'),
|
component: () => import('../views/DevicesView.vue'),
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
path: '/settings',
|
// path: '/settings',
|
||||||
name: 'settings',
|
// name: 'settings',
|
||||||
component: () => import('../views/SettingsView.vue'),
|
// component: () => import('../views/SettingsView.vue'),
|
||||||
},
|
// },
|
||||||
// {
|
// {
|
||||||
// path: '/about',
|
// path: '/about',
|
||||||
// name: 'about',
|
// name: 'about',
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useNoticationStore } from '@/stores/notifications'
|
|
||||||
|
|
||||||
export const SetPanelStyle = (styleStr) => {
|
export const SetPanelStyle = (styleStr) => {
|
||||||
const styleEl = document.createElement('style')
|
const styleEl = document.createElement('style')
|
||||||
styleEl.setAttribute('custom_panel_style', true)
|
styleEl.setAttribute('custom_panel_style', true)
|
||||||
|
|
@ -120,32 +118,3 @@ export const PanelDialogListeners = (panelEl) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPanelUrl = () => {
|
|
||||||
const url = new URL(window.location.href)
|
|
||||||
return url.pathname
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SavePanelToLocal = () => {
|
|
||||||
localStorage.setItem('last_opened_panel', getPanelUrl())
|
|
||||||
|
|
||||||
const notificationStore = useNoticationStore()
|
|
||||||
|
|
||||||
notificationStore.add({
|
|
||||||
message: 'Panel will be opened next launch',
|
|
||||||
variant: 'success',
|
|
||||||
time: 1000,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CheckLocalPanel = () => {
|
|
||||||
const localPanel = localStorage.getItem('last_opened_panel')
|
|
||||||
|
|
||||||
if (localPanel) return localPanel == getPanelUrl()
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
export const GetLocalPanel = () => {
|
|
||||||
return localStorage.getItem('last_opened_panel')
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
export const useNoticationStore = defineStore('notications', () => {
|
|
||||||
const list = ref({})
|
|
||||||
|
|
||||||
const add = (notification) => {
|
|
||||||
list.value[Date.now()] = notification
|
|
||||||
}
|
|
||||||
|
|
||||||
const remove = (id) => {
|
|
||||||
delete list.value[id]
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
list,
|
|
||||||
add,
|
|
||||||
remove,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
export const useSettingStore = defineStore('settings', () => {
|
|
||||||
const list = ref({})
|
|
||||||
|
|
||||||
const get = (key, all = false) => {
|
|
||||||
if (key === undefined) return list.value
|
|
||||||
|
|
||||||
if (!all) return list.value[key].value
|
|
||||||
else return list.value[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
const set = (key, value) => {
|
|
||||||
if (value === 'true' || value === 'false') value = value === 'true'
|
|
||||||
|
|
||||||
list.value[key].value = value
|
|
||||||
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadSettings = (returnObj = false) => {
|
|
||||||
const settings = localStorage.getItem('settings')
|
|
||||||
|
|
||||||
if (settings) list.value = JSON.parse(settings)
|
|
||||||
else list.value = loadDefaultSettings()
|
|
||||||
|
|
||||||
if (returnObj) return list.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadDefaultSettings = () => {
|
|
||||||
const defaultSettings = {
|
|
||||||
openLastPanel: {
|
|
||||||
title: 'Open last panel',
|
|
||||||
label: 'Open last panel',
|
|
||||||
description: 'Open the last panel on startup',
|
|
||||||
type: 'checkbox',
|
|
||||||
value: true,
|
|
||||||
remote: true,
|
|
||||||
},
|
|
||||||
mcrmPort: {
|
|
||||||
title: 'Macrame port',
|
|
||||||
label: 'Port',
|
|
||||||
description:
|
|
||||||
'The port that is used by Macrame, changing this will require a restart of the application.',
|
|
||||||
type: 'number',
|
|
||||||
value: window.__CONFIG__.MCRM__PORT,
|
|
||||||
remote: false,
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveSettings = () => {
|
|
||||||
localStorage.setItem('settings', JSON.stringify(list.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetSettings = () => {
|
|
||||||
localStorage.removeItem('settings')
|
|
||||||
list.value = loadDefaultSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
list,
|
|
||||||
get,
|
|
||||||
set,
|
|
||||||
loadSettings,
|
|
||||||
loadDefaultSettings,
|
|
||||||
saveSettings,
|
|
||||||
resetSettings,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
@ -45,6 +45,7 @@ import ButtonComp from '@/components/base/ButtonComp.vue'
|
||||||
import PanelEdit from '@/components/panels/PanelEdit.vue'
|
import PanelEdit from '@/components/panels/PanelEdit.vue'
|
||||||
import PanelView from '@/components/panels/PanelView.vue'
|
import PanelView from '@/components/panels/PanelView.vue'
|
||||||
import PanelsOverview from '@/components/panels/PanelsOverview.vue'
|
import PanelsOverview from '@/components/panels/PanelsOverview.vue'
|
||||||
|
import { isLocal } from '@/services/ApiService'
|
||||||
import { IconArrowLeft } from '@tabler/icons-vue'
|
import { IconArrowLeft } from '@tabler/icons-vue'
|
||||||
import { onMounted, onUpdated, reactive } from 'vue'
|
import { onMounted, onUpdated, reactive } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
|
||||||
|
|
@ -20,96 +20,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="settings" class="panel">
|
<div></div>
|
||||||
<h1 class="flex items-end justify-between !w-full panel__title">
|
|
||||||
<div>Settings</div>
|
|
||||||
<ButtonComp variant="subtle" size="sm" @click="settings.resetSettings()">
|
|
||||||
<IconRestore /> Reset
|
|
||||||
</ButtonComp>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div class="panel__content">
|
|
||||||
<div class="settings-container">
|
|
||||||
<template
|
|
||||||
class="setting-block mcrm-block block__dark"
|
|
||||||
v-for="(setting, name) in settingList"
|
|
||||||
:key="setting"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="(setting.remote && !isLocal()) || (!setting.remote && isLocal())"
|
|
||||||
class="setting-block mcrm-block block__dark"
|
|
||||||
>
|
|
||||||
<h4>{{ setting.title }}</h4>
|
|
||||||
<p class="text-sm">
|
|
||||||
<em>{{ setting.description }}</em>
|
|
||||||
</p>
|
|
||||||
<FormInput
|
|
||||||
:type="setting.type"
|
|
||||||
:label="setting.label"
|
|
||||||
:name="name"
|
|
||||||
:value="setting.value"
|
|
||||||
:options="setting.options"
|
|
||||||
:disabled="setting.disabled"
|
|
||||||
:horizontal="true"
|
|
||||||
@onChange="updateSetting(name, setting.type, $event)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup></script>
|
||||||
import ButtonComp from '@/components/base/ButtonComp.vue'
|
|
||||||
import { IconRestore } from '@tabler/icons-vue'
|
|
||||||
import FormInput from '@/components/form/FormInput.vue'
|
|
||||||
|
|
||||||
import { isLocal } from '@/services/ApiService'
|
<style lang="scss" scoped></style>
|
||||||
|
|
||||||
import { useSettingStore } from '@/stores/settings'
|
|
||||||
import { useNoticationStore } from '@/stores/notifications'
|
|
||||||
|
|
||||||
import { computed, onMounted } from 'vue'
|
|
||||||
|
|
||||||
const settings = useSettingStore()
|
|
||||||
const notifications = useNoticationStore()
|
|
||||||
|
|
||||||
const settingList = computed(() => settings.list)
|
|
||||||
|
|
||||||
onMounted(() => {})
|
|
||||||
|
|
||||||
function updateSetting(name, type, target) {
|
|
||||||
if (type == 'checkbox' || type == 'radio') target.value = target.checked
|
|
||||||
|
|
||||||
settings.set(name, target.value)
|
|
||||||
|
|
||||||
notifications.add({
|
|
||||||
title: 'Settings updated',
|
|
||||||
message: 'Settings have been updated',
|
|
||||||
variant: 'success',
|
|
||||||
time: 5000,
|
|
||||||
closable: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@reference "@/assets/main.css";
|
|
||||||
|
|
||||||
.settings-container {
|
|
||||||
@apply grid
|
|
||||||
grid-cols-1
|
|
||||||
sm:grid-cols-2
|
|
||||||
lg:grid-cols-3
|
|
||||||
items-start
|
|
||||||
gap-4
|
|
||||||
pt-8;
|
|
||||||
|
|
||||||
.setting-block {
|
|
||||||
@apply grid
|
|
||||||
gap-3
|
|
||||||
content-start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue