diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl index 16168a76b89d..0a8f427f9da1 100644 --- a/templates/user/dashboard/repolist.tmpl +++ b/templates/user/dashboard/repolist.tmpl @@ -51,148 +51,4 @@ data.canCreateOrganization = {{.SignedUser.CanCreateOrganization}} window.config.pageData.dashboardRepoList = data; -
- -
- - +
diff --git a/web_src/js/components/DashboardRepoList.js b/web_src/js/components/DashboardRepoList.js deleted file mode 100644 index f18a3cca601b..000000000000 --- a/web_src/js/components/DashboardRepoList.js +++ /dev/null @@ -1,288 +0,0 @@ -import {createApp, nextTick} from 'vue'; -import $ from 'jquery'; -import {initVueSvg, vueDelimiters} from './VueComponentLoader.js'; -import {initTooltip} from '../modules/tippy.js'; -import {SvgIcon} from '../svg.js'; - -const {appSubUrl, assetUrlPrefix, pageData} = window.config; - -function initVueComponents(app) { - app.component('repo-search', { - delimiters: vueDelimiters, - components: {SvgIcon}, - data() { - const params = new URLSearchParams(window.location.search); - const tab = params.get('repo-search-tab') || 'repos'; - const reposFilter = params.get('repo-search-filter') || 'all'; - const privateFilter = params.get('repo-search-private') || 'both'; - const archivedFilter = params.get('repo-search-archived') || 'unarchived'; - const searchQuery = params.get('repo-search-query') || ''; - const page = Number(params.get('repo-search-page')) || 1; - - return { - tab, - repos: [], - reposTotalCount: 0, - reposFilter, - archivedFilter, - privateFilter, - page, - finalPage: 1, - searchQuery, - isLoading: false, - staticPrefix: assetUrlPrefix, - counts: {}, - repoTypes: { - all: { - searchMode: '', - }, - forks: { - searchMode: 'fork', - }, - mirrors: { - searchMode: 'mirror', - }, - sources: { - searchMode: 'source', - }, - collaborative: { - searchMode: 'collaborative', - }, - }, - textArchivedFilterTitles: {}, - textPrivateFilterTitles: {}, - - organizations: [], - isOrganization: true, - canCreateOrganization: false, - organizationsTotalCount: 0, - - subUrl: appSubUrl, - ...pageData.dashboardRepoList, - }; - }, - - computed: { - // used in `repolist.tmpl` - showMoreReposLink() { - return this.repos.length > 0 && this.repos.length < this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`]; - }, - searchURL() { - return `${this.subUrl}/repo/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=${this.searchQuery - }&page=${this.page}&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode - }${this.reposFilter !== 'all' ? '&exclusive=1' : '' - }${this.archivedFilter === 'archived' ? '&archived=true' : ''}${this.archivedFilter === 'unarchived' ? '&archived=false' : '' - }${this.privateFilter === 'private' ? '&is_private=true' : ''}${this.privateFilter === 'public' ? '&is_private=false' : '' - }`; - }, - repoTypeCount() { - return this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`]; - }, - checkboxArchivedFilterTitle() { - return this.textArchivedFilterTitles[this.archivedFilter]; - }, - checkboxArchivedFilterProps() { - return {checked: this.archivedFilter === 'archived', indeterminate: this.archivedFilter === 'both'}; - }, - checkboxPrivateFilterTitle() { - return this.textPrivateFilterTitles[this.privateFilter]; - }, - checkboxPrivateFilterProps() { - return {checked: this.privateFilter === 'private', indeterminate: this.privateFilter === 'both'}; - }, - }, - - mounted() { - const el = document.getElementById('dashboard-repo-list'); - this.changeReposFilter(this.reposFilter); - for (const elTooltip of el.querySelectorAll('.tooltip')) { - initTooltip(elTooltip); - } - $(el).find('.dropdown').dropdown(); - nextTick(() => { - this.$refs.search.focus(); - }); - - this.textArchivedFilterTitles = { - 'archived': this.textShowOnlyArchived, - 'unarchived': this.textShowOnlyUnarchived, - 'both': this.textShowBothArchivedUnarchived, - }; - - this.textPrivateFilterTitles = { - 'private': this.textShowOnlyPrivate, - 'public': this.textShowOnlyPublic, - 'both': this.textShowBothPrivatePublic, - }; - }, - - methods: { - changeTab(t) { - this.tab = t; - this.updateHistory(); - }, - - changeReposFilter(filter) { - this.reposFilter = filter; - this.repos = []; - this.page = 1; - this.counts[`${filter}:${this.archivedFilter}:${this.privateFilter}`] = 0; - this.searchRepos(); - }, - - updateHistory() { - const params = new URLSearchParams(window.location.search); - - if (this.tab === 'repos') { - params.delete('repo-search-tab'); - } else { - params.set('repo-search-tab', this.tab); - } - - if (this.reposFilter === 'all') { - params.delete('repo-search-filter'); - } else { - params.set('repo-search-filter', this.reposFilter); - } - - if (this.privateFilter === 'both') { - params.delete('repo-search-private'); - } else { - params.set('repo-search-private', this.privateFilter); - } - - if (this.archivedFilter === 'unarchived') { - params.delete('repo-search-archived'); - } else { - params.set('repo-search-archived', this.archivedFilter); - } - - if (this.searchQuery === '') { - params.delete('repo-search-query'); - } else { - params.set('repo-search-query', this.searchQuery); - } - - if (this.page === 1) { - params.delete('repo-search-page'); - } else { - params.set('repo-search-page', `${this.page}`); - } - - const queryString = params.toString(); - if (queryString) { - window.history.replaceState({}, '', `?${queryString}`); - } else { - window.history.replaceState({}, '', window.location.pathname); - } - }, - - toggleArchivedFilter() { - if (this.archivedFilter === 'unarchived') { - this.archivedFilter = 'archived'; - } else if (this.archivedFilter === 'archived') { - this.archivedFilter = 'both'; - } else { // including both - this.archivedFilter = 'unarchived'; - } - this.page = 1; - this.repos = []; - this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0; - this.searchRepos(); - }, - - togglePrivateFilter() { - if (this.privateFilter === 'both') { - this.privateFilter = 'public'; - } else if (this.privateFilter === 'public') { - this.privateFilter = 'private'; - } else { // including private - this.privateFilter = 'both'; - } - this.page = 1; - this.repos = []; - this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0; - this.searchRepos(); - }, - - - changePage(page) { - this.page = page; - if (this.page > this.finalPage) { - this.page = this.finalPage; - } - if (this.page < 1) { - this.page = 1; - } - this.repos = []; - this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0; - this.searchRepos(); - }, - - async searchRepos() { - this.isLoading = true; - - const searchedMode = this.repoTypes[this.reposFilter].searchMode; - const searchedURL = this.searchURL; - const searchedQuery = this.searchQuery; - - let response, json; - try { - if (!this.reposTotalCount) { - const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`; - response = await fetch(totalCountSearchURL); - this.reposTotalCount = response.headers.get('X-Total-Count'); - } - - response = await fetch(searchedURL); - json = await response.json(); - } catch { - if (searchedURL === this.searchURL) { - this.isLoading = false; - } - return; - } - - if (searchedURL === this.searchURL) { - this.repos = json.data; - const count = response.headers.get('X-Total-Count'); - if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') { - this.reposTotalCount = count; - } - this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = count; - this.finalPage = Math.ceil(count / this.searchLimit); - this.updateHistory(); - this.isLoading = false; - } - }, - - repoIcon(repo) { - if (repo.fork) { - return 'octicon-repo-forked'; - } else if (repo.mirror) { - return 'octicon-mirror'; - } else if (repo.template) { - return `octicon-repo-template`; - } else if (repo.private) { - return 'octicon-lock'; - } else if (repo.internal) { - return 'octicon-repo'; - } - return 'octicon-repo'; - } - }, - - template: document.getElementById('dashboard-repo-list-template'), - }); -} - -export function initDashboardRepoList() { - const el = document.getElementById('dashboard-repo-list'); - const dashboardRepoListData = pageData.dashboardRepoList || null; - if (!el || !dashboardRepoListData) return; - - const app = createApp({delimiters: vueDelimiters}); - initVueSvg(app); - initVueComponents(app); - app.mount(el); -} diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue new file mode 100644 index 000000000000..9692fe605059 --- /dev/null +++ b/web_src/js/components/DashboardRepoList.vue @@ -0,0 +1,431 @@ + + + diff --git a/web_src/js/components/VueComponentLoader.js b/web_src/js/components/VueComponentLoader.js index 33ebf95eff96..5f3bb237e2c5 100644 --- a/web_src/js/components/VueComponentLoader.js +++ b/web_src/js/components/VueComponentLoader.js @@ -1,5 +1,4 @@ import {createApp} from 'vue'; -import {svgs} from '../svg.js'; export const vueDelimiters = ['${', '}']; @@ -14,29 +13,6 @@ export function initVueEnv() { // Vue.config.devtools = !isProd; } -let vueSvgInited = false; -export function initVueSvg(app) { - if (vueSvgInited) return; - vueSvgInited = true; - - // register svg icon vue components, e.g. - for (const [name, htmlString] of Object.entries(svgs)) { - const template = htmlString - .replace(/height="[0-9]+"/, 'v-bind:height="size"') - .replace(/width="[0-9]+"/, 'v-bind:width="size"'); - - app.component(name, { - props: { - size: { - type: String, - default: '16', - }, - }, - template, - }); - } -} - export function initVueApp(el, opts = {}) { if (typeof el === 'string') { el = document.querySelector(el); diff --git a/web_src/js/index.js b/web_src/js/index.js index 611c09d2b8c1..0469cbfa3391 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -4,7 +4,7 @@ import './bootstrap.js'; import $ from 'jquery'; import {initVueEnv} from './components/VueComponentLoader.js'; import {initRepoActivityTopAuthorsChart} from './components/RepoActivityTopAuthors.vue'; -import {initDashboardRepoList} from './components/DashboardRepoList.js'; +import {initDashboardRepoList} from './components/DashboardRepoList.vue'; import {attachTribute} from './features/tribute.js'; import {initGlobalCopyToClipboardListener} from './features/clipboard.js'; diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 85bfd501539e..e77d45d4d619 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -41,7 +41,7 @@ import giteaDoubleChevronRight from '../../public/img/svg/gitea-double-chevron-r import octiconChevronLeft from '../../public/img/svg/octicon-chevron-left.svg'; import octiconOrganization from '../../public/img/svg/octicon-organization.svg'; -export const svgs = { +const svgs = { 'octicon-blocked': octiconBlocked, 'octicon-check-circle-fill': octiconCheckCircleFill, 'octicon-chevron-down': octiconChevronDown,