mirror of
https://github.com/unraid/api.git
synced 2026-01-02 14:40:01 -06:00
feat: install plugin
This commit is contained in:
@@ -135,6 +135,9 @@ if ($display['theme'] === 'black' || $display['theme'] === 'azure') {
|
||||
<div class="ComponentWrapper">
|
||||
<connect-auth></connect-auth>
|
||||
</div>
|
||||
<div class="ComponentWrapper">
|
||||
<connect-download-api-logs></connect-download-api-logs>
|
||||
</div>
|
||||
<div class="ComponentWrapper">
|
||||
<connect-key-actions></connect-key-actions>
|
||||
</div>
|
||||
|
||||
@@ -26,11 +26,11 @@ const button = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="whitespace-normal flex flex-col gap-y-16px">
|
||||
<span v-if="stateData.error" class="text-red font-semibold leading-8 max-w-3xl">
|
||||
{{ stateData.error.heading }}
|
||||
<div class="whitespace-normal flex flex-col gap-y-16px max-w-3xl">
|
||||
<span v-if="stateData.error" class="text-red font-semibold leading-8">
|
||||
{{ stateData.heading }}
|
||||
<br />
|
||||
{{ stateData.error.message }}
|
||||
{{ stateData.message }}
|
||||
</span>
|
||||
<span>
|
||||
<component
|
||||
|
||||
@@ -12,8 +12,8 @@ const downloadUrl = computed(() => new URL(`/graphql/api/logs?apiKey=${apiKey.va
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="whitespace-normal flex flex-col gap-y-16px">
|
||||
<span class="leading-8 max-w-3xl">
|
||||
<div class="whitespace-normal flex flex-col gap-y-16px max-w-3xl">
|
||||
<span class="leading-8">
|
||||
The primary method of support for Unraid Connect is through <a href="https://forums.unraid.net/forum/94-connect-plugin-support/" target="_blank" rel="noopener noreferrer">our forums</a> and <a href="https://discord.gg/unraid" target="_blank" rel="noopener noreferrer">Discord</a>. If you are asked to supply logs, please open a support request on our <a href="https://unraid.net/contact" target="_blank" rel="noopener noreferrer">Contact Page</a> and reply to the email message you receive with your logs attached. The logs may contain sensitive information so do not post them publicly.
|
||||
</span>
|
||||
<span>
|
||||
|
||||
@@ -11,22 +11,21 @@ import '~/assets/main.css';
|
||||
|
||||
export interface Props {
|
||||
server?: Server;
|
||||
showDescription?: boolean;
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showDescription: true,
|
||||
});
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const callbackStore = useCallbackStore();
|
||||
const dropdownStore = useDropdownStore()
|
||||
const serverStore = useServerStore();
|
||||
|
||||
const { dropdownVisible } = storeToRefs(dropdownStore);
|
||||
const { name, description, lanIp } = storeToRefs(serverStore);
|
||||
const { name, description, lanIp, theme } = storeToRefs(serverStore);
|
||||
|
||||
/**
|
||||
* Close dropdown when clicking outside
|
||||
* @fix ignore not working
|
||||
* @note
|
||||
* If in testing you have two variants of the component on a page
|
||||
* the clickOutside will fire twice making it seem like it doesn't work
|
||||
*/
|
||||
const clickOutsideTarget = ref();
|
||||
const clickOutsideIgnoreTarget = ref();
|
||||
@@ -90,7 +89,7 @@ onBeforeMount(() => {
|
||||
|
||||
<div class="relative z-0 flex flex-row items-center justify-end gap-x-16px h-full">
|
||||
<h1 class="text-alpha relative text-18px border-t-0 border-r-0 border-l-0 border-b-2 border-transparent">
|
||||
<template v-if="showDescription">
|
||||
<template v-if="description && theme?.descriptionShow">
|
||||
<span>{{ description }}</span>
|
||||
<span class="text-grey-mid px-8px">•</span>
|
||||
</template>
|
||||
|
||||
@@ -11,9 +11,6 @@ const { pluginInstalled, registered } = storeToRefs(useServerStore());
|
||||
|
||||
const showDefaultContent = computed(() => !showLaunchpad.value);
|
||||
const showLaunchpad = computed(() => pluginInstalled.value && !registered.value);
|
||||
/**
|
||||
* @todo use gsap to animate width between the three dropdown variants
|
||||
*/
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -12,13 +12,13 @@ const showExpireTime = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-y-24px w-full min-w-300px md:min-w-[500px] max-w-4xl mr-8px p-16px md:py-24px md:px-32px lg:px-40px shadow-md rounded-lg">
|
||||
<div class="flex flex-col gap-y-24px w-full min-w-300px md:min-w-[500px] max-w-4xl p-16px">
|
||||
<header class="text-center">
|
||||
<h2 class="text-24px font-semibold">Thank you for installing Connect!</h2>
|
||||
<h3>Sign In to your Unraid.net account to register your server</h3>
|
||||
<UpcUptimeExpire v-if="showExpireTime" class="opacity-75 mt-12px" />
|
||||
</header>
|
||||
<ul class="list-reset flex flex-col gap-y-8px" v-if="stateData.actions">
|
||||
<ul class="list-reset flex flex-col gap-y-8px px-16px" v-if="stateData.actions">
|
||||
<li v-for="action in stateData.actions" :key="action.name">
|
||||
<component
|
||||
:is="action.click ? 'button' : 'a'"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @todo future idea – turn this into a carousel. each feature could have a short video if we ever them
|
||||
*/
|
||||
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
|
||||
import useInstallPlugin from '~/composables/installPlugin';
|
||||
import { usePromoStore } from '~/store/promo';
|
||||
import type { UserProfilePromoFeature } from '~/types/userProfile';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
@@ -53,10 +54,8 @@ const features = ref<UserProfilePromoFeature[]>([
|
||||
},
|
||||
]);
|
||||
|
||||
const installStaging = ref(false);
|
||||
const installPlugin = () => {
|
||||
return console.debug(installStaging.value ? 'dynamix.unraid.net.staging.plg' : 'dynamix.unraid.net.plg');
|
||||
};
|
||||
const staging = ref(false);
|
||||
const { install } = useInstallPlugin();
|
||||
|
||||
const installButtonClasses = 'text-white text-14px text-center w-full flex flex-row items-center justify-center gap-x-8px px-8px py-8px cursor-pointer rounded-md bg-gradient-to-r from-red to-orange hover:from-red/60 hover:to-orange/60 focus:from-red/60 focus:to-orange/60';
|
||||
</script>
|
||||
@@ -91,14 +90,14 @@ const installButtonClasses = 'text-white text-14px text-center w-full flex flex-
|
||||
<div class="w-full max-w-xs flex flex-col gap-y-16px mx-auto">
|
||||
<!-- v-if="devEnv" -->
|
||||
<SwitchGroup as="div" class="flex items-center justify-center">
|
||||
<Switch v-model="installStaging" :class="[installStaging ? 'bg-indigo-600' : 'bg-gray-200', 'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2']">
|
||||
<span aria-hidden="true" :class="[installStaging ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out']" />
|
||||
<Switch v-model="staging" :class="[staging ? 'bg-indigo-600' : 'bg-gray-200', 'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2']">
|
||||
<span aria-hidden="true" :class="[staging ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out']" />
|
||||
</Switch>
|
||||
<SwitchLabel as="span" class="ml-3 text-12px">
|
||||
<span class="font-semibold">Install Staging</span>
|
||||
</SwitchLabel>
|
||||
</SwitchGroup>
|
||||
<button @click="installPlugin()" :class="installButtonClasses">{{ 'Install Connect' }}</button>
|
||||
<button @click="install({ staging, update: false })" :class="installButtonClasses">{{ staging ? 'Install Connect Staging' : 'Install Connect' }}</button>
|
||||
<div>
|
||||
<a
|
||||
href="https://docs.unraid.net/category/unraid-connect"
|
||||
|
||||
48
composables/installPlugin.ts
Normal file
48
composables/installPlugin.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
const useInstallPlugin = () => {
|
||||
const install = (payload = { staging: false, update: false }) => {
|
||||
console.debug('[useInstallPlugin.install]', { payload });
|
||||
try {
|
||||
const file = `https://sfo2.digitaloceanspaces.com/unraid-dl/unraid-api/dynamix.unraid.net${payload?.staging ? '.staging.plg' : '.plg'}`;
|
||||
console.debug('[useInstallPlugin.install]', file);
|
||||
|
||||
if (!payload.update) {
|
||||
// after initial install, the dropdown store looks for this to automatically open the launchpad dropdown
|
||||
sessionStorage.setItem('clickedInstallPlugin', '1');
|
||||
}
|
||||
const modalTitle = payload.update ? 'Updating Connect (beta)' : 'Installing Connect (beta)';
|
||||
// eslint-disable-next-line no-undef
|
||||
// @ts-ignore – `openPlugin` will be included in 6.10.4+ DefaultPageLayout
|
||||
if (typeof openPlugin === 'function') {
|
||||
console.debug('[useInstallPlugin.install] using openPlugin', file);
|
||||
// eslint-disable-next-line no-undef
|
||||
// @ts-ignore
|
||||
openPlugin(
|
||||
`plugin ${payload.update ? 'update' : 'install'} ${file}`,
|
||||
modalTitle,
|
||||
'',
|
||||
'refresh',
|
||||
);
|
||||
} else {
|
||||
console.debug('[useInstallPlugin.install] using openBox', file);
|
||||
// `openBox()` is defined in the webgui's DefaultPageLayout.php and used when openPlugin is not available
|
||||
// eslint-disable-next-line no-undef
|
||||
// @ts-ignore
|
||||
openBox(
|
||||
`/plugins/dynamix.plugin.manager/scripts/plugin&arg1=install&arg2=${file}`,
|
||||
modalTitle,
|
||||
600,
|
||||
900,
|
||||
true,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
install,
|
||||
};
|
||||
};
|
||||
|
||||
export default useInstallPlugin;
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* @see https://www.telerik.com/blogs/how-to-trap-focus-modal-vue-3
|
||||
*/
|
||||
import { customRef } from "vue";
|
||||
import { createFocusTrap } from "focus-trap";
|
||||
import { customRef } from 'vue';
|
||||
import { createFocusTrap } from 'focus-trap';
|
||||
|
||||
const useFocusTrap = focusTrapArgs => {
|
||||
const trapRef = customRef((track, trigger) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useToggle } from '@vueuse/core';
|
||||
import { defineStore, createPinia, setActivePinia } from 'pinia';
|
||||
import { useServerStore } from './server';
|
||||
/**
|
||||
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
|
||||
* @see https://github.com/vuejs/pinia/discussions/1085
|
||||
@@ -7,6 +8,8 @@ import { defineStore, createPinia, setActivePinia } from 'pinia';
|
||||
setActivePinia(createPinia());
|
||||
|
||||
export const useDropdownStore = defineStore('dropdown', () => {
|
||||
const serverStore = useServerStore();
|
||||
|
||||
const dropdownVisible = ref<boolean>(false);
|
||||
|
||||
const dropdownHide = () => dropdownVisible.value = false;
|
||||
@@ -17,6 +20,14 @@ export const useDropdownStore = defineStore('dropdown', () => {
|
||||
console.debug('[dropdownVisible]', newVal, _oldVal);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// automatically open the launchpad dropdown after plugin install on first page load
|
||||
if (serverStore.pluginInstalled && !serverStore.registered && sessionStorage.getItem('clickedInstallPlugin')) {
|
||||
sessionStorage.removeItem('clickedInstallPlugin');
|
||||
dropdownShow();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
dropdownVisible,
|
||||
dropdownHide,
|
||||
|
||||
@@ -445,6 +445,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
regGuid,
|
||||
site,
|
||||
state,
|
||||
theme,
|
||||
uptime,
|
||||
username,
|
||||
// getters
|
||||
|
||||
Reference in New Issue
Block a user