Frontend update checking settings

This commit is contained in:
Michael Shamoon 2022-09-30 12:30:23 -07:00
parent 436f9e891e
commit 9e2430da46
10 changed files with 200 additions and 74 deletions

View File

@ -200,14 +200,25 @@
<li class="nav-item mt-2">
<div class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap">
<div class="me-3">{{ versionString }}</div>
<div *ngIf="appRemoteVersion" class="version-check">
<div *ngIf="!settingsService.updateCheckingIsSet || appRemoteVersion" class="version-check">
<ng-template #updateAvailablePopContent>
<span class="small">Paperless-ngx {{ appRemoteVersion.version }} <ng-container i18n>is available.</ng-container><br/><ng-container i18n>Click to view.</ng-container></span>
</ng-template>
<ng-template #updateCheckingNotEnabledPopContent>
<span class="small"><ng-container i18n>Checking for updates is disabled.</ng-container><br/><ng-container i18n>Click for more information.</ng-container></span>
<p class="small mb-2">
<ng-container i18n>Paperless-ngx can automatically check for updates</ng-container>
</p>
<div class="btn-group btn-group-xs flex-fill w-100">
<button class="btn btn-outline-primary" (click)="setUpdateChecking(true)">Enable</button>
<button class="btn btn-outline-secondary" (click)="setUpdateChecking(false)">Disable</button>
</div>
<p class="small mb-0 mt-2">
<a class="small text-decoration-none fst-italic" routerLink="/settings" fragment="update-checking" i18n>
How does this work?
</a>
</p>
</ng-template>
<ng-container *ngIf="appRemoteVersion.feature_is_set; else updateCheckNotSet">
<ng-container *ngIf="settingsService.updateCheckingIsSet; else updateCheckNotSet">
<a *ngIf="appRemoteVersion.update_available" class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/releases"
[ngbPopover]="updateAvailablePopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body">
<svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
@ -217,8 +228,8 @@
</a>
</ng-container>
<ng-template #updateCheckNotSet>
<a class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://paperless-ngx.readthedocs.io/en/latest/configuration.html#update-checking"
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body">
<a class="small text-decoration-none" routerLink="/settings" fragment="update-checking"
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter" container="body">
<svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#info-circle" />
</svg>

View File

@ -24,6 +24,8 @@ import {
import { SettingsService } from 'src/app/services/settings.service'
import { TasksService } from 'src/app/services/tasks.service'
import { ComponentCanDeactivate } from 'src/app/guards/dirty-doc.guard'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { ToastService } from 'src/app/services/toast.service'
@Component({
selector: 'app-app-frame',
@ -40,13 +42,12 @@ export class AppFrameComponent implements ComponentCanDeactivate {
private remoteVersionService: RemoteVersionService,
private list: DocumentListViewService,
public settingsService: SettingsService,
public tasksService: TasksService
public tasksService: TasksService,
private readonly toastService: ToastService
) {
this.remoteVersionService
.checkForUpdates()
.subscribe((appRemoteVersion: AppRemoteVersion) => {
this.appRemoteVersion = appRemoteVersion
})
if (settingsService.updateCheckingEnabled) {
this.checkForUpdates()
}
tasksService.reload()
}
@ -150,4 +151,30 @@ export class AppFrameComponent implements ComponentCanDeactivate {
}
})
}
private checkForUpdates() {
this.remoteVersionService
.checkForUpdates()
.subscribe((appRemoteVersion: AppRemoteVersion) => {
this.appRemoteVersion = appRemoteVersion
})
}
setUpdateChecking(enable: boolean) {
this.settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, enable)
this.settingsService
.storeSettings()
.pipe(first())
.subscribe({
error: (error) => {
this.toastService.showError(
$localize`An error occurred while saving update checking settings.`
)
console.log(error)
},
})
if (enable) {
this.checkForUpdates()
}
}
}

View File

@ -17,25 +17,6 @@
}
}
.btn-group-xs {
> .btn {
padding: 0.2rem 0.25rem;
font-size: 0.675rem;
line-height: 1.2;
border-radius: 0.15rem;
}
> .btn:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
> .btn:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
.btn-group > label.disabled {
filter: brightness(0.5);

View File

@ -116,6 +116,21 @@
</div>
</div>
<h4 class="mt-4" id="update-checking" i18n>Update checking</h4>
<div class="row mb-3">
<div class="offset-md-3 col">
<p i18n>
Update checking works by pinging the the public <a href="https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest" target="_blank" rel="noopener noreferrer">Github API</a> for the latest release to determine whether a new version is available.<br/>
Actual updating of the app must still be performed manually.
</p>
<p i18n>
<em>No tracking data is collected by the app in any way.</em>
</p>
<app-input-check i18n-title title="Enable update checking" formControlName="updateCheckingEnabled" i18n-hint hint="Note that for users of thirdy-party containers e.g. linuxserver.io this notification may be 'ahead' of the current third-party release."></app-input-check>
</div>
</div>
<h4 class="mt-4" i18n>Bulk editing</h4>
<div class="row mb-3">

View File

@ -4,7 +4,7 @@ import {
LOCALE_ID,
OnInit,
OnDestroy,
Renderer2,
AfterViewInit,
} from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
@ -18,13 +18,17 @@ import { Toast, ToastService } from 'src/app/services/toast.service'
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
import { Observable, Subscription, BehaviorSubject, first } from 'rxjs'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { ActivatedRoute } from '@angular/router'
import { ViewportScroller } from '@angular/common'
@Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss'],
})
export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
export class SettingsComponent
implements OnInit, AfterViewInit, OnDestroy, DirtyComponent
{
savedViewGroup = new FormGroup({})
settingsForm = new FormGroup({
@ -45,6 +49,7 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
notificationsConsumerFailed: new FormControl(null),
notificationsConsumerSuppressOnDashboard: new FormControl(null),
commentsEnabled: new FormControl(null),
updateCheckingEnabled: new FormControl(null),
})
savedViews: PaperlessSavedView[]
@ -74,9 +79,19 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
private documentListViewService: DocumentListViewService,
private toastService: ToastService,
private settings: SettingsService,
@Inject(LOCALE_ID) public currentLocale: string
@Inject(LOCALE_ID) public currentLocale: string,
private viewportScroller: ViewportScroller,
private activatedRoute: ActivatedRoute
) {}
ngAfterViewInit(): void {
if (this.activatedRoute.snapshot.fragment) {
this.viewportScroller.scrollToAnchor(
this.activatedRoute.snapshot.fragment
)
}
}
ngOnInit() {
this.savedViewService.listAll().subscribe((r) => {
this.savedViews = r.results
@ -118,6 +133,9 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD
),
commentsEnabled: this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED),
updateCheckingEnabled: this.settings.get(
SETTINGS_KEYS.UPDATE_CHECKING_ENABLED
),
}
for (let view of this.savedViews) {
@ -240,6 +258,10 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
SETTINGS_KEYS.COMMENTS_ENABLED,
this.settingsForm.value.commentsEnabled
)
this.settings.set(
SETTINGS_KEYS.UPDATE_CHECKING_ENABLED,
this.settingsForm.value.updateCheckingEnabled
)
this.settings.setLanguage(this.settingsForm.value.displayLanguage)
this.settings
.storeSettings()

View File

@ -37,6 +37,9 @@ export const SETTINGS_KEYS = {
NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD:
'general-settings:notifications:consumer-suppress-on-dashboard',
COMMENTS_ENABLED: 'general-settings:comments-enabled',
UPDATE_CHECKING_ENABLED: 'general-settings:update-checking:enabled',
UPDATE_CHECKING_BACKEND_SETTING:
'general-settings:update-checking:backend-setting',
}
export const SETTINGS: PaperlessUiSetting[] = [
@ -120,4 +123,14 @@ export const SETTINGS: PaperlessUiSetting[] = [
type: 'boolean',
default: true,
},
{
key: SETTINGS_KEYS.UPDATE_CHECKING_ENABLED,
type: 'boolean',
default: false,
},
{
key: SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING,
type: 'string',
default: '',
},
]

View File

@ -6,7 +6,6 @@ import { environment } from 'src/environments/environment'
export interface AppRemoteVersion {
version: string
update_available: boolean
feature_is_set: boolean
}
@Injectable({

View File

@ -313,13 +313,7 @@ export class SettingsService {
)
}
get(key: string): any {
let setting = SETTINGS.find((s) => s.key == key)
if (!setting) {
return null
}
private getSettingRawValue(key: string): any {
let value = null
// parse key:key:key into nested object
const keys = key.replace('general-settings:', '').split(':')
@ -330,6 +324,17 @@ export class SettingsService {
if (index == keys.length - 1) value = settingObj[keyPart]
else settingObj = settingObj[keyPart]
})
return value
}
get(key: string): any {
let setting = SETTINGS.find((s) => s.key == key)
if (!setting) {
return null
}
let value = this.getSettingRawValue(key)
if (value != null) {
switch (setting.type) {
@ -359,6 +364,11 @@ export class SettingsService {
})
}
private settingIsSet(key: string): boolean {
let value = this.getSettingRawValue(key)
return value != null
}
storeSettings(): Observable<any> {
return this.http.post(this.baseUrl, { settings: this.settings })
}
@ -401,4 +411,29 @@ export class SettingsService {
})
}
}
get updateCheckingEnabled(): boolean {
const backendSetting = this.get(
SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING
)
if (
!this.settingIsSet(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED) &&
backendSetting != 'default'
) {
this.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, backendSetting === 'true')
}
return (
this.get(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED) ||
(!this.settingIsSet(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED) &&
backendSetting == 'true')
)
}
get updateCheckingIsSet(): boolean {
return (
this.settingIsSet(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED) ||
this.get(SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING) != 'default'
)
}
}

View File

@ -526,6 +526,25 @@ a.badge {
border-color: var(--bs-primary);
}
.btn-group-xs {
> .btn {
padding: 0.2rem 0.25rem;
font-size: 0.675rem;
line-height: 1.2;
border-radius: 0.15rem;
}
> .btn:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
> .btn:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
code {
color: var(--pngx-body-color-accent)
}

View File

@ -780,9 +780,6 @@ class RemoteVersionView(GenericAPIView):
remote_version = "0.0.0"
is_greater_than_current = False
current_version = packaging_version.parse(version.__full_version_str__)
# TODO: this can likely be removed when frontend settings are saved to DB
feature_is_set = settings.ENABLE_UPDATE_CHECK != "default"
if feature_is_set and settings.ENABLE_UPDATE_CHECK:
try:
req = urllib.request.Request(
"https://api.github.com/repos/paperless-ngx/"
@ -815,7 +812,6 @@ class RemoteVersionView(GenericAPIView):
{
"version": remote_version,
"update_available": is_greater_than_current,
"feature_is_set": feature_is_set,
},
)
@ -848,15 +844,23 @@ class UiSettingsView(GenericAPIView):
displayname = user.username
if user.first_name or user.last_name:
displayname = " ".join([user.first_name, user.last_name])
settings = {}
ui_settings = {}
if hasattr(user, "ui_settings"):
settings = user.ui_settings.settings
ui_settings = user.ui_settings.settings
if ui_settings["update_checking"]:
ui_settings["update_checking"][
"backend_setting"
] = settings.ENABLE_UPDATE_CHECK
else:
ui_settings["update_checking"] = {
"backend_setting": settings.ENABLE_UPDATE_CHECK,
}
return Response(
{
"user_id": user.id,
"username": user.username,
"display_name": displayname,
"settings": settings,
"settings": ui_settings,
},
)