mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-28 03:50:10 +00:00
Compare commits
2 Commits
e5f79c60ae
...
7c808b56f7
Author | SHA1 | Date | |
---|---|---|---|
7c808b56f7 | |||
2057823b7a |
@ -86,7 +86,7 @@ func activitySourceToString(v ActivitySource) string {
|
|||||||
return "anon"
|
return "anon"
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary Get the requested set of activities, Paginated, filtered and sorted.
|
// @Summary Get the requested set of activities, Paginated, filtered and sorted. Is a POST because of some issues I was having, ideally should be a GET.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param GetActivitiesDTO body GetActivitiesDTO true "search parameters"
|
// @Param GetActivitiesDTO body GetActivitiesDTO true "search parameters"
|
||||||
// @Success 200 {object} GetActivitiesRespDTO
|
// @Success 200 {object} GetActivitiesRespDTO
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
lm "github.com/hrfee/jfa-go/logmessages"
|
lm "github.com/hrfee/jfa-go/logmessages"
|
||||||
"github.com/itchyny/timefmt-go"
|
"github.com/itchyny/timefmt-go"
|
||||||
"github.com/lithammer/shortuuid/v3"
|
"github.com/lithammer/shortuuid/v3"
|
||||||
"github.com/timshannon/badgerhold/v4"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -332,22 +331,7 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
invites = append(invites, invite)
|
invites = append(invites, invite)
|
||||||
}
|
}
|
||||||
fullProfileList := app.storage.GetProfiles()
|
|
||||||
profiles := make([]string, len(fullProfileList))
|
|
||||||
if len(profiles) != 0 {
|
|
||||||
defaultProfile := app.storage.GetDefaultProfile()
|
|
||||||
profiles[0] = defaultProfile.Name
|
|
||||||
i := 1
|
|
||||||
if len(fullProfileList) > 1 {
|
|
||||||
app.storage.db.ForEach(badgerhold.Where("Name").Ne(profiles[0]), func(p *Profile) error {
|
|
||||||
profiles[i] = p.Name
|
|
||||||
i++
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resp := getInvitesDTO{
|
resp := getInvitesDTO{
|
||||||
Profiles: profiles,
|
|
||||||
Invites: invites,
|
Invites: invites,
|
||||||
}
|
}
|
||||||
gc.JSON(200, resp)
|
gc.JSON(200, resp)
|
||||||
@ -360,7 +344,7 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
|||||||
// @Failure 500 {object} stringResponse
|
// @Failure 500 {object} stringResponse
|
||||||
// @Router /invites/profile [post]
|
// @Router /invites/profile [post]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Profiles & Settings
|
// @tags Invites
|
||||||
func (app *appContext) SetProfile(gc *gin.Context) {
|
func (app *appContext) SetProfile(gc *gin.Context) {
|
||||||
var req inviteProfileDTO
|
var req inviteProfileDTO
|
||||||
gc.BindJSON(&req)
|
gc.BindJSON(&req)
|
||||||
|
@ -9,7 +9,34 @@ import (
|
|||||||
"github.com/timshannon/badgerhold/v4"
|
"github.com/timshannon/badgerhold/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @Summary Get a list of profiles
|
// @Summary Get the names of all available profile.
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} getProfileNamesDTO
|
||||||
|
// @Router /profiles/names [get]
|
||||||
|
// @Security Bearer
|
||||||
|
// @tags Profiles & Settings
|
||||||
|
func (app *appContext) GetProfileNames(gc *gin.Context) {
|
||||||
|
fullProfileList := app.storage.GetProfiles()
|
||||||
|
profiles := make([]string, len(fullProfileList))
|
||||||
|
if len(profiles) != 0 {
|
||||||
|
defaultProfile := app.storage.GetDefaultProfile()
|
||||||
|
profiles[0] = defaultProfile.Name
|
||||||
|
i := 1
|
||||||
|
if len(fullProfileList) > 1 {
|
||||||
|
app.storage.db.ForEach(badgerhold.Where("Name").Ne(profiles[0]), func(p *Profile) error {
|
||||||
|
profiles[i] = p.Name
|
||||||
|
i++
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp := getProfileNamesDTO{
|
||||||
|
Profiles: profiles,
|
||||||
|
}
|
||||||
|
gc.JSON(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Get all available profiles, indexed by their names.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} getProfilesDTO
|
// @Success 200 {object} getProfilesDTO
|
||||||
// @Router /profiles [get]
|
// @Router /profiles [get]
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param newUserDTO body newUserDTO true "New user request object"
|
// @Param newUserDTO body newUserDTO true "New user request object"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Router /users [post]
|
// @Router /user [post]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Users
|
// @tags Users
|
||||||
func (app *appContext) NewUserFromAdmin(gc *gin.Context) {
|
func (app *appContext) NewUserFromAdmin(gc *gin.Context) {
|
||||||
@ -68,7 +68,7 @@ func (app *appContext) NewUserFromAdmin(gc *gin.Context) {
|
|||||||
// @Param newUserDTO body newUserDTO true "New user request object"
|
// @Param newUserDTO body newUserDTO true "New user request object"
|
||||||
// @Success 200 {object} PasswordValidation
|
// @Success 200 {object} PasswordValidation
|
||||||
// @Failure 400 {object} PasswordValidation
|
// @Failure 400 {object} PasswordValidation
|
||||||
// @Router /newUser [post]
|
// @Router /user/invite [post]
|
||||||
// @tags Users
|
// @tags Users
|
||||||
func (app *appContext) NewUserFromInvite(gc *gin.Context) {
|
func (app *appContext) NewUserFromInvite(gc *gin.Context) {
|
||||||
/*
|
/*
|
||||||
@ -709,7 +709,7 @@ func (app *appContext) SaveAnnounceTemplate(gc *gin.Context) {
|
|||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary Save an announcement as a template for use or editing later.
|
// @Summary Gets the names of each available announcement template.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} getAnnouncementsDTO
|
// @Success 200 {object} getAnnouncementsDTO
|
||||||
// @Router /users/announce [get]
|
// @Router /users/announce [get]
|
||||||
|
14
css/base.css
14
css/base.css
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
--bg-light: #fff;
|
--bg-light: #fff;
|
||||||
--bg-dark: #101010;
|
--bg-dark: #101010;
|
||||||
|
|
||||||
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
|
|
||||||
.light {
|
.light {
|
||||||
@ -26,6 +28,7 @@
|
|||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--settings-section-button-filter: 80%;
|
--settings-section-button-filter: 80%;
|
||||||
|
color-scheme: dark !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark body {
|
.dark body {
|
||||||
@ -456,3 +459,14 @@ input[type="checkbox" i], [class^="ri-"], [class*=" ri-"], .ri-refresh-line:befo
|
|||||||
margin-bottom: -0.5rem;
|
margin-bottom: -0.5rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
section.section:not(.\~neutral) {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.switch input {
|
||||||
|
@apply mr-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
654
html/setup.html
654
html/setup.html
@ -7,7 +7,9 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="max-w-full overflow-x-hidden section">
|
<body class="max-w-full overflow-x-hidden section">
|
||||||
<div id="notification-box"></div>
|
<div id="notification-box"></div>
|
||||||
<div class="top-2 left-2 absolute">
|
<div class="page-container m-2 lg:my-20 lg:mx-64 flex flex-col gap-4">
|
||||||
|
<div class="top-2 inset-x-2 lg:absolute flex flex-row justify-between">
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
||||||
<span class="button ~urge dropdown-button">
|
<span class="button ~urge dropdown-button">
|
||||||
<i class="ri-global-line"></i>
|
<i class="ri-global-line"></i>
|
||||||
@ -19,79 +21,83 @@
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-container m-2 lg:my-20 lg:mx-64" id="page-container">
|
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
|
||||||
<div class="card ~neutral @low mb-2">
|
|
||||||
<div class="row">
|
|
||||||
<img class="banner header" src="banner.svg" alt="jfa-go" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row col flex center">
|
<div class="card sectioned ~neutral @low flex flex-col gap-4 justify-between items-center">
|
||||||
|
<img class="w-[105%] max-w-none" src="banner.svg" alt="jfa-go" />
|
||||||
<span class="heading welcome">{{ .lang.StartPage.welcome }}</span>
|
<span class="heading welcome">{{ .lang.StartPage.welcome }}</span>
|
||||||
</div>
|
<p class="content text-center">{{ .lang.StartPage.pressStart }}</p>
|
||||||
<div class="row col flex center">
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<p class="content my-2">{{ .lang.StartPage.pressStart }}</p>
|
|
||||||
</div>
|
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
|
||||||
<span class="support">{{ .lang.StartPage.httpsNotice }}</span>
|
<span class="support">{{ .lang.StartPage.httpsNotice }}</span>
|
||||||
<span class="button ~urge @low next">{{ .lang.StartPage.start }}</span>
|
<span class="button ~urge @low next">{{ .lang.StartPage.start }}</span>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.Language.title }}</span>
|
<span class="heading">{{ .lang.Language.title }}</span>
|
||||||
<p class="content my-2" id="language-description"></p>
|
<p class="content" id="language-description"></p>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Language.defaultAdminLang }}</span>
|
<span>{{ .lang.Language.defaultAdminLang }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="ui-language-admin">
|
<select id="ui-language-admin">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Language.defaultFormLang }}</span>
|
<span>{{ .lang.Language.defaultFormLang }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="ui-language-form">
|
<select id="ui-language-form">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Language.defaultEmailLang }}</span>
|
<span>{{ .lang.Language.defaultEmailLang }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="email-language">
|
<select id="email-language">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.General.title }}</span>
|
<span class="heading">{{ .lang.General.title }}</span>
|
||||||
<div class="row">
|
<div class="flex flex-row gap-2 justify-between">
|
||||||
<div class="col">
|
<div class="flex flex-col gap-2">
|
||||||
<label class="label">
|
<div class="flex flex-row gap-2 justify-between">
|
||||||
<span class="mt-4">{{ .lang.General.listenAddress }}</span>
|
<label class="label flex flex-col gap-2 grow">
|
||||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="ui-host" value="0.0.0.0">
|
<span>{{ .lang.General.listenAddress }}</span>
|
||||||
|
<input type="url" class="input ~neutral @low" id="ui-host" value="0.0.0.0">
|
||||||
</label>
|
</label>
|
||||||
<label class="row switch">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="advanced-tls"><span>{{ .lang.General.useHTTPS }}</span>
|
<span>{{ .lang.Strings.port }}</span>
|
||||||
|
<input type="number" class="input ~neutral @low" id="ui-port" value="8056">
|
||||||
</label>
|
</label>
|
||||||
<p class="support mb-2 mt-1">{{ .lang.General.useHTTPSNotice }}</p>
|
</div>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.General.pathToCertificate }}</span>
|
<div class="switch"><input type="checkbox" id="advanced-tls"><span>{{ .lang.General.useHTTPS }}</span></div>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_cert">
|
<p class="support">{{ .lang.General.useHTTPSNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.General.pathToKeyFile }}</span>
|
<span>{{ .lang.General.pathToCertificate }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_key">
|
<input type="text" class="input ~neutral @low" id="advanced-tls_cert">
|
||||||
|
</label>
|
||||||
|
<label class="label flex flex-col gap-2">
|
||||||
|
<span>{{ .lang.General.pathToKeyFile }}</span>
|
||||||
|
<input type="text" class="input ~neutral @low" id="advanced-tls_key">
|
||||||
</label>
|
</label>
|
||||||
<span class="heading">{{ .lang.Updates.title }}</span>
|
<span class="heading">{{ .lang.Updates.title }}</span>
|
||||||
<p class="content my-2" id="updates-description"></p>
|
<p class="content" id="updates-description"></p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="updates-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="updates-enabled" checked><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span>{{ .lang.Updates.updateChannel }}</span>
|
<span>{{ .lang.Updates.updateChannel }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="updates-channel">
|
<select id="updates-channel">
|
||||||
<option value="stable">{{ .lang.Updates.stable }}</option>
|
<option value="stable">{{ .lang.Updates.stable }}</option>
|
||||||
<option value="unstable">{{ .lang.Updates.unstable }}</option>
|
<option value="unstable">{{ .lang.Updates.unstable }}</option>
|
||||||
@ -99,28 +105,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="flex flex-col gap-2">
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.port }}</span>
|
<span>{{ .lang.General.httpsPort }}</span>
|
||||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="ui-port" value="8056">
|
<input type="number" class="input ~neutral @low" id="advanced-tls_port" value="8057">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.General.httpsPort }}</span>
|
<span>{{ .lang.General.urlBase }} ({{ .lang.Strings.optional }})</span>
|
||||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_port" value="8057">
|
<input type="text" class="input ~neutral @low" id="ui-url_base" placeholder="/mysubfolder">
|
||||||
|
<p class="support">{{ .lang.General.urlBaseNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.General.urlBase }} ({{ .lang.Strings.optional }})</span>
|
<span>{{ .lang.General.externalURL }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="ui-url_base" placeholder="/mysubfolder">
|
<input type="text" class="input ~neutral @low" id="ui-jfa_url" placeholder="https://jellyf.in/mysubfolder">
|
||||||
<p class="support mb-2 mt-1">{{ .lang.General.urlBaseNotice }}</p>
|
<p class="support">{{ .lang.General.externalURLNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.General.externalURL }}</span>
|
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="ui-jfa_url" placeholder="https://jellyf.in/mysubfolder">
|
|
||||||
<p class="support mb-2 mt-1">{{ .lang.General.externalURLNotice }}</p>
|
|
||||||
</label>
|
|
||||||
<label class="label">
|
|
||||||
<span>{{ .lang.Strings.theme }}</span>
|
<span>{{ .lang.Strings.theme }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="ui-theme">
|
<select id="ui-theme">
|
||||||
<option value="Jellyfin (Dark)">{{ .lang.General.darkTheme }}</option>
|
<option value="Jellyfin (Dark)">{{ .lang.General.darkTheme }}</option>
|
||||||
<option value="Default (Light)">{{ .lang.General.lightTheme }}</option>
|
<option value="Default (Light)">{{ .lang.General.lightTheme }}</option>
|
||||||
@ -128,208 +130,221 @@
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<span class="heading">{{ .lang.Proxy.title }}</span>
|
<span class="heading">{{ .lang.Proxy.title }}</span>
|
||||||
<p class="content my-2" id="proxy-description">{{ .lang.Proxy.description }}</p>
|
<p class="content" id="proxy-description">{{ .lang.Proxy.description }}</p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="advanced-proxy"><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="advanced-proxy"><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span>{{ .lang.Proxy.protocol }}</span>
|
<span>{{ .lang.Proxy.protocol }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="advanced-proxy_protocol">
|
<select id="advanced-proxy_protocol">
|
||||||
<option value="http">HTTP</option>
|
<option value="http">HTTP</option>
|
||||||
<option value="socks">SOCKS5</option>
|
<option value="socks">SOCKS5</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Proxy.address }}</span>
|
<span>{{ .lang.Proxy.address }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-proxy_address">
|
<input type="text" class="input ~neutral @low" id="advanced-proxy_address">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.username }}</span>
|
<span>{{ .lang.Strings.username }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-proxy_user">
|
<input type="text" class="input ~neutral @low" id="advanced-proxy_user">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.password }}</span>
|
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-proxy_password">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
|
||||||
<span class="heading">{{ .lang.Login.title }}</span>
|
|
||||||
<p class="content my-2">{{ .lang.Login.description }}</p>
|
|
||||||
<div class="pl-4">
|
|
||||||
<label class="row switch pb-4">
|
|
||||||
<input type="radio" class="mr-2" name="ui-jellyfin_login" value="true" checked><span>{{ .lang.Login.authorizeWithJellyfin }}</span>
|
|
||||||
</label>
|
|
||||||
<label class="row switch pl-4 pb-4">
|
|
||||||
<input type="checkbox" class="mr-2" id="ui-admin_only" checked><span>{{ .lang.Login.adminOnly }}</span>
|
|
||||||
</label>
|
|
||||||
<label class="row switch pl-4 pb-2">
|
|
||||||
<input type="checkbox" class="mr-2" id="ui-allow_all"><span>{{ .lang.Login.allowAll }}</span>
|
|
||||||
</label>
|
|
||||||
<p class="support pb-4 pl-4 mt-1" id="description-ui-allow_all">{{ .lang.Login.allowAllDescription }}</p>
|
|
||||||
<label class="row switch pb-4">
|
|
||||||
<input type="radio" class="mr-2" name="ui-jellyfin_login" value="false"><span>{{ .lang.Login.authorizeManual }}</span>
|
|
||||||
</label>
|
|
||||||
<p class="support pb-4 pl-4 mt-1">{{ .lang.Login.authorizeManualUserPageNotice }}</p>
|
|
||||||
</div>
|
|
||||||
<div id="login-manual">
|
|
||||||
<label class="label">
|
|
||||||
<span class="mt-4">{{ .lang.Strings.username }}</span>
|
|
||||||
<input type="text" id="ui-username" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.username }}">
|
|
||||||
</label>
|
|
||||||
<label class="label">
|
|
||||||
<span>{{ .lang.Strings.password }}</span>
|
<span>{{ .lang.Strings.password }}</span>
|
||||||
<input type="password" id="ui-password" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.password }}">
|
<input type="text" class="input ~neutral @low" id="advanced-proxy_password">
|
||||||
</label>
|
|
||||||
<label class="label">
|
|
||||||
<span>{{ .lang.Strings.emailAddress }} ({{ .lang.Strings.optional }})</span>
|
|
||||||
<input type="email" id="ui-email" class="input ~neutral @low mt-4" placeholder="email@address">
|
|
||||||
<span class="support mb-2 mt-1">{{ .lang.Login.emailNotice }}</span>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
|
<span class="heading">{{ .lang.Login.title }}</span>
|
||||||
|
<p class="content">{{ .lang.Login.description }}</p>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<label class="label flex flex-col gap-2">
|
||||||
|
<div class="switch"><input type="radio" name="ui-jellyfin_login" value="true" checked><span>{{ .lang.Login.authorizeWithJellyfin }}</span></div>
|
||||||
|
</label>
|
||||||
|
<div class="pl-4 flex flex-col gap-2">
|
||||||
|
<label class="label flex flex-col gap-2">
|
||||||
|
<div class="switch"><input type="checkbox" id="ui-admin_only" checked><span>{{ .lang.Login.adminOnly }}</span></div>
|
||||||
|
</label>
|
||||||
|
<label class="label flex flex-col gap-2">
|
||||||
|
<div class="switch"><input type="checkbox" id="ui-allow_all"><span>{{ .lang.Login.allowAll }}</span></div>
|
||||||
|
<p class="support" id="description-ui-allow_all">{{ .lang.Login.allowAllDescription }}</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<label class="label flex flex-col gap-2">
|
||||||
|
<div class="switch"><input type="radio" name="ui-jellyfin_login" value="false"><span>{{ .lang.Login.authorizeManual }}</span></div>
|
||||||
|
</label>
|
||||||
|
<p class="support">{{ .lang.Login.authorizeManualUserPageNotice }}</p>
|
||||||
|
</div>
|
||||||
|
<div class ="flex flex-col gap-2" id="login-manual">
|
||||||
|
<label class="label flex flex-col gap-2">
|
||||||
|
<span>{{ .lang.Strings.username }}</span>
|
||||||
|
<input type="text" id="ui-username" class="input ~neutral @low" placeholder="{{ .lang.Strings.username }}">
|
||||||
|
</label>
|
||||||
|
<label class="label flex flex-col gap-2">
|
||||||
|
<span>{{ .lang.Strings.password }}</span>
|
||||||
|
<input type="password" id="ui-password" class="input ~neutral @low" placeholder="{{ .lang.Strings.password }}">
|
||||||
|
</label>
|
||||||
|
<label class="label flex flex-col gap-2">
|
||||||
|
<span>{{ .lang.Strings.emailAddress }} ({{ .lang.Strings.optional }})</span>
|
||||||
|
<input type="email" id="ui-email" class="input ~neutral @low" placeholder="email@address">
|
||||||
|
<span class="support">{{ .lang.Login.emailNotice }}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.JellyfinEmby.title }}</span>
|
<span class="heading">{{ .lang.JellyfinEmby.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.JellyfinEmby.description }}</p>
|
<p class="content">{{ .lang.JellyfinEmby.description }}</p>
|
||||||
<div class="row">
|
<div class="flex flex-row gap-2 justify-between">
|
||||||
<div class="col">
|
<div class="flex flex-col gap-2 grow">
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span>{{ .lang.Strings.serverType }}</span>
|
<span>{{ .lang.Strings.serverType }}</span>
|
||||||
<div class="select ~neutral @low mt-4">
|
<div class="select ~neutral @low">
|
||||||
<select id="jellyfin-type">
|
<select id="jellyfin-type">
|
||||||
<option value="jellyfin">Jellyfin</option>
|
<option value="jellyfin">Jellyfin</option>
|
||||||
<option value="emby">Emby</option>
|
<option value="emby">Emby</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.embyNotice }}</p>
|
<p class="support">{{ .lang.JellyfinEmby.embyNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.JellyfinEmby.replaceJellyfin }} ({{ .lang.Strings.optional }})</span>
|
<span>{{ .lang.JellyfinEmby.replaceJellyfin }} ({{ .lang.Strings.optional }})</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="jellyfin-substitute_jellyfin_strings">
|
<input type="text" class="input ~neutral @low" id="jellyfin-substitute_jellyfin_strings">
|
||||||
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.replaceJellyfinNotice }}</p>
|
<p class="support">{{ .lang.JellyfinEmby.replaceJellyfinNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.username }}</span>
|
<span>{{ .lang.Strings.username }}</span>
|
||||||
<input type="text" id="jellyfin-username" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.username }}">
|
<input type="text" id="jellyfin-username" class="input ~neutral @low" placeholder="{{ .lang.Strings.username }}">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span>{{ .lang.Strings.password }}</span>
|
<span>{{ .lang.Strings.password }}</span>
|
||||||
<input type="password" id="jellyfin-password" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.password }}">
|
<input type="password" id="jellyfin-password" class="input ~neutral @low" placeholder="{{ .lang.Strings.password }}">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="flex flex-col gap-2 grow ">
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.internal }})</span>
|
<span>{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.internal }})</span>
|
||||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="jellyfin-server" placeholder="http://jellyf.in:80">
|
<input type="url" class="input ~neutral @low" id="jellyfin-server" placeholder="http://jellyf.in:80">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.external }})</span>
|
<span>{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.external }})</span>
|
||||||
<input type="url" class="input ~neutral @low mt-4" id="jellyfin-public_server" placeholder="https://jellyf.in">
|
<input type="url" class="input ~neutral @low" id="jellyfin-public_server" placeholder="https://jellyf.in">
|
||||||
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.addressExternalNotice }}</p>
|
<p class="support">{{ .lang.JellyfinEmby.addressExternalNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div class="flex flex-row gap-2">
|
||||||
<span class="button ~urge @low" id="jellyfin-test-connection">{{ .lang.JellyfinEmby.testConnection }}</span>
|
<span class="button ~urge @low" id="jellyfin-test-connection">{{ .lang.JellyfinEmby.testConnection }}</span>
|
||||||
<span class="button ~urge @low next" disabled>{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next" disabled>{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.Ombi.title }}</span>
|
<span class="heading">{{ .lang.Ombi.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.Ombi.description }}</p>
|
<p class="content">{{ .lang.Ombi.description }}</p>
|
||||||
<aside class="aside ~warning my-2" id="ombi-stability-warning">{{ .lang.Ombi.stabilityWarning }}</aside>
|
<aside class="aside ~warning" id="ombi-stability-warning">{{ .lang.Ombi.stabilityWarning }}</aside>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="ombi-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="ombi-enabled"><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.serverAddress }}</span>
|
<span>{{ .lang.Strings.serverAddress }}</span>
|
||||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="ombi-server" placeholder="ombi.jellyf.in">
|
<input type="url" class="input ~neutral @low" id="ombi-server" placeholder="ombi.jellyf.in">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.apiKey }}</span>
|
<span>{{ .lang.Strings.apiKey }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="ombi-api_key">
|
<input type="text" class="input ~neutral @low" id="ombi-api_key">
|
||||||
<p class="support mb-2 mt-1">{{ .lang.Ombi.apiKeyNotice }}</p>
|
<p class="support">{{ .lang.Ombi.apiKeyNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<span class="heading">{{ .lang.Jellyseerr.title }}</span>
|
<span class="heading">{{ .lang.Jellyseerr.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.Jellyseerr.description }}</p>
|
<p class="content">{{ .lang.Jellyseerr.description }}</p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="jellyseerr-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="jellyseerr-enabled"><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.serverAddress }}</span>
|
<span>{{ .lang.Strings.serverAddress }}</span>
|
||||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="jellyseerr-server" placeholder="https://jellyseerr.jellyf.in:5055">
|
<input type="url" class="input ~neutral @low" id="jellyseerr-server" placeholder="https://jellyseerr.jellyf.in:5055">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.apiKey }}</span>
|
<span>{{ .lang.Strings.apiKey }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="jellyseerr-api_key">
|
<input type="text" class="input ~neutral @low" id="jellyseerr-api_key">
|
||||||
</label>
|
</label>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="jellyseerr-import_existing"><span>{{ .lang.Jellyseerr.importExisting }}</span>
|
<div class="switch"><input type="checkbox" id="jellyseerr-import_existing" checked><span>{{ .lang.Jellyseerr.importExisting }}</span></div>
|
||||||
<p class="support mb-2 mt-1">{{ .lang.Jellyseerr.importExistingDescription }}</p>
|
<p class="support">{{ .lang.Jellyseerr.importExistingDescription }}</p>
|
||||||
</label>
|
</label>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.UserPage.title }}</span>
|
<span class="heading">{{ .lang.UserPage.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.UserPage.description }}</p>
|
<p class="content">{{ .lang.UserPage.description }}</p>
|
||||||
<p class="content my-2">{{ .lang.UserPage.customizeMessages }}</p>
|
<p class="content">{{ .lang.UserPage.customizeMessages }}</p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="userpage-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="userpage-enabled"><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
|
<p class="support">{{ .lang.UserPage.requiredSettings }}</p>
|
||||||
</label>
|
</label>
|
||||||
<p class="support mb-1 mt-1">{{ .lang.UserPage.requiredSettings }}</p>
|
</section>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.Messages.title }}</span>
|
<span class="heading">{{ .lang.Messages.title }}</span>
|
||||||
<p class="content my-2" id="messages-description"></p>
|
<p class="content" id="messages-description"></p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="messages-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="messages-enabled" checked><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Email.dateFormat }}</span>
|
<span>{{ .lang.Email.dateFormat }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="email-date_format" value="%d/%m/%y">
|
<input type="text" class="input ~neutral @low" id="email-date_format" value="%d/%m/%y">
|
||||||
<p class="support mb-2 mt-1" id="email-dateformat-notice"></p>
|
<p class="support" id="email-dateformat-notice"></p>
|
||||||
</label>
|
</label>
|
||||||
<div>
|
<div class="flex flex-col gap-2">
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="radio" class="mr-2" name="email-24h" value="true" checked><span>{{ .lang.Strings.time24h }}</span>
|
<div class="switch"><input type="radio" class="mr-2" name="email-24h" value="true" checked><span>{{ .lang.Strings.time24h }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="radio" class="mr-2" name="email-24h" value="false"><span>{{ .lang.Strings.time12h }}</span>
|
<div class="switch"><input type="radio" class="mr-2" name="email-24h" value="false"><span>{{ .lang.Strings.time12h }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="email-sect">
|
<div id="email-sect" class="flex flex-row gap-2 justify-between">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
<span class="heading">{{ .lang.Email.title }}</span>
|
<span class="heading">{{ .lang.Email.title }}</span>
|
||||||
<p class="content my-2" id="email-description"></p>
|
<p class="content" id="email-description"></p>
|
||||||
<div class="row">
|
<label class="label flex flex-col gap-2">
|
||||||
<div class="col">
|
|
||||||
<label class="label">
|
|
||||||
<span>{{ .lang.Email.method }}</span>
|
<span>{{ .lang.Email.method }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="email-method">
|
<select id="email-method">
|
||||||
<option value="">{{ .lang.Strings.disabled }}</option>
|
<option value="">{{ .lang.Strings.disabled }}</option>
|
||||||
<option value="smtp">SMTP</option>
|
<option value="smtp">SMTP</option>
|
||||||
@ -337,218 +352,225 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label class="row switch">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="email-no_username"><span>{{ .lang.Email.useEmailAsUsername }}</span>
|
<div class="switch"><input type="checkbox" id="email-no_username"><span>{{ .lang.Email.useEmailAsUsername }}</span></div>
|
||||||
<p class="support mb-2 mt-1">{{ .lang.Email.useEmailAsUsernameNotice }}</p>
|
<p class="support">{{ .lang.Email.useEmailAsUsernameNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Email.fromAddress }}</span>
|
<span>{{ .lang.Email.fromAddress }}</span>
|
||||||
<input type="email" class="input ~neutral @low mt-4 mb-2" id="email-address" placeholder="mail@jellyf.in">
|
<input type="email" class="input ~neutral @low" id="email-address" placeholder="mail@jellyf.in">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Email.senderName }}</span>
|
<span>{{ .lang.Email.senderName }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="email-from" value="Jellyfin">
|
<input type="text" class="input ~neutral @low" id="email-from" value="Jellyfin">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div id="email-smtp" class="flex flex-col gap-2 min-w-[40%]">
|
||||||
<div id="email-smtp">
|
<p class="text-2xl font-semibold">SMTP</p>
|
||||||
<p class="text-2xl font-semibold mb-2">SMTP</p>
|
<label class="label flex flex-col gap-2">
|
||||||
<label class="label">
|
|
||||||
<span>{{ .lang.Email.encryption }}</span>
|
<span>{{ .lang.Email.encryption }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="smtp-encryption">
|
<select id="smtp-encryption">
|
||||||
<option value="starttls">STARTTLS ({{ .lang.Strings.port }} 587)</option>
|
<option value="starttls">STARTTLS ({{ .lang.Strings.port }} 587)</option>
|
||||||
<option value="ssl_tls">SSL/TLS ({{ .lang.Strings.port }} 465)</option>
|
<option value="ssl_tls">SSL/TLS ({{ .lang.Strings.port }} 465)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.serverAddress }}</span>
|
<span>{{ .lang.Strings.serverAddress }}</span>
|
||||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="smtp-server" placeholder="smtp.jellyf.in">
|
<input type="url" class="input ~neutral @low" id="smtp-server" placeholder="smtp.jellyf.in">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.port }}</span>
|
<span>{{ .lang.Strings.port }}</span>
|
||||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="smtp-port" placeholder="587">
|
<input type="number" class="input ~neutral @low" id="smtp-port" placeholder="587">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.username }}</span>
|
<span>{{ .lang.Strings.username }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="smtp-username">
|
<input type="text" class="input ~neutral @low" id="smtp-username">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.password }}</span>
|
<span>{{ .lang.Strings.password }}</span>
|
||||||
<input type="password" class="input ~neutral @low mt-4 mb-2" id="smtp-password">
|
<input type="password" class="input ~neutral @low" id="smtp-password">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="email-mailgun">
|
<div id="email-mailgun" class="flex flex-col gap-2 min-w-[40%]">
|
||||||
<p class="text-2xl font-semibold mb-2">Mailgun</p>
|
<p class="text-2xl font-semibold">Mailgun</p>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Email.mailgunApiURL }}</span>
|
<span>{{ .lang.Email.mailgunApiURL }}</span>
|
||||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="mailgun-api_url" placeholder="https://api.eu.mailgun.net/v3/mail.jellyf.in/messages">
|
<input type="url" class="input ~neutral @low" id="mailgun-api_url" placeholder="https://api.eu.mailgun.net/v3/mail.jellyf.in/messages">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.apiKey }}</span>
|
<span>{{ .lang.Strings.apiKey }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="mailgun-api_key">
|
<input type="text" class="input ~neutral @low" id="mailgun-api_key">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</div>
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused related-to-email">
|
<div class="card sectioned ~neutral @low unfocused related-to-email">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.Notifications.title }}</span>
|
<span class="heading">{{ .lang.Notifications.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.Notifications.description }}</p>
|
<p class="content">{{ .lang.Notifications.description }}</p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="notifications-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="notifications-enabled"><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<span class="heading">{{ .lang.WelcomeEmails.title }}</span>
|
<span class="heading">{{ .lang.WelcomeEmails.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.WelcomeEmails.description }}</p>
|
<p class="content">{{ .lang.WelcomeEmails.description }}</p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="welcome_email-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="welcome_email-enabled"><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
|
<span>{{ .lang.Strings.emailSubject }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="welcome_email-subject" placeholder="{{ .emailLang.WelcomeEmail.title }}">
|
<input type="text" class="input ~neutral @low" id="welcome_email-subject" placeholder="{{ .emailLang.WelcomeEmail.title }}">
|
||||||
</label>
|
</label>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused related-to-email">
|
<div class="card sectioned ~neutral @low unfocused related-to-email">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.InviteEmails.title }}</span>
|
<span class="heading">{{ .lang.InviteEmails.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.InviteEmails.description }}</p>
|
<p class="content">{{ .lang.InviteEmails.description }}</p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="invite_emails-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="invite_emails-enabled"><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
|
<span>{{ .lang.Strings.emailSubject }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="invite_emails-subject" placeholder="{{ .emailLang.InviteEmail.title }}">
|
<input type="text" class="input ~neutral @low" id="invite_emails-subject" placeholder="{{ .emailLang.InviteEmail.title }}">
|
||||||
</label>
|
</label>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div id="password-resets" class="card ~neutral @low mb-2 unfocused related-to-email">
|
<div id="password-resets" class="card sectioned ~neutral @low unfocused related-to-email">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.PasswordResets.title }}</span>
|
<span class="heading">{{ .lang.PasswordResets.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.PasswordResets.description }}</p>
|
<p class="content">{{ .lang.PasswordResets.description }}</p>
|
||||||
<p class="content my-2" id="password_resets-more-info">{{ .lang.PasswordResets.moreInfo }}</p>
|
<p class="content" id="password_resets-more-info">{{ .lang.PasswordResets.moreInfo }}</p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="password_resets-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="password_resets-enabled"><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.PasswordResets.pathToJellyfin }}</span>
|
<span>{{ .lang.PasswordResets.pathToJellyfin }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="password_resets-watch_directory" placeholder="/config/jellyfin">
|
<input type="text" class="input ~neutral @low" id="password_resets-watch_directory" placeholder="/config/jellyfin">
|
||||||
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.pathToJellyfinNotice }}</p>
|
<p class="support">{{ .lang.PasswordResets.pathToJellyfinNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="switch">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="password_resets-link_reset"><span>{{ .lang.PasswordResets.resetLinks }}</span>
|
<div class="switch"><input type="checkbox" id="password_resets-link_reset"><span>{{ .lang.PasswordResets.resetLinks }}</span></div>
|
||||||
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.resetLinksNotice }} {{ .lang.PasswordResets.resetLinksRequiredForUserPage }}</p>
|
<p class="support">{{ .lang.PasswordResets.resetLinksNotice }} {{ .lang.PasswordResets.resetLinksRequiredForUserPage }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="switch">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="password_resets-set_password"><span>{{ .lang.PasswordResets.setPassword }}</span>
|
<div class="switch"><input type="checkbox" id="password_resets-set_password"><span>{{ .lang.PasswordResets.setPassword }}</span></div>
|
||||||
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.setPasswordNotice }}</p>
|
<p class="support">{{ .lang.PasswordResets.setPasswordNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<p class="mt-4">{{ .lang.PasswordResets.resetLinksLanguage }}</p>
|
<span>{{ .lang.PasswordResets.resetLinksLanguage }}</span>
|
||||||
<div class="select ~neutral @low mt-4 mb-2">
|
<div class="select ~neutral @low">
|
||||||
<select id="password_resets-language">
|
<select id="password_resets-language">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label class="row label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
|
<span>{{ .lang.Strings.emailSubject }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="password_resets-subject" placeholder="{{ .emailLang.PasswordReset.title }}">
|
<input type="text" class="input ~neutral @low" id="password_resets-subject" placeholder="{{ .emailLang.PasswordReset.title }}">
|
||||||
</label>
|
</label>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.PasswordValidation.title }}</span>
|
<span class="heading">{{ .lang.PasswordValidation.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.PasswordValidation.description }}</p>
|
<p class="content">{{ .lang.PasswordValidation.description }}</p>
|
||||||
<label class="row switch pb-4">
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="checkbox" class="mr-2" id="password_validation-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
|
<div class="switch"><input type="checkbox" id="password_validation-enabled" checked><span>{{ .lang.Strings.enabled }}</span></div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.PasswordValidation.length }}</span>
|
<span>{{ .lang.PasswordValidation.length }}</span>
|
||||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-min_length" value="8">
|
<input type="number" class="input ~neutral @low" id="password_validation-min_length" value="8">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.PasswordValidation.uppercase }}</span>
|
<span>{{ .lang.PasswordValidation.uppercase }}</span>
|
||||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-upper" value="1">
|
<input type="number" class="input ~neutral @low" id="password_validation-upper" value="1">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.PasswordValidation.lowercase }}</span>
|
<span>{{ .lang.PasswordValidation.lowercase }}</span>
|
||||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-lower" value="0">
|
<input type="number" class="input ~neutral @low" id="password_validation-lower" value="0">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.PasswordValidation.numbers }}</span>
|
<span>{{ .lang.PasswordValidation.numbers }}</span>
|
||||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-number" value="0">
|
<input type="number" class="input ~neutral @low" id="password_validation-number" value="0">
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.PasswordValidation.special }}</span>
|
<span>{{ .lang.PasswordValidation.special }}</span>
|
||||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-special" value="0">
|
<input type="number" class="input ~neutral @low" id="password_validation-special" value="0">
|
||||||
</label>
|
</label>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
|
<section class="section flex flex-col gap-2 justify-between">
|
||||||
<span class="heading">{{ .lang.HelpMessages.title }}</span>
|
<span class="heading">{{ .lang.HelpMessages.title }}</span>
|
||||||
<p class="content my-2">{{ .lang.HelpMessages.description }}</p>
|
<p class="content">{{ .lang.HelpMessages.description }}</p>
|
||||||
<label class="label">
|
<p class="content">{{ .lang.HelpMessages.markdownMessageNotice }}</p>
|
||||||
<span class="mt-4">{{ .lang.HelpMessages.contactMessage }}</span>
|
<label class="label flex flex-col gap-2">
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="ui-contact_message">
|
<span>{{ .lang.HelpMessages.contactMessage }}</span>
|
||||||
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.contactMessageNotice }}</p>
|
<input type="text" class="input ~neutral @low" id="ui-contact_message">
|
||||||
|
<p class="support">{{ .lang.HelpMessages.contactMessageNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.HelpMessages.helpMessage }}</span>
|
<span>{{ .lang.HelpMessages.helpMessage }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="ui-help_message">
|
<input type="text" class="input ~neutral @low" id="ui-help_message">
|
||||||
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.helpMessageNotice }}</p>
|
<p class="support">{{ .lang.HelpMessages.helpMessageNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label">
|
<label class="label flex flex-col gap-2">
|
||||||
<span class="mt-4">{{ .lang.HelpMessages.successMessage }}</span>
|
<span>{{ .lang.HelpMessages.successMessage }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="ui-success_message">
|
<input type="text" class="input ~neutral @low" id="ui-success_message">
|
||||||
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.successMessageNotice }}</p>
|
<p class="support">{{ .lang.HelpMessages.successMessageNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="label related-to-email">
|
<label class="label related-to-email">
|
||||||
<span class="mt-4">{{ .lang.HelpMessages.emailMessage }}</span>
|
<span>{{ .lang.HelpMessages.emailMessage }}</span>
|
||||||
<input type="text" class="input ~neutral @low mt-4" id="email-message">
|
<input type="text" class="input ~neutral @low" id="email-message">
|
||||||
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.emailMessageNotice }}</p>
|
<p class="support">{{ .lang.HelpMessages.emailMessageNotice }}</p>
|
||||||
</label>
|
</label>
|
||||||
<section class="section ~neutral banner footer flex flex-row justify-between middle">
|
</section>
|
||||||
|
<section class="section w-full ~neutral footer flex flex-row justify-between items-center">
|
||||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div>
|
<div>
|
||||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low mb-2 unfocused">
|
<div class="card sectioned ~neutral @low unfocused">
|
||||||
<div class="row col flex center">
|
<section class="section flex flex-col gap-2 justify-center items-center">
|
||||||
<span class="heading">{{ .lang.EndPage.finished }}</span>
|
<span class="heading">{{ .lang.EndPage.finished }}</span>
|
||||||
</div>
|
<p class="content">{{ .lang.EndPage.restartMessage }}</p>
|
||||||
<div class="row col flex center">
|
</section>
|
||||||
<p class="content my-2">{{ .lang.EndPage.restartMessage }}</p>
|
<section class="section w-full ~neutral footer flex flex-row justify-center items-center gap-2">
|
||||||
</div>
|
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||||
<div class="row col flex center">
|
|
||||||
<span class="button ~neutral @low back mr-4">{{ .lang.Strings.back }}</span>
|
|
||||||
<span class="button ~urge @low" id="restart">{{ .lang.Strings.submit }}</span>
|
<span class="button ~urge @low" id="restart">{{ .lang.Strings.submit }}</span>
|
||||||
<span class="button ~urge @low unfocused" id="refresh">{{ .lang.EndPage.refreshPage }}</span>
|
<span class="button ~urge @low unfocused" id="refresh">{{ .lang.EndPage.refreshPage }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -102,6 +102,7 @@
|
|||||||
"jellyseerr": {
|
"jellyseerr": {
|
||||||
"title": "Jellyseerr",
|
"title": "Jellyseerr",
|
||||||
"description": "Jellyseerr is an alternative to Ombi, and integrates with jfa-go slightly better. Again, after setup is finished, go to Settings to create a profile and add a template for new Jellyseerr accounts.",
|
"description": "Jellyseerr is an alternative to Ombi, and integrates with jfa-go slightly better. Again, after setup is finished, go to Settings to create a profile and add a template for new Jellyseerr accounts.",
|
||||||
|
"importExisting": "Import existing users",
|
||||||
"importExistingDescription": "If enabled, your existing users will have contact details and preferences from jfa-go synchronized."
|
"importExistingDescription": "If enabled, your existing users will have contact details and preferences from jfa-go synchronized."
|
||||||
},
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
@ -164,6 +165,7 @@
|
|||||||
"helpMessages": {
|
"helpMessages": {
|
||||||
"title": "Help Messages",
|
"title": "Help Messages",
|
||||||
"description": "These messages will display in the account creation page and in some emails.",
|
"description": "These messages will display in the account creation page and in some emails.",
|
||||||
|
"markdownMessageNotice": "Contents of some emails, pages and messages can be customized further with markdown in Settings.",
|
||||||
"contactMessage": "Contact Message",
|
"contactMessage": "Contact Message",
|
||||||
"contactMessageNotice": "Displays at the bottom of all pages except admin.",
|
"contactMessageNotice": "Displays at the bottom of all pages except admin.",
|
||||||
"helpMessage": "Help Message",
|
"helpMessage": "Help Message",
|
||||||
|
@ -88,6 +88,10 @@ type getProfilesDTO struct {
|
|||||||
DefaultProfile string `json:"default_profile"`
|
DefaultProfile string `json:"default_profile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type getProfileNamesDTO struct {
|
||||||
|
Profiles []string `json:"profiles"` // List of profiles (name only)
|
||||||
|
}
|
||||||
|
|
||||||
type profileChangeDTO struct {
|
type profileChangeDTO struct {
|
||||||
Name string `json:"name" example:"DefaultProfile" binding:"required"` // Name of the profile
|
Name string `json:"name" example:"DefaultProfile" binding:"required"` // Name of the profile
|
||||||
}
|
}
|
||||||
@ -123,7 +127,6 @@ type inviteDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type getInvitesDTO struct {
|
type getInvitesDTO struct {
|
||||||
Profiles []string `json:"profiles"` // List of profiles (name only)
|
|
||||||
Invites []inviteDTO `json:"invites"` // List of invites
|
Invites []inviteDTO `json:"invites"` // List of invites
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
router.GET(p+"/lang/:page/:file", app.ServeLang)
|
router.GET(p+"/lang/:page/:file", app.ServeLang)
|
||||||
router.GET(p+"/token/login", app.getTokenLogin)
|
router.GET(p+"/token/login", app.getTokenLogin)
|
||||||
router.GET(p+"/token/refresh", app.getTokenRefresh)
|
router.GET(p+"/token/refresh", app.getTokenRefresh)
|
||||||
router.POST(p+"/newUser", app.NewUserFromInvite)
|
router.POST(p+"/user/invite", app.NewUserFromInvite)
|
||||||
router.Use(static.Serve(p+"/invite/", app.webFS))
|
router.Use(static.Serve(p+"/invite/", app.webFS))
|
||||||
router.GET(p+"/invite/:invCode", app.InviteProxy)
|
router.GET(p+"/invite/:invCode", app.InviteProxy)
|
||||||
if app.config.Section("captcha").Key("enabled").MustBool(false) {
|
if app.config.Section("captcha").Key("enabled").MustBool(false) {
|
||||||
@ -182,7 +182,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
router.POST(p+"/logout", app.Logout)
|
router.POST(p+"/logout", app.Logout)
|
||||||
api.DELETE(p+"/users", app.DeleteUsers)
|
api.DELETE(p+"/users", app.DeleteUsers)
|
||||||
api.GET(p+"/users", app.GetUsers)
|
api.GET(p+"/users", app.GetUsers)
|
||||||
api.POST(p+"/users", app.NewUserFromAdmin)
|
api.POST(p+"/user", app.NewUserFromAdmin)
|
||||||
api.POST(p+"/users/extend", app.ExtendExpiry)
|
api.POST(p+"/users/extend", app.ExtendExpiry)
|
||||||
api.DELETE(p+"/users/:id/expiry", app.RemoveExpiry)
|
api.DELETE(p+"/users/:id/expiry", app.RemoveExpiry)
|
||||||
api.POST(p+"/users/enable", app.EnableDisableUsers)
|
api.POST(p+"/users/enable", app.EnableDisableUsers)
|
||||||
@ -191,6 +191,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
api.DELETE(p+"/invites", app.DeleteInvite)
|
api.DELETE(p+"/invites", app.DeleteInvite)
|
||||||
api.POST(p+"/invites/profile", app.SetProfile)
|
api.POST(p+"/invites/profile", app.SetProfile)
|
||||||
api.GET(p+"/profiles", app.GetProfiles)
|
api.GET(p+"/profiles", app.GetProfiles)
|
||||||
|
api.GET(p+"/profiles/names", app.GetProfileNames)
|
||||||
api.POST(p+"/profiles/default", app.SetDefaultProfile)
|
api.POST(p+"/profiles/default", app.SetDefaultProfile)
|
||||||
api.POST(p+"/profiles", app.CreateProfile)
|
api.POST(p+"/profiles", app.CreateProfile)
|
||||||
api.DELETE(p+"/profiles", app.DeleteProfile)
|
api.DELETE(p+"/profiles", app.DeleteProfile)
|
||||||
|
@ -33,7 +33,7 @@ function fixHTML(infile, outfile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let doc = new parser.load(f);
|
let doc = new parser.load(f);
|
||||||
for (let item of ["badge", "chip", "shield", "input", "table", "button", "portal", "select", "aside", "card", "field", "textarea"]) {
|
for (let item of ["badge", "chip", "shield", "input", "table", "button", "portal", "select", "aside", "card", "field", "textarea", "section"]) {
|
||||||
let items = doc("."+item);
|
let items = doc("."+item);
|
||||||
items.each((i, elem) => {
|
items.each((i, elem) => {
|
||||||
let hasColor = false;
|
let hasColor = false;
|
||||||
@ -50,8 +50,8 @@ function fixHTML(infile, outfile) {
|
|||||||
}
|
}
|
||||||
if (!hasColor) {
|
if (!hasColor) {
|
||||||
if (!hasDark(doc(elem))) {
|
if (!hasDark(doc(elem))) {
|
||||||
// card without ~neutral look different than with.
|
// card (and sections in sectioned cards) without ~neutral look different than with.
|
||||||
if (item != "card") doc(elem).addClass("~neutral");
|
if (item != "card" && item != "section") doc(elem).addClass("~neutral");
|
||||||
doc(elem).addClass("dark:~d_neutral");
|
doc(elem).addClass("dark:~d_neutral");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { inviteList, createInvite } from "./modules/invites.js";
|
|||||||
import { accountsList } from "./modules/accounts.js";
|
import { accountsList } from "./modules/accounts.js";
|
||||||
import { settingsList } from "./modules/settings.js";
|
import { settingsList } from "./modules/settings.js";
|
||||||
import { activityList } from "./modules/activity.js";
|
import { activityList } from "./modules/activity.js";
|
||||||
import { ProfileEditor } from "./modules/profiles.js";
|
import { ProfileEditor, reloadProfileNames } from "./modules/profiles.js";
|
||||||
import { _get, _post, notificationBox, whichAnimationEvent, bindManualDropdowns } from "./modules/common.js";
|
import { _get, _post, notificationBox, whichAnimationEvent, bindManualDropdowns } from "./modules/common.js";
|
||||||
import { Updater } from "./modules/update.js";
|
import { Updater } from "./modules/update.js";
|
||||||
import { Login } from "./modules/login.js";
|
import { Login } from "./modules/login.js";
|
||||||
@ -187,7 +187,7 @@ login.onLogin = () => {
|
|||||||
console.log("Logged in.");
|
console.log("Logged in.");
|
||||||
window.updater = new Updater();
|
window.updater = new Updater();
|
||||||
// FIXME: Decide whether to autoload activity or not
|
// FIXME: Decide whether to autoload activity or not
|
||||||
window.invites.reload()
|
reloadProfileNames();
|
||||||
setInterval(() => { window.invites.reload(); accounts.reload(); }, 30*1000);
|
setInterval(() => { window.invites.reload(); accounts.reload(); }, 30*1000);
|
||||||
const currentTab = window.tabs.current;
|
const currentTab = window.tabs.current;
|
||||||
switch (currentTab) {
|
switch (currentTab) {
|
||||||
|
@ -289,7 +289,7 @@ const create = (event: SubmitEvent) => {
|
|||||||
send.captcha_text = captcha.input.value;
|
send.captcha_text = captcha.input.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_post("/newUser", send, (req: XMLHttpRequest) => {
|
_post("/user/invite", send, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState != 4) return;
|
if (req.readyState != 4) return;
|
||||||
removeLoader(submitSpan);
|
removeLoader(submitSpan);
|
||||||
let vals = req.response as ValidatorRespDTO;
|
let vals = req.response as ValidatorRespDTO;
|
||||||
|
@ -1128,7 +1128,7 @@ export class accountsList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
toggleLoader(button);
|
toggleLoader(button);
|
||||||
_post("/users", send, (req: XMLHttpRequest) => {
|
_post("/user", send, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
toggleLoader(button);
|
toggleLoader(button);
|
||||||
if (req.status == 200 || (req.response["user"] as boolean)) {
|
if (req.status == 200 || (req.response["user"] as boolean)) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { _get, _post, _delete, toClipboard, toggleLoader, toDateString } from "../modules/common.js";
|
import { _get, _post, _delete, toClipboard, toggleLoader, toDateString } from "../modules/common.js";
|
||||||
import { DiscordUser, newDiscordSearch } from "../modules/discord.js";
|
import { DiscordUser, newDiscordSearch } from "../modules/discord.js";
|
||||||
|
import { reloadProfileNames } from "../modules/profiles.js";
|
||||||
|
|
||||||
class DOMInvite implements Invite {
|
class DOMInvite implements Invite {
|
||||||
updateNotify = (checkbox: HTMLInputElement) => {
|
updateNotify = (checkbox: HTMLInputElement) => {
|
||||||
@ -431,7 +432,6 @@ export class inviteList implements inviteList {
|
|||||||
private _list: HTMLDivElement;
|
private _list: HTMLDivElement;
|
||||||
private _empty: boolean;
|
private _empty: boolean;
|
||||||
// since invite reload sends profiles, this event it broadcast so the createInvite object can load them.
|
// since invite reload sends profiles, this event it broadcast so the createInvite object can load them.
|
||||||
private _profileLoadEvent = new CustomEvent("profileLoadEvent");
|
|
||||||
|
|
||||||
invites: { [code: string]: DOMInvite };
|
invites: { [code: string]: DOMInvite };
|
||||||
|
|
||||||
@ -502,13 +502,9 @@ export class inviteList implements inviteList {
|
|||||||
this._list.appendChild(domInv.asElement());
|
this._list.appendChild(domInv.asElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
reload = (callback?: () => void) => _get("/invites", null, (req: XMLHttpRequest) => {
|
reload = (callback?: () => void) => reloadProfileNames(() => _get("/invites", null, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
let data = req.response;
|
let data = req.response;
|
||||||
if (req.status == 200) {
|
|
||||||
window.availableProfiles = data["profiles"];
|
|
||||||
document.dispatchEvent(this._profileLoadEvent);
|
|
||||||
}
|
|
||||||
if (data["invites"] === undefined || data["invites"] == null || data["invites"].length == 0) {
|
if (data["invites"] === undefined || data["invites"] == null || data["invites"].length == 0) {
|
||||||
this.empty = true;
|
this.empty = true;
|
||||||
return;
|
return;
|
||||||
@ -534,7 +530,7 @@ export class inviteList implements inviteList {
|
|||||||
|
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
}
|
}
|
||||||
})
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const inviteURLEvent = (id: string) => { return new CustomEvent(inviteList._inviteURLEvent, {"detail": id}) };
|
export const inviteURLEvent = (id: string) => { return new CustomEvent(inviteList._inviteURLEvent, {"detail": id}) };
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
import { _get, _post, _delete, toggleLoader } from "../modules/common.js";
|
import { _get, _post, _delete, toggleLoader } from "../modules/common.js";
|
||||||
|
|
||||||
|
export const profileLoadEvent = new CustomEvent("profileLoadEvent");
|
||||||
|
export const reloadProfileNames = (then?: () => void) => _get("/profiles/names", null, (req: XMLHttpRequest) => {
|
||||||
|
if (req.readyState != 4) return;
|
||||||
|
window.availableProfiles = req.response["profiles"];
|
||||||
|
document.dispatchEvent(profileLoadEvent);
|
||||||
|
if (then) then();
|
||||||
|
});
|
||||||
|
|
||||||
interface Profile {
|
interface Profile {
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
libraries: string;
|
libraries: string;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export class ThemeManager {
|
export class ThemeManager {
|
||||||
|
|
||||||
private _themeButton: HTMLElement = null;
|
private _themeButton: HTMLElement = null;
|
||||||
|
private _metaTag: HTMLMetaElement;
|
||||||
|
|
||||||
private _beforeTransition = () => {
|
private _beforeTransition = () => {
|
||||||
const doc = document.documentElement;
|
const doc = document.documentElement;
|
||||||
@ -45,6 +46,7 @@ export class ThemeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(button?: HTMLElement) {
|
constructor(button?: HTMLElement) {
|
||||||
|
this._metaTag = document.querySelector("meta[name=color-scheme]") as HTMLMetaElement;
|
||||||
const theme = localStorage.getItem("theme");
|
const theme = localStorage.getItem("theme");
|
||||||
if (theme == "dark") {
|
if (theme == "dark") {
|
||||||
this._enable(true);
|
this._enable(true);
|
||||||
@ -54,18 +56,22 @@ export class ThemeManager {
|
|||||||
this._enable(true);
|
this._enable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arguments.length == 1)
|
if (button)
|
||||||
this.bindButton(button);
|
this.bindButton(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggle = () => {
|
private _toggle = () => {
|
||||||
|
let metaValue = "light dark";
|
||||||
this._beforeTransition();
|
this._beforeTransition();
|
||||||
if (!document.documentElement.classList.contains('dark')) {
|
if (!document.documentElement.classList.contains('dark')) {
|
||||||
document.documentElement.classList.add('dark');
|
document.documentElement.classList.add('dark');
|
||||||
|
metaValue = "dark light";
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove('dark');
|
document.documentElement.classList.remove('dark');
|
||||||
}
|
}
|
||||||
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? "dark" : "light");
|
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? "dark" : "light");
|
||||||
|
|
||||||
|
// this._metaTag.setAttribute("content", metaValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _enable = (dark: boolean) => {
|
private _enable = (dark: boolean) => {
|
||||||
@ -80,6 +86,8 @@ export class ThemeManager {
|
|||||||
document.documentElement.classList.remove(opposite);
|
document.documentElement.classList.remove(opposite);
|
||||||
}
|
}
|
||||||
document.documentElement.classList.add(mode);
|
document.documentElement.classList.add(mode);
|
||||||
|
|
||||||
|
// this._metaTag.setAttribute("content", `${mode} ${opposite}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
enable = (dark: boolean) => {
|
enable = (dark: boolean) => {
|
||||||
|
108
ts/setup.ts
108
ts/setup.ts
@ -1,5 +1,6 @@
|
|||||||
import { _get, _post, toggleLoader, notificationBox } from "./modules/common.js";
|
import { _get, _post, toggleLoader, notificationBox } from "./modules/common.js";
|
||||||
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
|
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
|
||||||
|
import { ThemeManager } from "./modules/theme.js";
|
||||||
|
|
||||||
interface sWindow extends Window {
|
interface sWindow extends Window {
|
||||||
messages: {};
|
messages: {};
|
||||||
@ -8,6 +9,7 @@ interface sWindow extends Window {
|
|||||||
declare var window: sWindow;
|
declare var window: sWindow;
|
||||||
window.URLBase = "";
|
window.URLBase = "";
|
||||||
|
|
||||||
|
const theme = new ThemeManager(document.getElementById("button-theme"));
|
||||||
|
|
||||||
window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5);
|
window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5);
|
||||||
|
|
||||||
@ -68,7 +70,16 @@ class Checkbox {
|
|||||||
constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
|
constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
|
||||||
this._el = el as HTMLInputElement;
|
this._el = el as HTMLInputElement;
|
||||||
this._hideEl = this._el as HTMLElement;
|
this._hideEl = this._el as HTMLElement;
|
||||||
if (this._hideEl.parentElement.tagName == "LABEL") { this._hideEl = this._hideEl.parentElement; }
|
if (this._hideEl.parentElement.tagName == "LABEL") {
|
||||||
|
this._hideEl = this._hideEl.parentElement;
|
||||||
|
} else if (this._hideEl.parentElement.classList.contains("switch")) {
|
||||||
|
if (this._hideEl.parentElement.parentElement.tagName == "LABEL") {
|
||||||
|
this._hideEl = this._hideEl.parentElement.parentElement;
|
||||||
|
} else {
|
||||||
|
this._hideEl = this._hideEl.parentElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (section && setting) {
|
if (section && setting) {
|
||||||
this._section = section;
|
this._section = section;
|
||||||
this._setting = setting;
|
this._setting = setting;
|
||||||
@ -86,11 +97,11 @@ class Checkbox {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._el.hasAttribute("checked")) {
|
/* if (this._el.hasAttribute("checked")) {
|
||||||
this._el.checked = true;
|
this._el.checked = true;
|
||||||
} else {
|
} else {
|
||||||
this._el.checked = false;
|
this._el.checked = false;
|
||||||
}
|
} */
|
||||||
this.broadcast();
|
this.broadcast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,7 +228,7 @@ class LangSelect extends Select {
|
|||||||
}
|
}
|
||||||
this.value = "en-us";
|
this.value = "en-us";
|
||||||
}
|
}
|
||||||
});
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,23 +459,32 @@ settings["email"]["method"].onchange = emailMethodChange;
|
|||||||
settings["messages"]["enabled"].onchange = emailMethodChange;
|
settings["messages"]["enabled"].onchange = emailMethodChange;
|
||||||
emailMethodChange();
|
emailMethodChange();
|
||||||
|
|
||||||
|
const getParentCard = (el: HTMLElement): HTMLDivElement => {
|
||||||
|
let pEl = el.parentElement;
|
||||||
|
while (pEl.tagName != "html") {
|
||||||
|
if (pEl.classList.contains("card")) return pEl as HTMLDivElement;
|
||||||
|
pEl = pEl.parentElement;
|
||||||
|
}
|
||||||
|
return pEl as HTMLDivElement;
|
||||||
|
};
|
||||||
|
|
||||||
const jellyfinLoginAccessChange = () => {
|
const jellyfinLoginAccessChange = () => {
|
||||||
const adminOnly = settings["ui"]["admin_only"].value == "true";
|
const adminOnly = settings["ui"]["admin_only"].value == "true";
|
||||||
const allowAll = settings["ui"]["allow_all"].value == "true";
|
const allowAll = settings["ui"]["allow_all"].value == "true";
|
||||||
const adminOnlyEl = document.getElementById("ui-admin_only") as HTMLInputElement;
|
const adminOnlyEl = document.getElementById("ui-admin_only") as HTMLInputElement;
|
||||||
const allowAllEls = [document.getElementById("ui-allow_all"), document.getElementById("description-ui-allow_all")];
|
const allowAllEl = document.getElementById("ui-allow_all") as HTMLInputElement;
|
||||||
const nextButton = adminOnlyEl.parentElement.parentElement.parentElement.querySelector("span.next") as HTMLSpanElement;
|
const nextButton = getParentCard(adminOnlyEl).querySelector("span.next") as HTMLSpanElement;
|
||||||
if (adminOnly && !allowAll) {
|
if (adminOnly && !allowAll) {
|
||||||
(allowAllEls[0] as HTMLInputElement).disabled = true;
|
allowAllEl.disabled = true;
|
||||||
adminOnlyEl.disabled = false;
|
adminOnlyEl.disabled = false;
|
||||||
nextButton.removeAttribute("disabled");
|
nextButton.removeAttribute("disabled");
|
||||||
} else if (!adminOnly && allowAll) {
|
} else if (!adminOnly && allowAll) {
|
||||||
adminOnlyEl.disabled = true;
|
adminOnlyEl.disabled = true;
|
||||||
(allowAllEls[0] as HTMLInputElement).disabled = false;
|
allowAllEl.disabled = false;
|
||||||
nextButton.removeAttribute("disabled");
|
nextButton.removeAttribute("disabled");
|
||||||
} else {
|
} else {
|
||||||
adminOnlyEl.disabled = false;
|
adminOnlyEl.disabled = false;
|
||||||
(allowAllEls[0] as HTMLInputElement).disabled = false;
|
allowAllEl.disabled = false;
|
||||||
nextButton.setAttribute("disabled", "true")
|
nextButton.setAttribute("disabled", "true")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -495,18 +515,17 @@ for (let section in settings) {
|
|||||||
|
|
||||||
const pageNames: string[][] = [];
|
const pageNames: string[][] = [];
|
||||||
|
|
||||||
window.history.replaceState("welcome", "Setup - jfa-go");
|
(() => {
|
||||||
|
const pushState = window.history.pushState;
|
||||||
|
window.history.pushState = function (data: any, __: string, _: string | URL) {
|
||||||
|
pushState.apply(window.history, arguments);
|
||||||
|
let ev = { state: data as string } as PopStateEvent;
|
||||||
|
window.onpopstate(ev);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
const changePage = (title: string, pageTitle: string) => {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const lang = urlParams.get("lang");
|
|
||||||
let page = "/#" + title;
|
|
||||||
if (lang) { page += "?lang=" + lang; }
|
|
||||||
window.history.pushState(title || "welcome", pageTitle, page);
|
|
||||||
};
|
|
||||||
const cards = Array.from(document.getElementById("page-container").getElementsByClassName("card")) as Array<HTMLDivElement>;
|
|
||||||
(window as any).cards = cards;
|
|
||||||
window.onpopstate = (event: PopStateEvent) => {
|
window.onpopstate = (event: PopStateEvent) => {
|
||||||
|
console.log("CALLLLLLLL", event);
|
||||||
if (event.state === "welcome") {
|
if (event.state === "welcome") {
|
||||||
cards[0].classList.remove("unfocused");
|
cards[0].classList.remove("unfocused");
|
||||||
for (let i = 1; i < cards.length; i++) { cards[i].classList.add("unfocused"); }
|
for (let i = 1; i < cards.length; i++) { cards[i].classList.add("unfocused"); }
|
||||||
@ -521,6 +540,35 @@ window.onpopstate = (event: PopStateEvent) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cards = Array.from(document.getElementsByClassName("page-container")[0].querySelectorAll(".card.sectioned")) as Array<HTMLDivElement>;
|
||||||
|
(window as any).cards = cards;
|
||||||
|
|
||||||
|
const changePageToIndex = (title: string, pageTitle: string, i: number) => {
|
||||||
|
cards[i].classList.remove("unfocused");
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const lang = urlParams.get("lang");
|
||||||
|
let page = "/#" + title;
|
||||||
|
if (lang) { page += "?lang=" + lang; }
|
||||||
|
console.log("pushing", title, pageTitle, page);
|
||||||
|
window.history.pushState(title || "welcome", pageTitle, page);
|
||||||
|
};
|
||||||
|
|
||||||
|
const changePage = (title: string, pageTitle: string) => {
|
||||||
|
let found = false;
|
||||||
|
for (let i = 0; i < cards.length; i++) {
|
||||||
|
if (!found && pageNames[i][0] == title && !(cards[i].classList.contains("hidden"))) {
|
||||||
|
found = true;
|
||||||
|
changePageToIndex(title, pageTitle, i);
|
||||||
|
} else {
|
||||||
|
cards[i].classList.add("unfocused");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
changePageToIndex(title, pageTitle, 0);
|
||||||
|
}
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
};
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
for (let i = 0; i < cards.length; i++) {
|
for (let i = 0; i < cards.length; i++) {
|
||||||
const card = cards[i];
|
const card = cards[i];
|
||||||
@ -534,36 +582,34 @@ window.onpopstate = (event: PopStateEvent) => {
|
|||||||
let pageTitle = titleEl.textContent + " - jfa-go";
|
let pageTitle = titleEl.textContent + " - jfa-go";
|
||||||
pageNames.push([title, pageTitle]);
|
pageNames.push([title, pageTitle]);
|
||||||
if (back) { back.addEventListener("click", () => {
|
if (back) { back.addEventListener("click", () => {
|
||||||
let found = false;
|
|
||||||
for (let ind = cards.length - 1; ind >= 0; ind--) {
|
for (let ind = cards.length - 1; ind >= 0; ind--) {
|
||||||
cards[ind].classList.add("unfocused");
|
if (ind < i && !(cards[ind].classList.contains("hidden"))) {
|
||||||
if (ind < i && !(cards[ind].classList.contains("hidden")) && !found) {
|
|
||||||
cards[ind].classList.remove("unfocused");
|
|
||||||
changePage(pageNames[ind][0], pageNames[ind][1]);
|
changePage(pageNames[ind][0], pageNames[ind][1]);
|
||||||
found = true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.scrollTo(0, 0);
|
|
||||||
}); }
|
}); }
|
||||||
if (next) {
|
if (next) {
|
||||||
const func = () => {
|
const func = () => {
|
||||||
if (next.hasAttribute("disabled")) return;
|
if (next.hasAttribute("disabled")) return;
|
||||||
let found = false;
|
|
||||||
for (let ind = 0; ind < cards.length; ind++) {
|
for (let ind = 0; ind < cards.length; ind++) {
|
||||||
cards[ind].classList.add("unfocused");
|
if (ind > i && !(cards[ind].classList.contains("hidden"))) {
|
||||||
if (ind > i && !(cards[ind].classList.contains("hidden")) && !found) {
|
|
||||||
cards[ind].classList.remove("unfocused");
|
|
||||||
changePage(pageNames[ind][0], pageNames[ind][1]);
|
changePage(pageNames[ind][0], pageNames[ind][1]);
|
||||||
found = true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.scrollTo(0, 0);
|
|
||||||
};
|
};
|
||||||
next.addEventListener("click", func)
|
next.addEventListener("click", func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
let initialLocation = window.location.hash.replace("#", "") || "welcome";
|
||||||
|
changePage(initialLocation, "Setup - jfa-go");
|
||||||
|
})();
|
||||||
|
// window.history.replaceState("welcome", "Setup - jfa-go",);
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const button = document.getElementById("jellyfin-test-connection") as HTMLSpanElement;
|
const button = document.getElementById("jellyfin-test-connection") as HTMLSpanElement;
|
||||||
const ogText = button.textContent;
|
const ogText = button.textContent;
|
||||||
|
Loading…
Reference in New Issue
Block a user