[feat] update multiple extensions from the UI (#2833)

* feat: add `Update` button
* feat: select all updatable extensions
* feat: dialog improvements
* fix: bad logging and permissions
This commit is contained in:
Vlad Stan 2024-12-16 14:54:49 +02:00 committed by GitHub
parent 25d59e4142
commit b81b30c896
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 135 additions and 8 deletions

View file

@ -35,6 +35,17 @@
v-if="!g.user.admin && tab != 'installed'" v-if="!g.user.admin && tab != 'installed'"
v-text="$t('only_admins_can_install')" v-text="$t('only_admins_can_install')"
></i> ></i>
<q-space></q-space>
<q-badge
v-if="g.user.admin && updatableExtensions?.length"
@click="showUpdateAllDialog = true"
color="primary"
class="float-right q-pa-sm q-mr-md cursor-pointer"
>
<span
v-text="$t('new_version') + ` (${updatableExtensions?.length})`"
></span>
</q-badge>
</q-tabs> </q-tabs>
</div> </div>
</div> </div>
@ -229,7 +240,7 @@
</div> </div>
</div> </div>
<q-dialog v-model="showUninstallDialog"> <q-dialog v-model="showUninstallDialog" position="top">
<q-card class="q-pa-lg"> <q-card class="q-pa-lg">
<h6 class="q-my-md text-primary" v-text="$t('warning')"></h6> <h6 class="q-my-md text-primary" v-text="$t('warning')"></h6>
<p> <p>
@ -266,7 +277,7 @@
</q-card> </q-card>
</q-dialog> </q-dialog>
<q-dialog v-model="showDropDbDialog"> <q-dialog v-model="showDropDbDialog" position="top">
<q-card v-if="selectedExtension" class="q-pa-lg"> <q-card v-if="selectedExtension" class="q-pa-lg">
<h6 class="q-my-md text-primary" v-text="$t('warning')"></h6> <h6 class="q-my-md text-primary" v-text="$t('warning')"></h6>
<p><span v-text="$t('extension_db_drop_warning')"></span><br /></p> <p><span v-text="$t('extension_db_drop_warning')"></span><br /></p>
@ -296,7 +307,7 @@
</q-card> </q-card>
</q-dialog> </q-dialog>
<q-dialog v-model="showManageExtensionDialog"> <q-dialog v-model="showManageExtensionDialog" position="top">
<q-card v-if="selectedRelease" class="q-pa-lg lnbits__dialog-card"> <q-card v-if="selectedRelease" class="q-pa-lg lnbits__dialog-card">
<q-card-section> <q-card-section>
<div v-if="selectedRelease.paymentRequest"> <div v-if="selectedRelease.paymentRequest">
@ -632,7 +643,7 @@
</q-card> </q-card>
</q-dialog> </q-dialog>
<q-dialog v-model="showPayToEnableDialog"> <q-dialog v-model="showPayToEnableDialog" position="top">
<q-card v-if="selectedExtension" class="q-pa-md"> <q-card v-if="selectedExtension" class="q-pa-md">
<q-card-section> <q-card-section>
<p> <p>
@ -738,7 +749,7 @@
</q-card> </q-card>
</q-dialog> </q-dialog>
<q-dialog v-model="showExtensionDetailsDialog"> <q-dialog v-model="showExtensionDetailsDialog" position="top">
<q-card <q-card
v-if="selectedExtensionDetails" v-if="selectedExtensionDetails"
class="q-pa-sm" class="q-pa-sm"
@ -884,6 +895,63 @@
</q-card-section> </q-card-section>
</q-card> </q-card>
</q-dialog> </q-dialog>
<q-dialog v-model="showUpdateAllDialog" position="top">
<q-card class="q-pa-md q-pt-md lnbits__dialog-card">
<div class="row">
<div class="col-12">
<h6 class="q-my-md" v-text="$t('update')"></h6>
</div>
</div>
<div v-if="updatableExtensions?.length > 1" class="row">
<div class="col-12">
<q-btn
outline
color="grey"
@click="selectAllUpdatableExtensionss()"
v-text="$t('select_all')"
></q-btn>
</div>
</div>
<q-virtual-scroll :items="updatableExtensions" style="max-height: 400px">
<template v-slot="{ item, index }">
<div class="row">
<div class="q-col">
<q-checkbox
v-model="item.selectedForUpdate"
:disable="item.isUpgraded"
value="false"
:label="item.name + ` (v${item.latestRelease?.version})`"
>
<q-spinner
v-if="item.inProgress"
color="primary"
size="1.5em"
class="q-ml-md"
></q-spinner>
</q-checkbox>
</div>
</div>
</template>
</q-virtual-scroll>
<div class="row q-mt-lg">
<q-btn
@click="updateSelectedExtensions()"
outline
color="grey"
v-text="$t('update')"
></q-btn>
<q-btn
v-close-popup
flat
color="grey"
class="q-ml-auto"
v-text="$t('cancel')"
></q-btn>
</div>
</q-card>
</q-dialog>
{% endblock %} {% block scripts %} {{ window_vars(user) }} {% endblock %} {% block scripts %} {{ window_vars(user) }}
<script> <script>
window.app = Vue.createApp({ window.app = Vue.createApp({
@ -898,11 +966,13 @@
tab: 'all', tab: 'all',
manageExtensionTab: 'releases', manageExtensionTab: 'releases',
filteredExtensions: null, filteredExtensions: null,
updatableExtensions: [],
showUninstallDialog: false, showUninstallDialog: false,
showManageExtensionDialog: false, showManageExtensionDialog: false,
showExtensionDetailsDialog: false, showExtensionDetailsDialog: false,
showDropDbDialog: false, showDropDbDialog: false,
showPayToEnableDialog: false, showPayToEnableDialog: false,
showUpdateAllDialog: false,
dropDbExtensionId: '', dropDbExtensionId: '',
selectedExtension: null, selectedExtension: null,
selectedImage: null, selectedImage: null,
@ -1074,6 +1144,7 @@
) )
.then(response => { .then(response => {
Quasar.Notify.create({ Quasar.Notify.create({
timeout: 2000,
type: 'positive', type: 'positive',
message: `Extension '${extension.id}' ${action}d!` message: `Extension '${extension.id}' ${action}d!`
}) })
@ -1476,12 +1547,63 @@
} finally { } finally {
release.inProgress = false release.inProgress = false
} }
},
selectAllUpdatableExtensionss: async function () {
this.updatableExtensions.forEach(e => (e.selectedForUpdate = true))
},
updateSelectedExtensions: async function () {
let count = 0
for (const ext of this.updatableExtensions) {
try {
if (!ext.selectedForUpdate) {
continue
}
ext.inProgress = true
await LNbits.api.request(
'POST',
`/api/v1/extension`,
this.g.user.wallets[0].adminkey,
{
ext_id: ext.id,
archive: ext.latestRelease.archive,
source_repo: ext.latestRelease.source_repo,
payment_hash: ext.latestRelease.payment_hash,
version: ext.latestRelease.version
}
)
count++
ext.isAvailable = true
ext.isInstalled = true
ext.isUpgraded = true
ext.inProgress = false
ext.installedRelease = ext.latestRelease
this.toggleExtension(ext)
} catch (err) {
console.warn(err)
Quasar.Notify.create({
type: 'negative',
message: `Failed to update ${ext.code}!`
})
} finally {
ext.inProgress = false
}
}
Quasar.Notify.create({
type: 'positive',
message: `${count} extensions updated!`
})
this.showUpdateAllDialog = false
setTimeout(() => {
window.location.reload()
}, 2000)
} }
}, },
created: function () { created: function () {
this.extensions = JSON.parse('{{extensions | tojson | safe}}').map(e => ({ this.extensions = JSON.parse('{{extensions | tojson | safe}}').map(e => ({
...e, ...e,
inProgress: false inProgress: false,
selectedForUpdate: false
})) }))
this.filteredExtensions = this.extensions.concat([]) this.filteredExtensions = this.extensions.concat([])
for (let i = 0; i < this.filteredExtensions.length; i++) { for (let i = 0; i < this.filteredExtensions.length; i++) {
@ -1493,6 +1615,9 @@
if (window.user) { if (window.user) {
this.user = LNbits.map.user(window.user) this.user = LNbits.map.user(window.user)
} }
this.updatableExtensions = this.extensions.filter(ext =>
this.hasNewVersion(ext)
)
}, },
mixins: [windowMixin] mixins: [windowMixin]
}) })

File diff suppressed because one or more lines are too long

View file

@ -156,6 +156,7 @@ window.localisation.en = {
expiry: 'Expiry', expiry: 'Expiry',
webhook: 'Webhook', webhook: 'Webhook',
payment_proof: 'Payment Proof', payment_proof: 'Payment Proof',
update: 'Update',
update_available: 'Update {version} available!', update_available: 'Update {version} available!',
latest_update: 'You are on the latest version {version}.', latest_update: 'You are on the latest version {version}.',
notifications: 'Notifications', notifications: 'Notifications',
@ -273,5 +274,6 @@ window.localisation.en = {
license: 'License', license: 'License',
reset_key: 'Reset Key', reset_key: 'Reset Key',
reset_password: 'Reset Password', reset_password: 'Reset Password',
border_choices: 'Border Choices' border_choices: 'Border Choices',
select_all: 'Select All'
} }