{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {{ window_vars(user, extensions) }}{% block page %}
<div class="row q-col-gutter-md q-mb-md">
  <div class="col-sm-9 col-xs-12">
    <p class="text-h4 gt-sm">{%raw%}{{ $t('extensions') }}{%endraw%}</p>
  </div>

  <div class="col-sm-3 col-xs-12 q-ml-auto">
    <q-input v-model="searchTerm" :label="$t('search_extensions')">
      <q-icon
        v-if="searchTerm !== ''"
        name="close"
        @click="searchTerm = ''"
        class="cursor-pointer"
      />
    </q-input>
  </div>
</div>

<div class="row q-col-gutter-md q-mb-md">
  <div class="col-12">
    <q-card>
      <div class="q-pa-xs">
        <div class="q-gutter-y-md">
          <q-tabs
            v-model="tab"
            @input="handleTabChanged"
            active-color="primary"
            align="left"
          >
            <q-tab
              name="installed"
              :label="$t('installed')"
              @update="val => tab = val.name"
            ></q-tab>
            <q-tab
              name="all"
              :label="$t('all')"
              @update="val => tab = val.name"
            ></q-tab>
            <q-tab
              name="featured"
              :label="$t('featured')"
              @update="val => tab = val.name"
            ></q-tab>
            <i v-if="!g.user.admin && tab != 'installed'"
              >{%raw%}{{ $t('only_admins_can_install') }}{%endraw%}</i
            >
          </q-tabs>
        </div>
      </div>
    </q-card>
  </div>
</div>
<div class="row q-col-gutter-md">
  <div
    class="col-6 col-md-4 col-lg-3"
    v-for="extension in filteredExtensions"
    :key="extension.id + extension.hash"
  >
    <q-card>
      <q-card-section style="min-height: 140px" class="q-pb-none">
        <div class="row">
          <div class="col-3">
            <q-img
              v-if="extension.icon"
              :src="extension.icon"
              spinner-color="white"
              style="max-width: 100%"
            ></q-img>
            <div v-else>
              <q-icon
                class="gt-sm"
                name="extension"
                color="primary"
                size="70px"
              ></q-icon>
              <q-icon
                class="lt-md"
                name="extension"
                color="primary"
                size="35px"
              ></q-icon>
            </div>
          </div>
          <div class="col-9 q-pl-sm">
            <q-badge
              v-if="hasNewVersion(extension)"
              color="green"
              class="float-right"
            >
              <small>{%raw%}{{ $t('new_version') }}{%endraw%}</small>
              <q-tooltip
                ><span v-text="extension.latestRelease.version"></span
              ></q-tooltip>
            </q-badge>
            {% raw %}
            <div class="text-h5 gt-sm q-mt-sm q-mb-xs gt-sm">
              {{ extension.name }}
            </div>
            <div
              class="text-h5 gt-sm q-mt-sm q-mb-xs lt-md"
              style="min-height: 60px"
            >
              {{ extension.name }}
            </div>
            <div
              class="text-subtitle2 gt-sm"
              style="font-size: 11px; height: 34px"
            >
              {{ extension.shortDescription ||
              extension.installedRelease?.description }}
            </div>
            <div class="text-subtitle1 lt-md q-mt-sm q-mb-xs">
              {{ extension.name }}
            </div>
            <div
              class="text-subtitle2 lt-md"
              style="font-size: 9px; height: 34px"
            >
              {{ extension.shortDescription }}
            </div>
            {% endraw %}
          </div>
        </div>
        <div class="row q-pt-sm">
          <div class="col">
            <small v-if="extension.dependencies?.length"
              >{%raw%}{{ $t('extension_depends_on') }}{%endraw%}</small
            >
            <small v-else>&nbsp;</small>
            <q-badge
              v-for="dep in extension.dependencies"
              :key="dep"
              color="orange"
            >
              <small v-text="dep"></small>
            </q-badge>
          </div>
        </div>
      </q-card-section>
      <q-card-section>
        <div>
          <q-rating
            class="gt-sm"
            v-model="maxStars"
            disable
            size="1.5em"
            :max="5"
            color="primary"
            ><q-tooltip
              >{%raw%}{{ $t('extension_rating_soon') }}{%endraw%}</q-tooltip
            ></q-rating
          >
          <q-rating
            v-model="maxStars"
            class="lt-md"
            size="1.5em"
            :max="5"
            color="primary"
            ><q-tooltip
              >{%raw%}{{ $t('extension_rating_soon') }}{%endraw%}</q-tooltip
            ></q-rating
          >
          <q-toggle
            v-if="extension.isAvailable && extension.isInstalled && g.user.admin"
            :label="extension.isActive ? $t('activated'):  $t('deactivated') "
            color="secondary"
            style="max-height: 21px"
            v-model="extension.isActive"
            @input="toggleExtension(extension)"
            ><q-tooltip
              >{%raw%}{{ $t('activate_extension_details')
              }}{%endraw%}</q-tooltip
            ></q-toggle
          >
        </div>
      </q-card-section>
      <q-separator></q-separator>
      <q-card-actions style="min-height: 52px">
        <div class="col-10">
          <div v-if="!extension.inProgress">
            <q-btn
              v-if="user.extensions.includes(extension.id) && extension.isActive && extension.isInstalled"
              flat
              color="primary"
              type="a"
              :href="extension.id + '/'"
              >{%raw%}{{ $t('open') }}{%endraw%}</q-btn
            >
            <q-btn
              v-if="user.extensions.includes(extension.id) && extension.isActive && extension.isInstalled"
              flat
              color="grey-5"
              type="a"
              :href="['{{
                url_for('install.extensions')
              }}', '?disable=', extension.id].join('')"
            >
              {%raw%}{{ $t('disable') }}{%endraw%}</q-btn
            >
            <q-badge v-if="extension.isAdminOnly && !user.admin">
              {%raw%}{{ $t('admin_only') }}{%endraw%}
            </q-badge>
            <q-btn
              v-else-if="extension.isInstalled && extension.isActive && !user.extensions.includes(extension.id)"
              flat
              color="primary"
              type="a"
              :href="['{{
                url_for('install.extensions')
              }}', '?enable=', extension.id].join('')"
            >
              {%raw%}{{ $t('enable') }}{%endraw%}
              <q-tooltip>
                <span v-text="$t('enable_extension_details')">
                </span> </q-tooltip
            ></q-btn>

            <q-btn
              @click="showUpgrade(extension)"
              flat
              color="primary"
              v-if="g.user.admin"
            >
              {%raw%}{{ $t('manage') }}{%endraw%}<q-tooltip
                >{%raw%}{{ $t('manage_extension_details')
                }}{%endraw%}</q-tooltip
              ></q-btn
            >
          </div>
          <div v-else>
            <q-spinner color="primary" size="2.55em"></q-spinner>
          </div>
        </div>

        <div class="col-2">
          <div
            v-if="extension.isInstalled && extension.installedRelease"
            class="float-right"
          >
            <q-badge>
              {% raw %}{{ extension.installedRelease.version }}{% endraw %}
              <q-tooltip>
                <span v-text="$t('extension_installed_version')"></span>
              </q-tooltip>
            </q-badge>
          </div>
        </div>
      </q-card-actions>
    </q-card>
  </div>
  <q-dialog v-model="showUninstallDialog">
    <q-card class="q-pa-lg">
      <h6 class="q-my-md text-primary">{%raw%}{{ $t('warning') }}{%endraw%}</h6>
      <p>
        {%raw%}{{ $t('extension_uninstall_warning') }}{%endraw%} <br />
        {%raw%}{{ $t('confirm_continue') }}{%endraw%}
      </p>

      <div class="row q-mt-lg">
        <q-checkbox
          v-model="uninstallAndDropDb"
          value="false"
          label="Cleanup database tables"
        >
          <q-tooltip class="bg-grey-8" anchor="bottom left" self="top left">
            {%raw%}{{ $t('extension_db_drop_info') }}{%endraw%}
          </q-tooltip>
        </q-checkbox>
      </div>
      <div class="row q-mt-lg">
        <q-btn outline color="grey" @click="uninstallExtension()"
          >{%raw%}{{ $t('uninstall_confirm') }}{%endraw%}</q-btn
        >
        <q-btn v-close-popup flat color="grey" class="q-ml-auto"
          >{%raw%}{{ $t('cancel') }}{%endraw%}</q-btn
        >
      </div>
    </q-card>
  </q-dialog>

  <q-dialog v-model="showDropDbDialog">
    <q-card v-if="selectedExtension" class="q-pa-lg">
      <h6 class="q-my-md text-primary">{%raw%}{{ $t('warning') }}{%endraw%}</h6>
      <p>{%raw%}{{ $t('extension_db_drop_warning') }}{%endraw%} <br /></p>
      <q-input
        v-model="dropDbExtensionId"
        :label="selectedExtension.id"
      ></q-input>
      <br />
      <p>{%raw%}{{ $t('confirm_continue') }}{%endraw%}</p>

      <div class="row q-mt-lg">
        <q-btn
          :disable="dropDbExtensionId !== selectedExtension.id"
          outline
          color="red"
          @click="dropExtensionDb()"
          >{%raw%}{{ $t('confirm') }}{%endraw%}</q-btn
        >
        <q-btn v-close-popup flat color="grey" class="q-ml-auto"
          >{%raw%}{{ $t('cancel') }}{%endraw%}</q-btn
        >
      </div>
    </q-card>
  </q-dialog>

  <q-dialog v-model="showUpgradeDialog">
    <q-card class="q-pa-lg lnbits__dialog-card">
      <q-card-section>
        <div class="text-h6" v-text="selectedExtension?.name"></div>
      </q-card-section>
      <div class="col-12 col-md-5 q-gutter-y-md" v-if="selectedExtensionRepos">
        <q-card
          flat
          bordered
          class="my-card"
          v-for="repoName of Object.keys(selectedExtensionRepos)"
          :key="repoName"
        >
          <q-expansion-item
            :key="repoName"
            group="repos"
            :caption="repoName"
            :content-inset-level="0.5"
            :default-opened="selectedExtensionRepos[repoName].isInstalled"
          >
            <template v-slot:header>
              <q-item-section avatar>
                <q-avatar
                  :icon="selectedExtensionRepos[repoName].isInstalled ? 'download_done': 'download'"
                  :text-color="selectedExtensionRepos[repoName].isInstalled ? 'green' : ''"
                />
              </q-item-section>

              <q-item-section>
                <div class="row">
                  <div class="col-10">
                    {%raw%}{{ $t('repository') }}{%endraw%}
                    <br />
                    <small v-text="repoName"></small>
                    <q-tooltip
                      ><span
                        v-text="selectedExtensionRepos[repoName].repo"
                      ></span
                    ></q-tooltip>
                  </div>
                  <div class="col-2"></div>
                </div>
              </q-item-section>
            </template>

            <q-card-section class="q-pa-none">
              <q-separator></q-separator>

              <q-list>
                <q-expansion-item
                  v-for="release of selectedExtensionRepos[repoName].releases"
                  :key="release.version"
                  group="releases"
                  @click="getGitHubReleaseDetails(release)"
                  :icon="getReleaseIcon(release)"
                  :label="release.description"
                  :caption="release.version"
                  :content-inset-level="0.5"
                  :header-class="getReleaseIconColor(release)"
                >
                  <div v-if="release.inProgress">
                    <q-spinner color="primary" size="2.55em"></q-spinner>
                  </div>
                  <div v-else-if="release.error">
                    <q-icon
                      class="gt-sm"
                      name="error"
                      color="pink"
                      size="70px"
                    ></q-icon>
                    Cannot get the release details.
                  </div>
                  <q-card v-else>
                    <q-card-section v-if="release.is_version_compatible">
                      <q-btn
                        v-if="!release.isInstalled"
                        @click="installExtension(release)"
                        color="primary unelevated mt-lg pt-lg"
                        >{%raw%}{{ $t('install') }}{%endraw%}</q-btn
                      >
                      <q-btn v-else @click="showUninstall()" flat color="red">
                        {%raw%}{{ $t('uninstall') }}{%endraw%}</q-btn
                      >
                      <a
                        v-if="release.html_url"
                        class="text-secondary float-right"
                        :href="release.html_url"
                        target="_blank"
                        rel="noopener noreferrer"
                        style="color: inherit"
                        >{%raw%}{{ $t('release_notes') }}{%endraw%}</a
                      >
                    </q-card-section>
                    <q-card-section v-else>
                      {%raw%}{{ $t('extension_min_lnbits_version') }}{%endraw%}
                      <strong>
                        <span v-text="release.min_lnbits_version"></span>
                      </strong>
                    </q-card-section>
                    <q-card v-if="release.warning">
                      <q-card-section>
                        <div class="text-h6">
                          <q-badge color="yellow" text-color="black">
                            {%raw%}{{ $t('warning') }}{%endraw%}
                          </q-badge>
                        </div>
                        <div class="text-subtitle2">
                          <span v-text="release.warning"></span>
                        </div>
                      </q-card-section>
                    </q-card>

                    <q-separator></q-separator> </q-card
                ></q-expansion-item>
              </q-list>
            </q-card-section>
          </q-expansion-item>
        </q-card>
      </div>
      <q-spinner v-else color="primary" size="2.55em"></q-spinner>
      <div class="row q-mt-lg">
        <q-btn
          v-if="selectedExtension?.isInstalled"
          @click="showUninstall()"
          flat
          color="red"
        >
          {%raw%}{{ $t('uninstall') }}{%endraw%}</q-btn
        >
        <q-btn
          v-else-if="selectedExtension?.hasDatabaseTables"
          @click="showDropDb()"
          flat
          color="red"
          :label="$t('drop_db')"
        ></q-btn>
        <q-btn v-close-popup flat color="grey" class="q-ml-auto">
          {%raw%}{{ $t('close') }}{%endraw%}</q-btn
        >
      </div>
    </q-card>
  </q-dialog>
</div>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script>
  new Vue({
    el: '#vue',
    data: function () {
      return {
        searchTerm: '',
        tab: 'all',
        filteredExtensions: null,
        showUninstallDialog: false,
        showUpgradeDialog: false,
        showDropDbDialog: false,
        dropDbExtensionId: '',
        selectedExtension: null,
        selectedExtensionRepos: null,
        uninstallAndDropDb: false,
        maxStars: 5,
        user: null
      }
    },
    watch: {
      searchTerm(term) {
        this.filterExtensions(term, this.tab)
      }
    },
    methods: {
      handleTabChanged: function (tab) {
        this.filterExtensions(this.searchTerm, tab)
      },
      filterExtensions: function (term, tab) {
        // Filter the extensions list
        function extensionNameContains(searchTerm) {
          return function (extension) {
            return (
              extension.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
              extension.shortDescription
                ?.toLowerCase()
                .includes(searchTerm.toLowerCase())
            )
          }
        }

        this.filteredExtensions = this.extensions
          .filter(e => (tab === 'installed' ? e.isInstalled : true))
          .filter(e =>
            tab === 'installed'
              ? e.isActive
                ? true
                : !!this.g.user.admin
              : true
          )
          .filter(e => (tab === 'featured' ? e.isFeatured : true))
          .filter(extensionNameContains(term))
        this.tab = tab
      },
      installExtension: async function (release) {
        const extension = this.selectedExtension
        extension.inProgress = true
        this.showUpgradeDialog = false
        LNbits.api
          .request(
            'POST',
            `/api/v1/extension`,
            this.g.user.wallets[0].adminkey,
            {
              ext_id: extension.id,
              archive: release.archive,
              source_repo: release.source_repo
            }
          )
          .then(response => {
            extension.isAvailable = true
            extension.isInstalled = true
            extension.installedRelease = release
            this.toggleExtension(extension)
            extension.inProgress = false
            this.filteredExtensions = this.extensions.concat([])
            this.handleTabChanged('installed')
            this.tab = 'installed'
          })
          .catch(err => {
            LNbits.utils.notifyApiError(err)
            extension.inProgress = false
          })
      },
      uninstallExtension: async function () {
        const extension = this.selectedExtension
        this.showUpgradeDialog = false
        this.showUninstallDialog = false
        extension.inProgress = true
        LNbits.api
          .request(
            'DELETE',
            `/api/v1/extension/${extension.id}`,
            this.g.user.wallets[0].adminkey
          )
          .then(response => {
            extension.isAvailable = false
            extension.isInstalled = false
            extension.inProgress = false
            extension.installedRelease = null
            this.filteredExtensions = this.extensions.concat([])
            this.handleTabChanged('installed')
            this.tab = 'installed'
            this.$q.notify({
              type: 'positive',
              message: 'Extension uninstalled!'
            })
            if (this.uninstallAndDropDb) {
              this.showDropDb()
            }
          })
          .catch(err => {
            LNbits.utils.notifyApiError(err)
            extension.inProgress = false
          })
      },

      dropExtensionDb: async function () {
        const extension = this.selectedExtension
        this.showUpgradeDialog = false
        this.showDropDbDialog = false
        this.dropDbExtensionId = ''
        extension.inProgress = true
        LNbits.api
          .request(
            'DELETE',
            `/api/v1/extension/${extension.id}/db`,
            this.g.user.wallets[0].adminkey
          )
          .then(response => {
            extension.installedRelease = null
            extension.inProgress = false
            extension.hasDatabaseTables = false
            this.$q.notify({
              type: 'positive',
              message: 'Extension DB deleted!'
            })
          })
          .catch(err => {
            LNbits.utils.notifyApiError(err)
            extension.inProgress = false
          })
      },
      toggleExtension: function (extension) {
        const action = extension.isActive ? 'activate' : 'deactivate'
        LNbits.api
          .request(
            'GET',
            "{{ url_for('install.extensions') }}" +
              '?' +
              action +
              '=' +
              extension.id
          )
          .then(response => {})
          .catch(err => {
            LNbits.utils.notifyApiError(err)
            extension.inProgress = false
          })
      },

      showUninstall: function () {
        this.showUpgradeDialog = false
        this.showUninstallDialog = true
        this.uninstallAndDropDb = false
      },

      showDropDb: function () {
        this.showDropDbDialog = true
      },

      showUpgrade: async function (extension) {
        this.selectedExtension = extension
        this.showUpgradeDialog = true
        this.selectedExtensionRepos = null
        try {
          const {data} = await LNbits.api.request(
            'GET',
            `/api/v1/extension/${extension.id}/releases`,
            this.g.user.wallets[0].adminkey
          )

          this.selectedExtensionRepos = data.reduce((repos, release) => {
            repos[release.source_repo] = repos[release.source_repo] || {
              releases: [],
              isInstalled: false,
              repo: release.repo
            }
            release.inProgress = false
            release.error = null
            release.loaded = false
            release.isInstalled = this.isInstalledVersion(
              this.selectedExtension,
              release
            )
            if (release.isInstalled) {
              repos[release.source_repo].isInstalled = true
            }
            repos[release.source_repo].releases.push(release)
            return repos
          }, {})
        } catch (error) {
          LNbits.utils.notifyApiError(error)
          extension.inProgress = false
        }
      },
      hasNewVersion: function (extension) {
        if (extension.installedRelease && extension.latestRelease) {
          return (
            extension.installedRelease.version !==
            extension.latestRelease.version
          )
        }
      },
      isInstalledVersion: function (extension, release) {
        if (extension.installedRelease) {
          return (
            extension.installedRelease.source_repo === release.source_repo &&
            extension.installedRelease.version === release.version
          )
        }
      },
      getReleaseIcon: function (release) {
        if (!release.is_version_compatible) return 'block'
        if (release.isInstalled) return 'download_done'

        return 'download'
      },
      getReleaseIconColor: function (release) {
        if (!release.is_version_compatible) return 'text-red'
        if (release.isInstalled) return 'text-green'

        return ''
      },
      getGitHubReleaseDetails: async function (release) {
        if (!release.is_github_release || release.loaded) {
          return
        }
        const [org, repo] = release.source_repo.split('/')
        release.inProgress = true
        try {
          const {data} = await LNbits.api.request(
            'GET',
            `/api/v1/extension/release/${org}/${repo}/${release.version}`,
            this.g.user.wallets[0].adminkey
          )
          release.loaded = true
          release.is_version_compatible = data.is_version_compatible
          release.min_lnbits_version = data.min_lnbits_version
          release.warning = data.warning
        } catch (error) {
          console.warn(error)
          release.error = error
          LNbits.utils.notifyApiError(error)
        } finally {
          release.inProgress = false
        }
      }
    },
    created: function () {
      this.extensions = JSON.parse('{{extensions | tojson | safe}}').map(e => ({
        ...e,
        inProgress: false
      }))
      this.filteredExtensions = this.extensions.concat([])
      for (let i = 0; i < this.filteredExtensions.length; i++) {
        if (this.filteredExtensions[i].isInstalled != false) {
          this.handleTabChanged('installed')
          this.tab = 'installed'
        }
      }
      if (window.user) {
        this.user = LNbits.map.user(window.user)
      }
    },
    mixins: [windowMixin]
  })
</script>
{% endblock %}
