Skip to content

Commit

Permalink
[api][desktop]: Check for new version on startup. Fix #527
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagohm committed Aug 20, 2024
1 parent 151b38d commit ffc035b
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class PreferenceRepository(@Qualifier("preferenceBox") override val box: Box<Pre
.build().use { it.findUnique() }
}

@Synchronized
fun deleteByKey(key: String) {
return box.query(PreferenceEntity_.key equal key)
.build().use { it.remove() }
Expand Down
10 changes: 10 additions & 0 deletions api/src/main/kotlin/nebulosa/api/system/GitHubLatestRelease.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nebulosa.api.system

import com.fasterxml.jackson.annotation.JsonProperty

data class GitHubLatestRelease(
@JvmField @field:JsonProperty("tag_name") val version: String = "",
@JvmField @field:JsonProperty("name") val name: String = "",
@JvmField @field:JsonProperty("draft") val draft: Boolean = false,
@JvmField @field:JsonProperty("prerelease") val preRelease: Boolean = false,
)
13 changes: 13 additions & 0 deletions api/src/main/kotlin/nebulosa/api/system/SystemController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package nebulosa.api.system

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("system")
class SystemController(private val systemService: SystemService) {

@GetMapping("latest-release")
fun latestRelease() = systemService.latestRelease()
}
35 changes: 35 additions & 0 deletions api/src/main/kotlin/nebulosa/api/system/SystemService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package nebulosa.api.system

import com.fasterxml.jackson.databind.ObjectMapper
import okhttp3.OkHttpClient
import okhttp3.Request
import org.springframework.http.HttpHeaders
import org.springframework.stereotype.Service

@Service
class SystemService(
private val httpClient: OkHttpClient,
private val objectMapper: ObjectMapper,
) {

fun latestRelease(): GitHubLatestRelease? {
val request = Request.Builder()
.get()
.url(GITHUB_LATEST_RELEASE_URL)
.header(HttpHeaders.ACCEPT, "application/vnd.github+json")
.header(GITHUB_API_VERSION_HEADER_KEY, GITHUB_API_VERSION_HEADER_VALUE)
.build()

val response = httpClient.newCall(request).execute()

return response.body?.byteStream()
?.use { objectMapper.readValue(it, GitHubLatestRelease::class.java) }
}

companion object {

const val GITHUB_LATEST_RELEASE_URL = "https://api.github.com/repos/tiagohm/nebulosa/releases/latest"
const val GITHUB_API_VERSION_HEADER_KEY = "X-GitHub-Api-Version"
const val GITHUB_API_VERSION_HEADER_VALUE = "2022-11-28"
}
}
21 changes: 21 additions & 0 deletions desktop/src/app/home/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,27 @@
pTooltip="Disconnect"
tooltipPosition="bottom" />
</div>
<div
*ngIf="newVersion"
class="col-12 py-0">
<div class="m-0 p-message p-message-info w-full flex align-items-center justify-content-between px-2">
<div class="flex align-items-center">
<span class="p-message-summary">New version:</span>
<span class="p-message-detail">{{ newVersion }}</span>
</div>
<a
target="_blank"
href="https://github.com/tiagohm/nebulosa/releases/latest">
<p-button
icon="mdi mdi-download"
[text]="true"
label="Download"
size="small"
severity="info"
styleClass="py-1 text-blue-900" />
</a>
</div>
</div>
</div>
<div
class="buttons grid my-2 overflow-y-auto relative"
Expand Down
18 changes: 18 additions & 0 deletions desktop/src/app/home/home.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AfterContentInit, Component, NgZone, ViewChild, ViewEncapsulation } from '@angular/core'
import { dirname } from 'path'
import nebulosa from '../../assets/data/nebulosa.json'
import { DeviceChooserComponent } from '../../shared/components/device-chooser/device-chooser.component'
import { DeviceConnectionCommandEvent, DeviceListMenuComponent } from '../../shared/components/device-list-menu/device-list-menu.component'
import { MenuItem, SlideMenuItem } from '../../shared/components/menu-item/menu-item.component'
Expand Down Expand Up @@ -43,6 +44,7 @@ export class HomeComponent implements AfterContentInit {
protected guideOutputs: GuideOutput[] = []

protected page = 0
protected newVersion?: string

protected readonly deviceModel: MenuItem[] = []

Expand Down Expand Up @@ -273,6 +275,22 @@ export class HomeComponent implements AfterContentInit {
this.rotators = await this.api.rotators()
this.guideOutputs = await this.api.guideOutputs()
}

void this.checkForNewVersion()
}

private async checkForNewVersion() {
if (this.preferenceService.settings.get().checkVersion) {
try {
const release = await this.api.latestRelease()

if (release?.version && nebulosa.version !== release.version) {
this.newVersion = release.name
}
} catch {
console.error('failed to check for new version')
}
}
}

private deviceAdded(device: Device) {
Expand Down
13 changes: 13 additions & 0 deletions desktop/src/app/settings/settings.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
<div class="grid px-3 pb-2 pt-3 relative">
<div
class="col-12"
*ngIf="tab === 'GENERAL'">
<div class="grid">
<div class="col-12">
<p-checkbox
[binary]="true"
label="Check for new version on startup"
[(ngModel)]="preference.checkVersion"
(ngModelChange)="savePreference()" />
</div>
</div>
</div>
<div
class="col-12"
*ngIf="tab === 'LOCATION'">
Expand Down
7 changes: 7 additions & 0 deletions desktop/src/app/settings/settings.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ export class SettingsComponent implements AfterViewInit, OnDestroy {
private readonly locationChangeSubscription?: Subscription

protected readonly menuModel: MenuItem[] = [
{
icon: 'mdi mdi-cog',
label: 'General',
command: (e) => {
this.showTab('GENERAL', e.item?.label)
},
},
{
icon: 'mdi mdi-map-marker',
label: 'Location',
Expand Down
8 changes: 7 additions & 1 deletion desktop/src/shared/services/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { FlatWizardRequest } from '../types/flat-wizard.types'
import { Focuser } from '../types/focuser.types'
import { HipsSurvey } from '../types/framing.types'
import { GuideDirection, GuideOutput, Guider, GuiderHistoryStep, SettleInfo } from '../types/guider.types'
import { ConnectionStatus, ConnectionType } from '../types/home.types'
import { ConnectionStatus, ConnectionType, LatestRelease } from '../types/home.types'
import { AnnotateImageRequest, CoordinateInterpolation, DetectedStar, FOVCamera, FOVTelescope, ImageAnnotation, ImageInfo, ImageMousePosition, ImageSaveDialog, ImageSolved, ImageTransformation } from '../types/image.types'
import { CelestialLocationType, Mount, MountRemoteControl, MountRemoteControlProtocol, SlewRate, TrackMode } from '../types/mount.types'
import { PlateSolverRequest } from '../types/platesolver.types'
Expand Down Expand Up @@ -708,4 +708,10 @@ export class ApiService {
const query = this.http.query({ accepted })
return this.http.put(`confirmation/${idempotencyKey}?${query}`)
}

// SYSTEM

latestRelease() {
return this.http.get<LatestRelease | undefined>('system/latest-release')
}
}
7 changes: 7 additions & 0 deletions desktop/src/shared/types/home.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ export interface HomeConnectionDialog {
edited: boolean
}

export interface LatestRelease {
version: string
name: string
draft: boolean
preRelease: boolean
}

export const DEFAULT_CONNECTION_HOST: string = 'localhost'
export const DEFAULT_CONNECTION_PORT: number = 7624

Expand Down
5 changes: 4 additions & 1 deletion desktop/src/shared/types/settings.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { DEFAULT_PLATE_SOLVER_SETTINGS, plateSolverSettingsWithDefault, type Pla
import { DEFAULT_STACKER_SETTINGS, stackerSettingsWithDefault, type StackerSettings, type StackerType } from './stacker.types'
import { DEFAULT_STAR_DETECTOR_SETTINGS, starDetectorSettingsWithDefault, type StarDetectorSettings, type StarDetectorType } from './stardetector.types'

export type SettingsTab = 'LOCATION' | 'PLATE_SOLVER' | 'STAR_DETECTOR' | 'LIVE_STACKER' | 'STACKER' | 'CAPTURE_NAMING_FORMAT'
export type SettingsTab = 'GENERAL' | 'LOCATION' | 'PLATE_SOLVER' | 'STAR_DETECTOR' | 'LIVE_STACKER' | 'STACKER' | 'CAPTURE_NAMING_FORMAT'

export interface SettingsPreference {
checkVersion: boolean
plateSolver: Record<PlateSolverType, PlateSolverSettings>
starDetector: Record<StarDetectorType, StarDetectorSettings>
liveStacker: Record<LiveStackerType, LiveStackerSettings>
Expand All @@ -19,6 +20,7 @@ export interface SettingsPreference {
}

export const DEFAULT_SETTINGS_PREFERENCE: SettingsPreference = {
checkVersion: true,
plateSolver: {
ASTROMETRY_NET: structuredClone(DEFAULT_PLATE_SOLVER_SETTINGS),
ASTROMETRY_NET_ONLINE: structuredClone(DEFAULT_PLATE_SOLVER_SETTINGS),
Expand Down Expand Up @@ -46,6 +48,7 @@ export const DEFAULT_SETTINGS_PREFERENCE: SettingsPreference = {
export function settingsPreferenceWithDefault(preference?: Partial<SettingsPreference>, source: SettingsPreference = DEFAULT_SETTINGS_PREFERENCE) {
if (!preference) return structuredClone(source)

preference.checkVersion ??= source.checkVersion
preference.plateSolver ??= structuredClone(source.plateSolver)
preference.starDetector ??= structuredClone(source.starDetector)
preference.liveStacker ??= structuredClone(source.liveStacker)
Expand Down

0 comments on commit ffc035b

Please sign in to comment.