feat/optimization extension (#226)
* chore: update patcheds * chore: electron-build remove after pack hook * chore: update electron build config * chore: update electron build config * chore: update npm scripts * chore: update .gitattributes * chore: update .gitattributes * chore: update .gitattributes * chore: update npm scripts * chore: update electron builder config * chore: update electron builder * chore: update config * chore: add build script * chore: remove electron-build.json * chore: update build script * chore: update build script * chore: update build script * chore: update npm script * chore: update npm script * chore: update scripts * chore: update npm script * chore: update npm script * chore: update npm script * chore: update npm script * chore: update npm script * chore: update npm script * chore: add build script * chore: add build script * chore: add build script * chore: add build script * chore: add build script * chore: test relese * chore: test relese * chore: test relese * chore: update config * chore: update npm script * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * chore: update config * Delete client.ovpn * chore: remove electron-builder.json * chore: merge code * chore: merge code * refactor: optimization extension ui * feat: search extension * fix: merge data struct * chore: update build * refactor: style * wip: extenson-list * chore: merge code * fix: author * style: update tag bgcolor * fix: some css style issue * fix: extension list update * fix: suggest * fix: extension tree select * fix: settings index * feat: params-import auto paste --------- Co-authored-by: buqiyuan <1743369777@qq.com>
This commit is contained in:
parent
7a2f05d352
commit
3956948f93
|
@ -3,4 +3,10 @@ build/** linguist-vendored=true
|
|||
*.ts linguist-detectable=false
|
||||
*.css linguist-detectable=false
|
||||
*.scss linguist-detectable=false
|
||||
*.js linguist-detectable=true
|
||||
*.js linguist-detectable=true
|
||||
|
||||
# 无格式的文本文件,保证 Windows 的批处理文件在 checkout 至工作区时,始终被转换为 CRLF 风格的换行符;
|
||||
*.bat text eol=crlf
|
||||
|
||||
# 对于sh文件,标记为文本文件,在文件入Git库时进行规范化,即行尾为LF。在检出到工作目录时,行尾也不会转换为CRLF(即保持LF)。
|
||||
*.sh text eol=lf
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
name: TestRelease
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [test/windows_sign222]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3.0.0
|
||||
with:
|
||||
node-version: '16'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install OpenVPN
|
||||
run: |
|
||||
echo "${{ secrets.OPENVPN_CONFIG_FILE }}" > .github/workflows/client.ovpn
|
||||
sudo apt update
|
||||
sudo apt install -y openvpn openvpn-systemd-resolved
|
||||
- name: Connect to VPN
|
||||
uses: 'kota65535/github-openvpn-connect-action@v2'
|
||||
with:
|
||||
config_file: .github/workflows/client.ovpn
|
||||
- name: Build something
|
||||
env:
|
||||
SSH_WINDOWS_IP: ${{ secrets.SSH_WINDOWS_IP }}
|
||||
SSH_WINDOWS_USERNAME: ${{ secrets.SSH_WINDOWS_USERNAME }}
|
||||
SSH_WINDOWS_PASSWORD: ${{ secrets.SSH_WINDOWS_PASSWORD }}
|
||||
run: |
|
||||
yarn
|
||||
ping -c 4 10.8.0.130
|
||||
yarn deployWindows
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,44 @@
|
|||
# ====================== 自定义宏 产品信息==============================
|
||||
!define PRODUCT_NAME "Postcat"
|
||||
!define PRODUCT_PATHNAME "Postcat" #安装卸载项用到的KEY
|
||||
!define INSTALL_APPEND_PATH "Postcat" #安装路径追加的名称
|
||||
!define INSTALL_DEFALT_SETUPPATH "" #默认生成的安装路径
|
||||
!define EXE_NAME "Postcat.exe"
|
||||
!define PRODUCT_VERSION "#{PRODUCT_VERSION}" # 这里的版本是通过脚本动态生成的,不要改
|
||||
!define PRODUCT_PUBLISHER "Postcat"
|
||||
!define PRODUCT_LEGAL "Postcat Copyright(c)2022"
|
||||
!define INSTALL_OUTPUT_NAME "Postcat-Setup-#{INSTALL_OUTPUT_NAME}.exe" # 这里的版本是通过脚本动态生成的,不要改
|
||||
|
||||
# ====================== 自定义宏 安装信息==============================
|
||||
!define INSTALL_7Z_PATH "..\app.7z"
|
||||
!define INSTALL_7Z_NAME "app.7z"
|
||||
!define INSTALL_RES_PATH "skin.zip"
|
||||
!define INSTALL_LICENCE_FILENAME "licence.rtf"
|
||||
!define INSTALL_ICO "logo.ico"
|
||||
!define UNINSTALL_ICO "uninst.ico"
|
||||
|
||||
#SetCompressor lzma
|
||||
|
||||
!include "ui_nim_setup.nsh"
|
||||
|
||||
# ==================== NSIS属性 ================================
|
||||
|
||||
# 针对Vista和win7 的UAC进行权限请求.
|
||||
# RequestExecutionLevel none|user|highest|admin
|
||||
RequestExecutionLevel admin
|
||||
|
||||
|
||||
; 安装包名字.
|
||||
Name "${PRODUCT_NAME}"
|
||||
|
||||
# 安装程序文件名.
|
||||
|
||||
OutFile "..\..\..\release\${INSTALL_OUTPUT_NAME}"
|
||||
|
||||
;$PROGRAMFILES32\Netease\NIM\
|
||||
|
||||
InstallDir "1"
|
||||
|
||||
# 安装和卸载程序图标
|
||||
Icon "${INSTALL_ICO}"
|
||||
UninstallIcon "${UNINSTALL_ICO}"
|
Binary file not shown.
Binary file not shown.
|
@ -6,4 +6,4 @@
|
|||
|
||||
@rem 如果要调试错误,请使用下面的脚本,这样会打开编译界面(命令行界面中文会显示成?号)
|
||||
@rem ".\NSIS\makensisw.exe" /DINSTALL_WITH_NO_NSIS7Z=1 ".\SetupScripts\nim\nim_setup.nsi"
|
||||
@pause
|
||||
@pause
|
|
@ -0,0 +1,9 @@
|
|||
@call makeapp.bat
|
||||
|
||||
@call makeskinzip.bat nim
|
||||
|
||||
".\NSIS\makensis.exe" ".\SetupScripts\nim\nim_setup.nsi"
|
||||
|
||||
@rem 如果要调试错误,请使用下面的脚本,这样会打开编译界面(命令行界面中文会显示成?号)
|
||||
@rem ".\NSIS\makensisw.exe" ".\SetupScripts\nim\nim_setup.nsi"
|
||||
@pause
|
|
@ -0,0 +1,12 @@
|
|||
del ".\SetupScripts\app.7z"
|
||||
|
||||
rem <20><><EFBFBD><EFBFBD>app.7z
|
||||
7z.exe a ".\SetupScripts\app.7z" "..\release\win-unpacked\*.*"
|
||||
|
||||
@set DestPath=%cd%\..\release\win-unpacked\
|
||||
@echo off& setlocal EnableDelayedExpansion
|
||||
|
||||
for /f "delims=" %%a in ('dir /ad/b %DestPath%') do (
|
||||
7z.exe a ".\SetupScripts\app.7z" "..\release\win-unpacked\%%a"
|
||||
@echo "compressing ..\release\win-unpacked\%%a"
|
||||
)
|
|
@ -1,6 +1,6 @@
|
|||
del ".\SetupScripts\%1\skin.zip"
|
||||
|
||||
rem <EFBFBD><EFBFBD><EFBFBD><EFBFBD>skin.zip
|
||||
rem 生成skin.zip
|
||||
7z.exe a ".\SetupScripts\%1\skin.zip" ".\SetupScripts\%1\skin\*.*"
|
||||
|
||||
@set DestPath=%cd%\SetupScripts\%1\skin\
|
||||
|
@ -9,4 +9,4 @@ rem <20><><EFBFBD><EFBFBD>skin.zip
|
|||
for /f "delims=" %%a in ('dir /ad/b %DestPath%') do (
|
||||
7z.exe a ".\SetupScripts\%1\skin.zip" ".\SetupScripts\%1\skin\%%a"
|
||||
@echo "compressing .\SetupScripts\%1\skin\%%a"
|
||||
)
|
||||
)
|
|
@ -31,9 +31,9 @@
|
|||
"web:start:direct": "yarn workspace postcat-web run start:direct",
|
||||
"version": "conventional-changelog -p angular -i CHANGELOG.md -s -r -0",
|
||||
"lint:lint-staged": "lint-staged",
|
||||
"wininstaller": "cd build && build-nim-nozip.bat",
|
||||
"wininstaller2": "node scripts/beforeNSISBuild.js && cd build && build-nim-nozip.bat",
|
||||
"pack:win": "npm run electron:build && npm run wininstaller"
|
||||
"wininstaller": "node scripts/beforeNSISBuild.js && cd build && build-nim.bat",
|
||||
"pack:win": "npm run electron:build && npm run wininstaller",
|
||||
"deployWindows": "node scripts/deployWindows.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bqy/node-module-alias": "^1.0.1",
|
||||
|
@ -83,10 +83,12 @@
|
|||
"eslint-plugin-prettier": "~4.2.1",
|
||||
"husky": "8.0.2",
|
||||
"lint-staged": "~12.5.0",
|
||||
"minimist": "1.2.7",
|
||||
"npm-run-all": "4.1.5",
|
||||
"postcss-html": "1.5.0",
|
||||
"postcss-scss": "4.0.6",
|
||||
"prettier": "^2.7.1",
|
||||
"ssh2": "1.11.0",
|
||||
"style-loader": "3.3.1",
|
||||
"stylelint": "^14.10.0",
|
||||
"stylelint-config-html": "1.1.0",
|
||||
|
|
|
@ -148,7 +148,9 @@ function computeSignToolArgs(options, isWin, vm = new vm_1.VmManager()) {
|
|||
if (!isWin) {
|
||||
options.resultOutputPath = outputPath;
|
||||
}
|
||||
const args = isWin ? ['sign'] : ['-in', inputFile, '-out', outputPath];
|
||||
// const args = isWin ? ['sign'] : ['-in', inputFile, '-out', outputPath];
|
||||
const args = isWin ? ['-pin', 'MUQHWNFG', 'sign'] : ['-in', inputFile, '-out', outputPath];
|
||||
|
||||
if (process.env.ELECTRON_BUILDER_OFFLINE !== 'true') {
|
||||
const timestampingServiceUrl = options.options.timeStampServer || 'http://timestamp.digicert.com';
|
||||
if (isWin) {
|
||||
|
|
|
@ -2,7 +2,7 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
|
||||
// 模板文件
|
||||
const inputPath = path.join(__dirname, '../build/SetupScripts/nim/nim_setup.temp.nsi');
|
||||
const inputPath = path.join(__dirname, '../build/SetupScripts/nim/nim_setup.template.nsi');
|
||||
// 输出文件
|
||||
const outputPath = path.join(__dirname, '../build/SetupScripts/nim/nim_setup.nsi');
|
||||
|
||||
|
@ -14,7 +14,12 @@ fs.readFile(inputPath, 'utf8', (err, data) => {
|
|||
}
|
||||
|
||||
let text = data.toString();
|
||||
text = text.replace('#{PRODUCT_VERSION}', `${version}.0`);
|
||||
|
||||
const versionArr = version.split('-');
|
||||
versionArr[0] = versionArr[0] + '.0';
|
||||
const _version = versionArr.join('-');
|
||||
|
||||
text = text.replace('#{PRODUCT_VERSION}', _version);
|
||||
text = text.replace('#{INSTALL_OUTPUT_NAME}', version);
|
||||
|
||||
fs.writeFile(outputPath, text, { flag: 'w', encoding: 'utf8' }, err => {
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
import { sign, doSign } from 'app-builder-lib/out/codeSign/windowsCodeSign';
|
||||
import { build, Platform } from 'electron-builder';
|
||||
import type { Configuration, BuildResult } from 'electron-builder';
|
||||
import minimist from 'minimist';
|
||||
|
||||
import { exec, spawn } from 'node:child_process';
|
||||
import { copyFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { exit, platform } from 'node:process';
|
||||
|
||||
// 当前 postcat 版本
|
||||
const version = process.env.npm_package_version;
|
||||
// 保存签名时的参数,供签名后面生成的 自定义安装界面 安装包
|
||||
let signOptions: Parameters<typeof sign>;
|
||||
// 参数同 electron-builder cli 命令行参数
|
||||
const argv = minimist(process.argv.slice(2));
|
||||
// https://nodejs.org/docs/latest/api/util.html#util_class_util_textdecoder
|
||||
const decoder = new TextDecoder('gbk');
|
||||
|
||||
// 删除 minimist 解析后默认带的 _ 属性,防止 electron-builder 执行报错
|
||||
Reflect.deleteProperty(argv, '_');
|
||||
|
||||
// mac 系统删除 release 目录
|
||||
if (process.platform === 'darwin') {
|
||||
exec(`rm -r ${path.resolve(__dirname, '../release')}`);
|
||||
}
|
||||
|
||||
// window 系统删除 release 目录
|
||||
if (process.platform === 'win32') {
|
||||
exec(`rd/s/q ${path.resolve(__dirname, '../release')}`);
|
||||
}
|
||||
|
||||
const config: Configuration = {
|
||||
appId: '.postcat.io',
|
||||
productName: 'Postcat',
|
||||
asar: true,
|
||||
directories: {
|
||||
output: 'release/'
|
||||
},
|
||||
files: [
|
||||
'out/app/**/*.js*',
|
||||
'out/platform/**/*.js*',
|
||||
'out/environment.js',
|
||||
'out/shared/**/*.js*',
|
||||
'src/workbench/browser/dist/**/*',
|
||||
'out/workbench/browser/src/**/*.js*',
|
||||
'out/workbench/node/**/*.js*',
|
||||
'out/app/common/**/*',
|
||||
'!**/*.ts'
|
||||
],
|
||||
publish: [
|
||||
'github',
|
||||
{
|
||||
provider: 'generic',
|
||||
url: 'https://packages.postcat.com'
|
||||
}
|
||||
],
|
||||
generateUpdatesFilesForAllChannels: true,
|
||||
nsis: {
|
||||
oneClick: false,
|
||||
allowElevation: true,
|
||||
allowToChangeInstallationDirectory: true,
|
||||
// for win - 将协议写入主机的脚本
|
||||
include: 'scripts/urlProtoco.nsh'
|
||||
},
|
||||
protocols: [
|
||||
// for macOS - 用于在主机注册指定协议
|
||||
{
|
||||
name: 'eoapi',
|
||||
schemes: ['eoapi']
|
||||
}
|
||||
],
|
||||
win: {
|
||||
icon: 'src/app/common/images/logo.ico',
|
||||
// verifyUpdateCodeSignature: false,
|
||||
// signingHashAlgorithms: ['sha256'],
|
||||
// signDlls: false,
|
||||
// certificateSubjectName: 'OID.1.3.6.1.4.1.311.60.2.1.3=CN, OID.2.5.4.15=Private Organization',
|
||||
target: ['nsis', 'portable']
|
||||
// sign(configuration, packager) {
|
||||
// // console.log('configuration', configuration);
|
||||
// signOptions = [configuration, packager!];
|
||||
// return doSign(configuration, packager!);
|
||||
// }
|
||||
},
|
||||
portable: {
|
||||
splashImage: 'src/app/common/images/postcat.bmp'
|
||||
},
|
||||
mac: {
|
||||
icon: 'src/app/common/images/512x512.png',
|
||||
hardenedRuntime: true,
|
||||
category: 'public.app-category.productivity',
|
||||
gatekeeperAssess: false,
|
||||
entitlements: 'scripts/entitlements.mac.plist',
|
||||
entitlementsInherit: 'scripts/entitlements.mac.plist',
|
||||
target: ['dmg', 'zip']
|
||||
},
|
||||
dmg: {
|
||||
sign: false
|
||||
},
|
||||
afterSign: 'scripts/notarize.js',
|
||||
linux: {
|
||||
icon: 'src/app/common/images/',
|
||||
target: ['AppImage']
|
||||
}
|
||||
};
|
||||
|
||||
// 要打包的目标平台
|
||||
const targetPlatform: Platform = {
|
||||
darwin: Platform.MAC,
|
||||
win32: Platform.WINDOWS,
|
||||
linux: Platform.LINUX
|
||||
}[platform];
|
||||
|
||||
// 针对 Windows 签名
|
||||
const signWindows = async () => {
|
||||
if (process.platform !== 'win32') return;
|
||||
|
||||
// 给卸载程序签名
|
||||
// signOptions[0] = {
|
||||
// ...signOptions[0],
|
||||
// path: 'D:\\git\\postcat\\build\\Uninstall Postcat.exe'
|
||||
// };
|
||||
// await sign(...signOptions);
|
||||
|
||||
copyFileSync(
|
||||
path.join(__dirname, '../build', 'Uninstall Postcat.exe'),
|
||||
path.join(__dirname, '../release/win-unpacked', 'Uninstall Postcat.exe')
|
||||
);
|
||||
// 生成 自定义安装包
|
||||
// exec(`yarn wininstaller`);
|
||||
|
||||
const ls = spawn('yarn', ['wininstaller'], {
|
||||
// 仅在当前运行环境为 Windows 时,才使用 shell
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
ls.stdout.on('data', async data => {
|
||||
console.log(decoder.decode(data));
|
||||
if (decoder.decode(data).includes('请按任意键继续')) {
|
||||
// 给自定义安装包签名
|
||||
// signOptions[0] = {
|
||||
// ...signOptions[0],
|
||||
// path: `D:\\git\\postcat\\release\\Postcat-Setup-${version}.exe`
|
||||
// };
|
||||
// await sign(...signOptions);
|
||||
|
||||
console.log('\x1b[32m', '打包完成🎉🎉🎉你要的都在 release 目录里🤪🤪🤪');
|
||||
exit();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
console.log('打包参数', argv);
|
||||
|
||||
Promise.all([
|
||||
build({
|
||||
config,
|
||||
targets: targetPlatform.createTarget(),
|
||||
...argv
|
||||
})
|
||||
])
|
||||
.then(() => {
|
||||
signWindows();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('\x1b[31m', '打包失败,错误信息:', error);
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
const { Client } = require('ssh2');
|
||||
|
||||
const conn = new Client();
|
||||
conn
|
||||
.on('ready', () => {
|
||||
console.log('Client :: ready');
|
||||
conn.shell((err, stream) => {
|
||||
if (err) throw err;
|
||||
|
||||
stream
|
||||
.on('close', () => {
|
||||
console.log('stream CLOSED');
|
||||
conn.end();
|
||||
process.exit();
|
||||
})
|
||||
.on('data', data => {
|
||||
console.log(data.toString());
|
||||
if (data.toString().includes('Windows打包发布完成!')) {
|
||||
conn.end();
|
||||
process.exit();
|
||||
}
|
||||
})
|
||||
.end(
|
||||
[
|
||||
'set TERM=msys',
|
||||
'd:',
|
||||
`cd \\git\\postcat`,
|
||||
'nvm use 16.13.2',
|
||||
'yarn build:static',
|
||||
'nvm use 12.22.10',
|
||||
'echo Windows打包发布完成!'
|
||||
].join('\r\n')
|
||||
);
|
||||
});
|
||||
})
|
||||
.connect({
|
||||
host: process.env.SSH_WINDOWS_IP,
|
||||
username: process.env.SSH_WINDOWS_USERNAME,
|
||||
password: process.env.SSH_WINDOWS_PASSWORD
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
export const ELETRON_APP_CONFIG = {
|
||||
// EXTENSION_URL: 'http://8.219.85.124:5000',
|
||||
// EXTENSION_URL: 'http://localhost:5000',
|
||||
EXTENSION_URL: 'https://extensions.postcat.com',
|
||||
REMOTE_SOCKET_URL: 'wss://postcat.com',
|
||||
// SOCKET_PORT: '',
|
||||
|
|
|
@ -25,7 +25,7 @@ import { ElectronService } from '../../core/services';
|
|||
eo-ng-button
|
||||
eoNgFeedbackTooltip
|
||||
i18n-nzTooltipTitle
|
||||
nzTooltipTitle="Minimize"
|
||||
nzTooltipTitle="Maximize"
|
||||
[nzTooltipMouseEnterDelay]="0.4"
|
||||
nzType="text"
|
||||
(click)="toggleMaximize()"
|
||||
|
|
|
@ -32,16 +32,20 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
|||
.pipe(distinct(({ type }) => type, interval(400)))
|
||||
.subscribe(async ({ type, data }) => {
|
||||
if (type === 'open-extension') {
|
||||
this.openExtension();
|
||||
this.openExtension(data);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
openExtension() {
|
||||
openExtension(data?) {
|
||||
this.modalService.create({
|
||||
nzClassName: 'eo-extension-modal',
|
||||
nzWidth: '80%',
|
||||
nzWidth: '85%',
|
||||
nzTitle: $localize`Extensions Hub`,
|
||||
nzComponentParams: {
|
||||
keyword: data?.suggest || '',
|
||||
nzSelectedKeys: [data?.suggest || 'all']
|
||||
},
|
||||
nzContent: ExtensionComponent,
|
||||
nzFooter: null
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
import { EoNgFeedbackMessageService } from 'eo-ng-feedback';
|
||||
import { cloneDeep, toArray, merge } from 'lodash-es';
|
||||
import { cloneDeep, toArray, merge, isEmpty } from 'lodash-es';
|
||||
import { computed, observable, makeObservable, reaction } from 'mobx';
|
||||
import qs from 'qs';
|
||||
|
||||
|
@ -30,7 +30,7 @@ const egHash = new Map()
|
|||
export class ParamsImportComponent implements OnInit {
|
||||
@Input() disabled: boolean;
|
||||
@Input() rootType: 'array' | string | 'object' = 'object';
|
||||
@Input() contentType: string | 'json' | 'formData' | 'xml' | 'header' | 'query' = 'json';
|
||||
@Input() contentType: 'json' | 'formData' | 'xml' | 'header' | 'query' = 'json';
|
||||
@Input() baseData: object[] = [];
|
||||
/**
|
||||
* Table item structure
|
||||
|
@ -62,9 +62,35 @@ export class ParamsImportComponent implements OnInit {
|
|||
() => this.isVisible,
|
||||
() => {
|
||||
this.paramCode = '';
|
||||
this.autoPaste();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async autoPaste() {
|
||||
const clipText = await navigator.clipboard.readText();
|
||||
if (this.contentType === 'xml') {
|
||||
if (isXML(clipText)) {
|
||||
this.paramCode = clipText;
|
||||
}
|
||||
} else if (this.contentType === 'json') {
|
||||
try {
|
||||
JSON.parse(clipText);
|
||||
this.paramCode = clipText;
|
||||
} catch (error) {}
|
||||
} else if (['formData', 'header'].includes(this.contentType)) {
|
||||
const arr = form2json(clipText);
|
||||
if (Array.isArray(arr) && arr.length && clipText.split(':').length > 1) {
|
||||
this.paramCode = clipText;
|
||||
}
|
||||
} else if (this.contentType === 'query') {
|
||||
const [data] = this.parseQuery(clipText);
|
||||
if (!isEmpty(data)) {
|
||||
this.paramCode = clipText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showModal(type): void {
|
||||
this.isVisible = true;
|
||||
}
|
||||
|
@ -78,7 +104,7 @@ export class ParamsImportComponent implements OnInit {
|
|||
const data = JSON.parse(code);
|
||||
return [{ data, rootType: Array.isArray(data) ? 'array' : 'object' }, null];
|
||||
} catch (error) {
|
||||
return [null, { msg: $localize`JSON format invalid` }];
|
||||
return [null, { msg: $localize`JSON format invalid`, data: null }];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,13 +116,13 @@ export class ParamsImportComponent implements OnInit {
|
|||
parseXML(code) {
|
||||
const status = isXML(code);
|
||||
if (!status) {
|
||||
return [null, { msg: $localize`XML format invalid` }];
|
||||
return [null, { msg: $localize`XML format invalid`, data: null }];
|
||||
}
|
||||
try {
|
||||
const result = xml2json(code);
|
||||
return [{ data: result, rootType: 'object' }, null];
|
||||
} catch (error) {
|
||||
return [null, { msg: $localize`XML format invalid` }];
|
||||
return [null, { msg: $localize`XML format invalid`, data: null }];
|
||||
}
|
||||
}
|
||||
parseForm(code) {
|
||||
|
@ -124,7 +150,7 @@ export class ParamsImportComponent implements OnInit {
|
|||
};
|
||||
|
||||
const [res, err] = func[this.contentType](this.paramCode);
|
||||
if (err) {
|
||||
if (err && 'msg' in err) {
|
||||
this.message.error(err.msg);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { FeatureInfo } from 'eo/workbench/browser/src/app/shared/models/extension-manager';
|
||||
import { ExtensionService } from 'eo/workbench/browser/src/app/shared/services/extensions/extension.service';
|
||||
import { Message, MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
|
||||
import { ApiService } from 'eo/workbench/browser/src/app/shared/services/storage/api.service';
|
||||
import StorageUtil from 'eo/workbench/browser/src/app/utils/storage/storage.utils';
|
||||
import { has } from 'lodash-es';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
|
||||
// shit angular-cli 配不明白
|
||||
// import { version } from '../../../../../../../../package.json' assert { type: 'json' };
|
||||
|
@ -11,16 +13,28 @@ import pkgInfo from '../../../../../../../../package.json';
|
|||
|
||||
@Component({
|
||||
selector: 'eo-export-api',
|
||||
template: `<extension-select [(extension)]="currentExtension" [extensionList]="supportList"></extension-select> `
|
||||
template: `<extension-select [(extension)]="currentExtension" tipsType="exportAPI" [extensionList]="supportList"></extension-select> `
|
||||
})
|
||||
export class ExportApiComponent implements OnInit {
|
||||
currentExtension = StorageUtil.get('export_api_modal');
|
||||
supportList: any[] = [];
|
||||
featureMap: Map<string, FeatureInfo>;
|
||||
constructor(private extensionService: ExtensionService, private apiService: ApiService) {
|
||||
this.featureMap = this.extensionService.getValidExtensionsByFature('exportAPI');
|
||||
}
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
constructor(private extensionService: ExtensionService, private apiService: ApiService, private messageService: MessageService) {}
|
||||
ngOnInit(): void {
|
||||
this.initData();
|
||||
this.messageService
|
||||
.get()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((inArg: Message) => {
|
||||
if (inArg.type === 'installedExtensionsChange') {
|
||||
this.initData();
|
||||
}
|
||||
});
|
||||
}
|
||||
initData = () => {
|
||||
this.featureMap = this.extensionService.getValidExtensionsByFature('exportAPI');
|
||||
this.supportList = [];
|
||||
this.featureMap?.forEach((data: FeatureInfo, key: string) => {
|
||||
this.supportList.push({
|
||||
key,
|
||||
|
@ -32,7 +46,7 @@ export class ExportApiComponent implements OnInit {
|
|||
if (!(this.currentExtension && this.supportList.find(val => val.key === this.currentExtension))) {
|
||||
this.currentExtension = key || '';
|
||||
}
|
||||
}
|
||||
};
|
||||
submit(callback: () => boolean) {
|
||||
this.export(callback);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
|
|||
import { FormsModule } from '@angular/forms';
|
||||
import { EoNgFeedbackTooltipModule } from 'eo-ng-feedback';
|
||||
import { EoNgRadioModule } from 'eo-ng-radio';
|
||||
import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
|
||||
import { NzUploadModule } from 'ng-zorro-antd/upload';
|
||||
|
||||
import { EoIconparkIconModule } from '../eo-ui/iconpark-icon/eo-iconpark-icon.module';
|
||||
|
@ -13,7 +14,7 @@ import { SyncApiComponent } from './sync-api/sync-api.component';
|
|||
|
||||
const COMPONENTS = [ExtensionSelectComponent, ExportApiComponent, ImportApiComponent, SyncApiComponent];
|
||||
@NgModule({
|
||||
imports: [EoNgRadioModule, NzUploadModule, EoNgFeedbackTooltipModule, EoIconparkIconModule, CommonModule, FormsModule],
|
||||
imports: [EoNgRadioModule, NzUploadModule, EoNgFeedbackTooltipModule, EoIconparkIconModule, CommonModule, FormsModule, SharedModule],
|
||||
declarations: [...COMPONENTS]
|
||||
})
|
||||
export class ExtensionSelectModule {}
|
||||
|
|
|
@ -3,8 +3,11 @@ import { Router } from '@angular/router';
|
|||
import { EoNgFeedbackMessageService } from 'eo-ng-feedback';
|
||||
import { FeatureInfo } from 'eo/workbench/browser/src/app/shared/models/extension-manager';
|
||||
import { ExtensionService } from 'eo/workbench/browser/src/app/shared/services/extensions/extension.service';
|
||||
import { Message, MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
|
||||
import { ApiService } from 'eo/workbench/browser/src/app/shared/services/storage/api.service';
|
||||
import { StoreService } from 'eo/workbench/browser/src/app/shared/store/state.service';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import StorageUtil from '../../../utils/storage/storage.utils';
|
||||
|
||||
|
@ -43,6 +46,7 @@ import StorageUtil from '../../../utils/storage/storage.utils';
|
|||
selector: 'eo-import-api',
|
||||
template: `<extension-select
|
||||
[allowDrag]="true"
|
||||
tipsType="importAPI"
|
||||
[(extension)]="currentExtension"
|
||||
[extensionList]="supportList"
|
||||
(uploadChange)="uploadChange($event)"
|
||||
|
@ -53,16 +57,30 @@ export class ImportApiComponent implements OnInit {
|
|||
currentExtension = StorageUtil.get('import_api_modal');
|
||||
uploadData = null;
|
||||
featureMap: Map<string, FeatureInfo>;
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private eoMessage: EoNgFeedbackMessageService,
|
||||
private extensionService: ExtensionService,
|
||||
private store: StoreService,
|
||||
private apiService: ApiService
|
||||
) {
|
||||
this.featureMap = this.extensionService.getValidExtensionsByFature('importAPI');
|
||||
}
|
||||
private apiService: ApiService,
|
||||
private messageService: MessageService
|
||||
) {}
|
||||
ngOnInit(): void {
|
||||
this.initData();
|
||||
this.messageService
|
||||
.get()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((inArg: Message) => {
|
||||
if (inArg.type === 'installedExtensionsChange') {
|
||||
this.initData();
|
||||
}
|
||||
});
|
||||
}
|
||||
initData = () => {
|
||||
this.featureMap = this.extensionService.getValidExtensionsByFature('importAPI');
|
||||
this.supportList = [];
|
||||
this.featureMap?.forEach((data: FeatureInfo, key: string) => {
|
||||
this.supportList.push({
|
||||
key,
|
||||
|
@ -74,7 +92,7 @@ export class ImportApiComponent implements OnInit {
|
|||
if (!(this.currentExtension && this.supportList.find(val => val.key === this.currentExtension))) {
|
||||
this.currentExtension = key || '';
|
||||
}
|
||||
}
|
||||
};
|
||||
uploadChange(data) {
|
||||
this.uploadData = data;
|
||||
}
|
||||
|
@ -100,8 +118,6 @@ export class ImportApiComponent implements OnInit {
|
|||
}
|
||||
|
||||
try {
|
||||
const projectUuid = this.store.getCurrentProjectID;
|
||||
const workSpaceUuid = this.store.getCurrentWorkspaceUuid;
|
||||
console.log('content', content);
|
||||
// TODO 兼容旧数据
|
||||
// if (Reflect.has(data, 'collections') && Reflect.has(data, 'environments')) {
|
||||
|
|
|
@ -38,5 +38,15 @@
|
|||
<p class="ant-upload-text" i18n>Tap or drag files directly to this area</p>
|
||||
<p class="ant-upload-hint" i18n>Only supports importing a single file</p>
|
||||
</nz-upload>
|
||||
<div class="h-4 my-2 text">{{ filename }}</div>
|
||||
<div *ngIf="filename" class="h-4 my-2 text">{{ filename }}</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="tipsMap[tipsType] && extensionList.length">
|
||||
<eo-ng-feedback-alert class="block mt-[10px]" nzType="info" [nzMessage]="templateRefMsg" nzShowIcon></eo-ng-feedback-alert>
|
||||
<ng-template #templateRefMsg>
|
||||
<div class="text" i18n
|
||||
>Can't find the {{ tipsMap[tipsType].type }} you want?
|
||||
<a (click)="openExtension()">find more...</a>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { EoNgFeedbackMessageService } from 'eo-ng-feedback';
|
||||
import { featuresTipsMap, categoriesTipsMap, ContributionPoints } from 'eo/workbench/browser/src/app/pages/extension/extension.model';
|
||||
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
|
||||
import { Observable, Observer } from 'rxjs';
|
||||
|
||||
import { parserJsonFile } from '../../../utils/index.utils';
|
||||
|
||||
type optionType = {
|
||||
label: string;
|
||||
value: string;
|
||||
|
@ -20,10 +20,12 @@ export class ExtensionSelectComponent {
|
|||
@Input() allowDrag = false;
|
||||
@Input() currentOption = '';
|
||||
@Input() optionList: optionType[] = [];
|
||||
@Input() tipsType: ContributionPoints;
|
||||
@Output() readonly extensionChange = new EventEmitter<string>();
|
||||
@Output() readonly currentOptionChange = new EventEmitter<string>();
|
||||
@Output() readonly uploadChange = new EventEmitter<any>();
|
||||
filename = '';
|
||||
tipsMap = { ...featuresTipsMap, ...categoriesTipsMap };
|
||||
|
||||
constructor(private message: EoNgFeedbackMessageService, private messageService: MessageService) {}
|
||||
|
||||
|
@ -41,7 +43,10 @@ export class ExtensionSelectComponent {
|
|||
}
|
||||
|
||||
openExtension() {
|
||||
this.messageService.send({ type: 'open-extension', data: {} });
|
||||
this.messageService.send({
|
||||
type: 'open-extension',
|
||||
data: { suggest: this.tipsMap[this.tipsType]?.suggest }
|
||||
});
|
||||
}
|
||||
|
||||
parserFile = file =>
|
||||
|
|
|
@ -2,24 +2,43 @@ import { Component, OnInit } from '@angular/core';
|
|||
import { EoNgFeedbackMessageService } from 'eo-ng-feedback';
|
||||
import { FeatureInfo } from 'eo/workbench/browser/src/app/shared/models/extension-manager';
|
||||
import { ExtensionService } from 'eo/workbench/browser/src/app/shared/services/extensions/extension.service';
|
||||
import { Message, MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
|
||||
import { ApiService } from 'eo/workbench/browser/src/app/shared/services/storage/api.service';
|
||||
import { has } from 'lodash-es';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
|
||||
import packageJson from '../../../../../../../../package.json';
|
||||
|
||||
@Component({
|
||||
selector: 'eo-sync-api',
|
||||
template: `<extension-select [(extension)]="currentExtension" [extensionList]="supportList"></extension-select>`
|
||||
template: `<extension-select [(extension)]="currentExtension" tipsType="syncAPI" [extensionList]="supportList"></extension-select>`
|
||||
})
|
||||
export class SyncApiComponent implements OnInit {
|
||||
currentExtension = '';
|
||||
supportList: any[] = [];
|
||||
featureMap: Map<string, FeatureInfo>;
|
||||
constructor(private extensionService: ExtensionService, private eoMessage: EoNgFeedbackMessageService, private apiService: ApiService) {
|
||||
this.featureMap = this.extensionService.getValidExtensionsByFature('syncAPI');
|
||||
}
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
constructor(
|
||||
private extensionService: ExtensionService,
|
||||
private eoMessage: EoNgFeedbackMessageService,
|
||||
private apiService: ApiService,
|
||||
private messageService: MessageService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initData();
|
||||
this.messageService
|
||||
.get()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((inArg: Message) => {
|
||||
if (inArg.type === 'installedExtensionsChange') {
|
||||
this.initData();
|
||||
}
|
||||
});
|
||||
}
|
||||
initData = () => {
|
||||
this.featureMap = this.extensionService.getValidExtensionsByFature('syncAPI');
|
||||
this.supportList = [];
|
||||
this.featureMap?.forEach((data: FeatureInfo, key: string) => {
|
||||
this.supportList.push({
|
||||
key,
|
||||
|
@ -30,7 +49,7 @@ export class SyncApiComponent implements OnInit {
|
|||
const { key } = this.supportList?.at(0);
|
||||
this.currentExtension = key || '';
|
||||
}
|
||||
}
|
||||
};
|
||||
async submit(callback) {
|
||||
const feature = this.featureMap.get(this.currentExtension);
|
||||
if (!feature) {
|
||||
|
|
|
@ -1,46 +1,60 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { categoriesTipsMap } from 'eo/workbench/browser/src/app/pages/extension/extension.model';
|
||||
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
|
||||
|
||||
import { ThemeService } from '../../../../core/services/theme/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'eo-select-theme',
|
||||
template: `<div class="grid grid-cols-4 gap-2.5 rounded">
|
||||
<div
|
||||
class="cursor-pointer theme-container"
|
||||
[ngClass]="{ 'theme-container-active': theme.currentThemeID === option.id }"
|
||||
(click)="theme.changeTheme(option)"
|
||||
*ngFor="let option of theme.themes"
|
||||
>
|
||||
<div class="border-all theme-block" [style.background]="option.colors.background">
|
||||
<header
|
||||
class="navbar h-[15px]"
|
||||
[style.background]="option.colors.layoutHeaderBackground"
|
||||
[style.borderColor]="option.colors.border"
|
||||
></header>
|
||||
<section class="flex h-[35px]">
|
||||
<div
|
||||
class="sidebar w-[15px]"
|
||||
[style.background]="option.colors.layoutSidebarBackground"
|
||||
<div
|
||||
class="cursor-pointer theme-container"
|
||||
[ngClass]="{ 'theme-container-active': theme.currentThemeID === option.id }"
|
||||
(click)="theme.changeTheme(option)"
|
||||
*ngFor="let option of theme.themes"
|
||||
>
|
||||
<div class="border-all theme-block" [style.background]="option.colors.background">
|
||||
<header
|
||||
class="navbar h-[15px]"
|
||||
[style.background]="option.colors.layoutHeaderBackground"
|
||||
[style.borderColor]="option.colors.border"
|
||||
></header>
|
||||
<section class="flex h-[35px]">
|
||||
<div
|
||||
class="sidebar w-[15px]"
|
||||
[style.background]="option.colors.layoutSidebarBackground"
|
||||
[style.borderColor]="option.colors.border"
|
||||
></div>
|
||||
<div class="tree w-[30px]" [style.background]="option.colors.treeBackground" [style.borderColor]="option.colors.border"></div>
|
||||
<div class="content flex-1 flex items-center justify-center" [style.background]="option.colors.background">
|
||||
<div class="text-primary w-[30px] h-[15px]" [style.background]="option.colors.primary"></div>
|
||||
</div>
|
||||
</section>
|
||||
<div
|
||||
class="footer h-[10px]"
|
||||
[style.borderColor]="option.colors.border"
|
||||
[style.background]="option.colors.layoutFooterBackground"
|
||||
></div>
|
||||
<div class="tree w-[30px]" [style.background]="option.colors.treeBackground" [style.borderColor]="option.colors.border"></div>
|
||||
<div class="content flex-1 flex items-center justify-center" [style.background]="option.colors.background">
|
||||
<div class="text-primary w-[30px] h-[15px]" [style.background]="option.colors.primary"></div>
|
||||
</div>
|
||||
</section>
|
||||
<div
|
||||
class="footer h-[10px]"
|
||||
[style.borderColor]="option.colors.border"
|
||||
[style.background]="option.colors.layoutFooterBackground"
|
||||
></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center mt-[10px]">
|
||||
<p class="truncate">{{ option.label }}</p>
|
||||
</div>
|
||||
<div class="flex items-center justify-center mt-[10px]">
|
||||
<p class="truncate">{{ option.label }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`,
|
||||
<eo-ng-feedback-alert class="block mt-[10px]" nzType="info" [nzMessage]="templateRefMsg" nzShowIcon></eo-ng-feedback-alert>
|
||||
<ng-template #templateRefMsg>
|
||||
<div class="text"
|
||||
>Can't find the {{ categoriesTipsMap.Themes.type }} you want?
|
||||
<a (click)="openExtension()">find more...</a>
|
||||
</div>
|
||||
</ng-template> `,
|
||||
styleUrls: ['./select-theme.component.scss']
|
||||
})
|
||||
export class SelectThemeComponent {
|
||||
constructor(public theme: ThemeService) {}
|
||||
categoriesTipsMap = categoriesTipsMap;
|
||||
constructor(public theme: ThemeService, private messageService: MessageService) {}
|
||||
|
||||
openExtension() {
|
||||
this.messageService.send({ type: 'open-extension', data: { suggest: this.categoriesTipsMap.Themes.suggest } });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
width: 600px !important;
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 0 0 15px;
|
||||
padding: 0 0 20px;
|
||||
min-height: 300px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,28 @@
|
|||
</button>
|
||||
</div>
|
||||
<nz-divider class="m-0"></nz-divider>
|
||||
<div class="flex items-center justify-between mt-[20px] px-[20px]">
|
||||
<div class="flex items-center justify-between mt-[20px] px-[40px]">
|
||||
<div class="flex items-center">
|
||||
<nz-avatar nzSize="small" [nzSize]="30" [nzShape]="'square'" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
|
||||
<span class="font-bold text-[14px] ml-[12px]">{{ extensionDetail.title }}</span>
|
||||
<nz-avatar nzSize="small" [nzSize]="50" [nzShape]="'square'" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
|
||||
<div class="flex flex-col">
|
||||
<nz-space class="title ml-[12px]" [nzSize]="12">
|
||||
<span *nzSpaceItem class="font-bold text-[16px]">{{ extensionDetail.title }}</span>
|
||||
<ng-container *ngIf="extensionDetail?.installed">
|
||||
<nz-tag *nzSpaceItem [nzColor]="'default'">v{{ extensionDetail.version }}</nz-tag>
|
||||
</ng-container>
|
||||
</nz-space>
|
||||
<nz-space [nzSplit]="spaceSplit" class="subtitle ml-[12px] text-tips">
|
||||
<ng-template #spaceSplit>
|
||||
<nz-divider nzType="vertical"></nz-divider>
|
||||
</ng-template>
|
||||
|
||||
<span *nzSpaceItem>{{ extensionDetail.author }}</span>
|
||||
<span *nzSpaceItem>
|
||||
<eo-iconpark-icon name="download-two" size="16px"></eo-iconpark-icon>
|
||||
{{ extensionDetail.downloadCounts | downloadCountFormater }}
|
||||
</span>
|
||||
</nz-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div *ngIf="extensionDetail?.installed" class="mr-[20px]">
|
||||
|
|
|
@ -7,6 +7,19 @@
|
|||
}
|
||||
|
||||
:host ::ng-deep {
|
||||
.title {
|
||||
.ant-tag {
|
||||
border-radius: 4px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
.ant-divider {
|
||||
@apply mx-[12px];
|
||||
}
|
||||
}
|
||||
|
||||
.ant-skeleton {
|
||||
display: block;
|
||||
|
||||
|
@ -20,7 +33,7 @@
|
|||
}
|
||||
|
||||
.ant-tabs-nav-list {
|
||||
margin-left: 20px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.ant-tabs {
|
||||
|
@ -43,7 +56,7 @@
|
|||
}
|
||||
|
||||
.tab-content-container {
|
||||
@apply w-4/5 m-auto;
|
||||
@apply px-[40px] m-auto;
|
||||
}
|
||||
|
||||
.md-preview {
|
||||
|
|
|
@ -16,12 +16,12 @@ import { EoExtensionInfo } from '../extension.model';
|
|||
export class ExtensionDetailComponent implements OnInit {
|
||||
@Input() extensionData: ExtensionInfo | null = null;
|
||||
@Output() readonly goBack: EventEmitter<any> = new EventEmitter();
|
||||
@Input() nzSelectedIndex = 0;
|
||||
isOperating = false;
|
||||
introLoading = false;
|
||||
changelogLoading = false;
|
||||
isNotLoaded = true;
|
||||
extensionDetail: EoExtensionInfo;
|
||||
nzSelectedIndex = 0;
|
||||
|
||||
changeLog = '';
|
||||
changeLogNotFound = false;
|
||||
|
@ -44,6 +44,8 @@ export class ExtensionDetailComponent implements OnInit {
|
|||
}
|
||||
|
||||
async getDetail() {
|
||||
const nzSelectedIndex = this.nzSelectedIndex;
|
||||
this.nzSelectedIndex = -1;
|
||||
this.extensionDetail = { ...this.extensionDetail, ...this.extensionData };
|
||||
if (this.electron.isElectron) {
|
||||
this.isOperating = window.electron.getInstallingExtension(this.extensionData?.name, ({ type, status }) => {
|
||||
|
@ -56,17 +58,20 @@ export class ExtensionDetailComponent implements OnInit {
|
|||
this.isOperating = false;
|
||||
});
|
||||
}
|
||||
|
||||
this.extensionDetail = await this.extensionService.getDetail(this.extensionData?.name);
|
||||
if (!this.extensionDetail?.installed || this.webService.isWeb) {
|
||||
await this.fetchReadme(this.language.systemLanguage);
|
||||
}
|
||||
|
||||
this.isNotLoaded = false;
|
||||
this.extensionDetail.introduction ||= $localize`This plugin has no documentation yet.`;
|
||||
|
||||
if (this.extensionDetail?.features?.configuration) {
|
||||
this.nzSelectedIndex = ~~this.route.snapshot.queryParams.tab;
|
||||
}
|
||||
this.fetchChangelog(this.language.systemLanguage);
|
||||
|
||||
setTimeout(() => {
|
||||
this.nzSelectedIndex = nzSelectedIndex;
|
||||
});
|
||||
}
|
||||
|
||||
async fetchChangelog(locale = '') {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({ name: 'downloadCountFormater' })
|
||||
export class DownloadCountFormaterPipe implements PipeTransform {
|
||||
constructor() {}
|
||||
|
||||
transform(count = 0) {
|
||||
if (count > 999) {
|
||||
return `${(count / 1000).toFixed(1)}K`;
|
||||
} else if (count > 9999) {
|
||||
return `${(count / 10000).toFixed(1)}M`;
|
||||
} else {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,17 @@
|
|||
<section class="flex-shrink-0 p-0 left tree-sider">
|
||||
<div class="m-[10px]">
|
||||
<eo-ng-input-group [nzPrefix]="prefixTemplateUser" class="!rounded-full">
|
||||
<input type="text" eo-ng-input class="flex-1 w-full px-3" i18n-placeholder="@@Search" placeholder="Search" [(ngModel)]="keyword" />
|
||||
</eo-ng-input-group>
|
||||
<!-- <eo-ng-input-group class="!rounded-full">
|
||||
<input type="text" eo-ng-input class="flex-1 w-full px-3" i18n-placeholder="@@Search" placeholder="Search" [(ngModel)]="keyword" [nzAutocomplete]="auto" /> -->
|
||||
<eo-ng-auto-complete
|
||||
[(ngModel)]="keyword"
|
||||
(ngModelChange)="onInput($event)"
|
||||
[nzControl]="true"
|
||||
[nzOptions]="searchOptions"
|
||||
[nzPrefix]="prefixTemplateUser"
|
||||
i18n-nzPlaceholder="@@Search"
|
||||
nzPlaceholder="Search"
|
||||
></eo-ng-auto-complete>
|
||||
<!-- </eo-ng-input-group> -->
|
||||
<ng-template #prefixTemplateUser
|
||||
><svg class="iconpark-icon">
|
||||
<use href="#search"></use></svg
|
||||
|
@ -30,9 +39,14 @@
|
|||
<section class="flex-1 min-w-0">
|
||||
<eo-extension-list
|
||||
*ngIf="!hasExtension"
|
||||
[keyword]="keyword"
|
||||
[keyword]="keyword.trim()"
|
||||
[type]="selectGroup"
|
||||
(selectChange)="selectExtension($event)"
|
||||
></eo-extension-list>
|
||||
<eo-extension-detail *ngIf="hasExtension" [extensionData]="getExtension" (goBack)="selectExtension()"></eo-extension-detail>
|
||||
<eo-extension-detail
|
||||
*ngIf="hasExtension"
|
||||
[extensionData]="getExtension"
|
||||
[nzSelectedIndex]="nzSelectedIndex"
|
||||
(goBack)="selectExtension()"
|
||||
></eo-extension-detail>
|
||||
</section>
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
eo-ng-auto-complete input {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.ant-btn-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -35,8 +39,6 @@
|
|||
|
||||
::ng-deep {
|
||||
.eo-extension-modal {
|
||||
width: 80%;
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ElectronService } from 'eo/workbench/browser/src/app/core/services';
|
||||
import { ExtensionInfo } from 'eo/workbench/browser/src/app/shared/models/extension-manager';
|
||||
import { observable, makeObservable, computed, action } from 'mobx';
|
||||
import { NzFormatEmitEvent, NzTreeNodeOptions } from 'ng-zorro-antd/tree';
|
||||
|
||||
import { ExtensionService } from '../../shared/services/extensions/extension.service';
|
||||
import { ExtensionGroupType } from './extension.model';
|
||||
|
||||
import { getExtensionCates, ExtensionGroupType, suggestList } from './extension.model';
|
||||
@Component({
|
||||
selector: 'eo-extension',
|
||||
templateUrl: './extension.component.html',
|
||||
|
@ -15,8 +14,11 @@ import { ExtensionGroupType } from './extension.model';
|
|||
export class ExtensionComponent implements OnInit {
|
||||
@observable currentExtension: ExtensionInfo | null = null;
|
||||
@observable selectGroup: ExtensionGroupType | string = ExtensionGroupType.all;
|
||||
keyword = '';
|
||||
nzSelectedKeys: Array<number | string> = ['all'];
|
||||
@Input() keyword = '';
|
||||
@Input() nzSelectedKeys: Array<number | string> = ['all'];
|
||||
category = '';
|
||||
nzSelectedIndex = 0;
|
||||
searchOptions = [];
|
||||
treeNodes: NzTreeNodeOptions[] = [
|
||||
{
|
||||
key: 'all',
|
||||
|
@ -29,6 +31,7 @@ export class ExtensionComponent implements OnInit {
|
|||
title: $localize`Official`,
|
||||
isLeaf: true
|
||||
},
|
||||
...getExtensionCates(),
|
||||
{
|
||||
key: 'installed',
|
||||
title: $localize`Installed`,
|
||||
|
@ -50,8 +53,20 @@ export class ExtensionComponent implements OnInit {
|
|||
makeObservable(this);
|
||||
}
|
||||
|
||||
onInput(value: string): void {
|
||||
this.searchOptions = value.trim() ? suggestList.filter(n => n.startsWith(value)) : [];
|
||||
const suggest = suggestList.find(n => n.startsWith(value));
|
||||
const node = this.treeNodes.find(n => n.key === suggest);
|
||||
if (suggest && node) {
|
||||
this.nzSelectedKeys = [node.key];
|
||||
}
|
||||
}
|
||||
|
||||
selectExtension(ext = null) {
|
||||
this.setExtension(ext);
|
||||
if (Number.isInteger(ext?.nzSelectedIndex)) {
|
||||
this.nzSelectedIndex = ext?.nzSelectedIndex;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Group tree item click.
|
||||
|
@ -59,8 +74,12 @@ export class ExtensionComponent implements OnInit {
|
|||
* @param event
|
||||
*/
|
||||
clickTreeItem(event: NzFormatEmitEvent): void {
|
||||
const { key } = event.node.origin;
|
||||
if (this.selectGroup !== key) {
|
||||
this.keyword = '';
|
||||
}
|
||||
this.selectExtension('');
|
||||
this.setGroup(event.node.key);
|
||||
this.setGroup(key);
|
||||
}
|
||||
|
||||
@action setGroup(data) {
|
||||
|
|
|
@ -14,3 +14,82 @@ export interface EoExtensionInfo extends ExtensionInfo {
|
|||
};
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export enum ContributionPointsPrefix {
|
||||
category = '@category:',
|
||||
feature = '@feature:'
|
||||
}
|
||||
|
||||
export const featuresTipsMap = {
|
||||
importAPI: {
|
||||
type: 'format',
|
||||
suggest: '@feature:importAPI'
|
||||
},
|
||||
exportAPI: {
|
||||
type: 'format',
|
||||
suggest: '@feature:exportAPI'
|
||||
},
|
||||
syncAPI: {
|
||||
type: 'format',
|
||||
suggest: '@feature:syncAPI'
|
||||
},
|
||||
sidebarView: {
|
||||
type: 'format',
|
||||
suggest: '@feature:sidebarView'
|
||||
},
|
||||
theme: {
|
||||
type: 'theme',
|
||||
suggest: '@feature:theme'
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const categoriesTipsMap = {
|
||||
'Data Migration': {
|
||||
type: 'format',
|
||||
suggest: '@category:Data Migration'
|
||||
},
|
||||
Themes: {
|
||||
type: 'format',
|
||||
suggest: '@category:Themes'
|
||||
},
|
||||
'API Security': {
|
||||
type: 'format',
|
||||
suggest: '@category:API Security'
|
||||
},
|
||||
Other: {
|
||||
type: 'format',
|
||||
suggest: '@category:Other'
|
||||
}
|
||||
} as const;
|
||||
|
||||
export type FeatureContributionPoints = keyof typeof featuresTipsMap;
|
||||
export type CategoryContributionPoints = keyof typeof categoriesTipsMap;
|
||||
export type ContributionPoints = FeatureContributionPoints | CategoryContributionPoints;
|
||||
|
||||
export const contributionPoints = Object.keys(featuresTipsMap).concat(Object.keys(categoriesTipsMap));
|
||||
|
||||
export const getExtensionCates = () =>
|
||||
[
|
||||
{
|
||||
key: categoriesTipsMap['Data Migration'].suggest,
|
||||
title: $localize`Data Migration`,
|
||||
isLeaf: true
|
||||
},
|
||||
{
|
||||
key: categoriesTipsMap.Themes.suggest,
|
||||
title: $localize`Themes`,
|
||||
isLeaf: true
|
||||
},
|
||||
{
|
||||
key: categoriesTipsMap['API Security'].suggest,
|
||||
title: $localize`API Security`,
|
||||
isLeaf: true
|
||||
},
|
||||
{
|
||||
key: categoriesTipsMap.Other.suggest,
|
||||
title: $localize`Other`,
|
||||
isLeaf: true
|
||||
}
|
||||
] as const;
|
||||
|
||||
export const suggestList = Object.values({ ...categoriesTipsMap, ...featuresTipsMap }).map(n => n.suggest);
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { EoNgAutoCompleteModule } from 'eo-ng-auto-complete';
|
||||
import { EoNgSwitchModule } from 'eo-ng-switch';
|
||||
import { EoNgTabsModule } from 'eo-ng-tabs';
|
||||
import { EoNgTreeModule } from 'eo-ng-tree';
|
||||
import { ExtensionDetailComponent } from 'eo/workbench/browser/src/app/pages/extension/detail/extension-detail.component';
|
||||
import { DownloadCountFormaterPipe } from 'eo/workbench/browser/src/app/pages/extension/download-count-formater.pipe';
|
||||
import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
|
||||
import { NzAvatarModule } from 'ng-zorro-antd/avatar';
|
||||
import { NzCardModule } from 'ng-zorro-antd/card';
|
||||
import { NzDescriptionsModule } from 'ng-zorro-antd/descriptions';
|
||||
import { NzInputNumberModule } from 'ng-zorro-antd/input-number';
|
||||
import { NzResultModule } from 'ng-zorro-antd/result';
|
||||
import { NzSpaceModule } from 'ng-zorro-antd/space';
|
||||
import { NzTagModule } from 'ng-zorro-antd/tag';
|
||||
|
||||
// import { ExtensionRoutingModule } from './extension-routing.module';
|
||||
import { ShadowDomEncapsulationModule } from '../../modules/eo-ui/shadow/shadow-dom-encapsulation.module';
|
||||
|
@ -27,8 +31,11 @@ import { ExtensionListComponent } from './list/extension-list.component';
|
|||
EoNgSwitchModule,
|
||||
EoNgTreeModule,
|
||||
NzResultModule,
|
||||
ShadowDomEncapsulationModule
|
||||
ShadowDomEncapsulationModule,
|
||||
NzTagModule,
|
||||
EoNgAutoCompleteModule,
|
||||
NzSpaceModule
|
||||
],
|
||||
declarations: [ExtensionComponent, ExtensionSettingComponent, ExtensionListComponent, ExtensionDetailComponent]
|
||||
declarations: [ExtensionComponent, ExtensionSettingComponent, ExtensionListComponent, ExtensionDetailComponent, DownloadCountFormaterPipe]
|
||||
})
|
||||
export class ExtensionModule {}
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
<div class="px-6 overflow-auto extension-list">
|
||||
<ng-container *ngIf="!renderList?.length">
|
||||
<ng-container *ngIf="!extensionList?.length">
|
||||
<nz-spin class="w-fit mx-auto my-[30px]" nzSimple *ngIf="loading"></nz-spin>
|
||||
<nz-empty class="w-fit mx-auto my-[30px]" *ngIf="!loading"></nz-empty>
|
||||
</ng-container>
|
||||
<div class="grid gap-6 py-5 2xl:grid-cols-4 lg:grid-cols-3 sm:grid-cols-1">
|
||||
<div class="grid gap-6 py-5 2xl:grid-cols-3 lg:grid-cols-3 sm:grid-cols-1">
|
||||
<div
|
||||
class="flex flex-col flex-wrap w-full transition-shadow duration-300 rounded border-all plugin-block hover:shadow-lg"
|
||||
*ngFor="let it of renderList"
|
||||
*ngFor="let it of extensionList"
|
||||
(click)="clickExtension($event, it)"
|
||||
>
|
||||
<div class="flex w-full p-5">
|
||||
<div class="flex w-full">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex items-center justify-between">
|
||||
<nz-avatar [nzSrc]="it.logo" nzShape="square"></nz-avatar>
|
||||
<div class="flex flex-col w-full h-full p-5">
|
||||
<div class="flex flex-col flex-1 w-full">
|
||||
<div class="flex items-center">
|
||||
<nz-avatar class="flex-shrink-0" [nzSize]="35" [nzShape]="'square'" [nzSrc]="it.logo"></nz-avatar>
|
||||
<div class="flex flex-col flex-1 w-0">
|
||||
<div class="flex items-center ml-[20px]">
|
||||
<span class="flex-1 font-bold truncate" [title]="it.title">{{ it.title }}</span>
|
||||
<div *ngIf="type === 'installed'">
|
||||
<nz-tag [nzColor]="'default'">v{{ it.version }}</nz-tag>
|
||||
</div>
|
||||
<div *ngIf="type !== 'installed'">
|
||||
<span
|
||||
*ngIf="extensionService.installedMap?.has(it.name)"
|
||||
|
@ -24,27 +28,37 @@
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between text-lg">
|
||||
<span class="mt-2 mb-1 text-base font-bold">{{ it.title }}</span>
|
||||
<div *ngIf="type === 'installed'">
|
||||
<eo-ng-switch
|
||||
click-stop-propagation
|
||||
[(ngModel)]="it.enable"
|
||||
(ngModelChange)="extensionService.toggleEnableExtension(it.name, $event)"
|
||||
></eo-ng-switch>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-xs text-tips">{{ it.author }}</span>
|
||||
<div class="flex mt-[15px] text-[12px] desc leading-[1.65]">{{ it.description }}</div>
|
||||
</div>
|
||||
<div class="btn-group" *ngIf="type === 'installed'">
|
||||
<ng-container *ngIf="it.features?.configuration">
|
||||
<a eo-ng-button nzType="link" i18n>Setting</a>
|
||||
<nz-divider nzType="vertical"></nz-divider>
|
||||
</ng-container>
|
||||
<a eo-ng-button nzType="link"><span data-id="details" i18n="@@ExtensionDetail">Details</span></a>
|
||||
<nz-space [nzSplit]="spaceSplit" class="subtitle ml-[20px] text-tips text-[12px]">
|
||||
<ng-template #spaceSplit>
|
||||
<nz-divider nzType="vertical"></nz-divider>
|
||||
</ng-template>
|
||||
<span *nzSpaceItem>{{ it.author }}</span>
|
||||
<ng-container *ngIf="type !== 'installed'">
|
||||
<span class="flex items-center" *nzSpaceItem>
|
||||
<eo-iconpark-icon class="mr-[5px]" name="download-two" size="14px"></eo-iconpark-icon>
|
||||
{{ it.downloadCounts | downloadCountFormater }}
|
||||
</span>
|
||||
</ng-container>
|
||||
</nz-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-[15px] text-[12px] desc leading-[1.65] text-tips">{{ it.description }}</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between" *ngIf="type === 'installed'">
|
||||
<div>
|
||||
<ng-container *ngIf="it.features?.configuration">
|
||||
<a eo-ng-button nzType="link" i18n (click)="clickExtension($event, it, 0)">Setting</a>
|
||||
<nz-divider nzType="vertical"></nz-divider>
|
||||
</ng-container>
|
||||
<a eo-ng-button nzType="link" (click)="clickExtension($event, it, it.features?.configuration ? 1 : 0)"
|
||||
><span data-id="details" i18n="@@ExtensionDetail">Details</span></a
|
||||
>
|
||||
</div>
|
||||
<eo-ng-switch
|
||||
click-stop-propagation
|
||||
[(ngModel)]="it.enable"
|
||||
(ngModelChange)="extensionService.toggleEnableExtension(it.name, $event)"
|
||||
></eo-ng-switch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core';
|
||||
import { ElectronService } from 'eo/workbench/browser/src/app/core/services';
|
||||
import { autorun, computed, observable, makeObservable } from 'mobx';
|
||||
import { autorun, observable, makeObservable } from 'mobx';
|
||||
|
||||
import { ExtensionService } from '../../../shared/services/extensions/extension.service';
|
||||
import { ExtensionGroupType } from '../extension.model';
|
||||
|
||||
const extensionSearch = list => keyword => list.filter(it => it.name.includes(keyword) || it.keywords?.includes(keyword));
|
||||
import { ContributionPointsPrefix, ExtensionGroupType, suggestList } from '../extension.model';
|
||||
|
||||
const extensionSearch = list => {
|
||||
return (keyword = '') => {
|
||||
return list.filter(it => {
|
||||
if (keyword) {
|
||||
return it.name.includes(keyword) || it.keywords?.includes(keyword);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
};
|
||||
@Component({
|
||||
selector: 'eo-extension-list',
|
||||
templateUrl: './extension-list.component.html',
|
||||
|
@ -15,64 +23,74 @@ const extensionSearch = list => keyword => list.filter(it => it.name.includes(ke
|
|||
export class ExtensionListComponent implements OnInit {
|
||||
@Input() @observable type: string = ExtensionGroupType.all;
|
||||
@Input() @observable keyword = '';
|
||||
@Input() @observable category = '';
|
||||
@Output() readonly selectChange: EventEmitter<any> = new EventEmitter<any>();
|
||||
allList = [];
|
||||
officialList = [];
|
||||
installedList = [];
|
||||
extensionList = [];
|
||||
loading = false;
|
||||
@computed get renderList() {
|
||||
if (this.type === 'all') {
|
||||
return this.allList;
|
||||
}
|
||||
if (this.type === 'official') {
|
||||
return this.officialList;
|
||||
}
|
||||
return this.installedList;
|
||||
}
|
||||
constructor(public extensionService: ExtensionService, public electron: ElectronService) {}
|
||||
async ngOnInit() {
|
||||
makeObservable(this);
|
||||
autorun(async () => {
|
||||
switch (this.type) {
|
||||
case 'all': {
|
||||
this.allList = [];
|
||||
this.allList = await this.searchPlugin(this.type, this.keyword);
|
||||
break;
|
||||
}
|
||||
case 'official': {
|
||||
this.officialList = [];
|
||||
this.officialList = await this.searchPlugin(this.type, this.keyword);
|
||||
}
|
||||
default: {
|
||||
this.installedList = [];
|
||||
this.installedList = await this.searchPlugin(this.type, this.keyword);
|
||||
break;
|
||||
}
|
||||
if (this.keyword) {
|
||||
const notCompleteSuggest = suggestList.some(n => n.startsWith(this.keyword) && this.keyword !== n);
|
||||
if (notCompleteSuggest) return;
|
||||
}
|
||||
|
||||
let type = this.type;
|
||||
if (type.startsWith(ContributionPointsPrefix.category)) {
|
||||
type = 'category';
|
||||
this.category = this.type.slice(ContributionPointsPrefix.category.length);
|
||||
}
|
||||
this.extensionList = [];
|
||||
this.extensionList = await this.searchPlugin(type, { keyword: this.keyword, category: this.category });
|
||||
});
|
||||
}
|
||||
clickExtension(event, item) {
|
||||
clickExtension(event: MouseEvent, item, nzSelectedIndex?) {
|
||||
event.stopPropagation();
|
||||
item.nzSelectedIndex = nzSelectedIndex;
|
||||
this.selectChange.emit(item);
|
||||
}
|
||||
async searchPlugin(groupType, keyword = '') {
|
||||
async searchPlugin(groupType, { keyword = '', category = '', feature = '' }) {
|
||||
this.loading = true;
|
||||
const suggest = suggestList.find(n => keyword.startsWith(n));
|
||||
|
||||
if (suggest) {
|
||||
const prefix = Object.values(ContributionPointsPrefix).find(n => keyword.startsWith(n));
|
||||
const text = suggest.slice(prefix.length);
|
||||
keyword = keyword.slice(suggest.length).trim();
|
||||
if (prefix === ContributionPointsPrefix.feature) {
|
||||
groupType = 'feature';
|
||||
feature = text;
|
||||
} else if (prefix === ContributionPointsPrefix.category) {
|
||||
groupType = 'category';
|
||||
category = text;
|
||||
}
|
||||
}
|
||||
const func = {
|
||||
installed: () => {
|
||||
const list = this.extensionService.getInstalledList();
|
||||
return extensionSearch(list)(keyword);
|
||||
},
|
||||
official: async () => {
|
||||
const authorName = ['Postcat'];
|
||||
const { data }: any = await this.extensionService.requestList();
|
||||
return extensionSearch(data.filter(it => authorName.includes(it.author)))(keyword);
|
||||
const [{ data }]: any = await this.extensionService.requestList('list', { author: 'Postcat', keyword });
|
||||
return data;
|
||||
},
|
||||
all: async () => {
|
||||
const { data }: any = await this.extensionService.requestList();
|
||||
return extensionSearch(data)(keyword);
|
||||
const [{ data }]: any = await this.extensionService.requestList('list', { keyword });
|
||||
return data;
|
||||
},
|
||||
category: async () => {
|
||||
const [{ data }]: any = await this.extensionService.requestList('list', { category, keyword });
|
||||
return data;
|
||||
},
|
||||
feature: async () => {
|
||||
const [{ data }]: any = await this.extensionService.requestList('list', { feature, keyword });
|
||||
return data;
|
||||
}
|
||||
};
|
||||
try {
|
||||
return await func[groupType]();
|
||||
const result = await func[groupType]();
|
||||
return result;
|
||||
} catch (error) {
|
||||
} finally {
|
||||
this.loading = false;
|
||||
|
|
|
@ -47,6 +47,7 @@ export interface ExtensionInfo {
|
|||
//Entry js file,node environment
|
||||
node: string;
|
||||
title: string;
|
||||
downloadCounts: number;
|
||||
// extension logo
|
||||
logo: string;
|
||||
//Contribution Feature
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ElectronService } from 'eo/workbench/browser/src/app/core/services';
|
||||
import { LanguageService } from 'eo/workbench/browser/src/app/core/services/language/language.service';
|
||||
|
@ -6,7 +6,7 @@ import { DISABLE_EXTENSION_NAMES } from 'eo/workbench/browser/src/app/shared/con
|
|||
import { FeatureInfo, ExtensionInfo, SidebarView } from 'eo/workbench/browser/src/app/shared/models/extension-manager';
|
||||
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
|
||||
import { APP_CONFIG } from 'eo/workbench/browser/src/environments/environment';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { lastValueFrom, Subscription } from 'rxjs';
|
||||
|
||||
import { ExtensionStoreService } from './extension-store.service';
|
||||
import { WebExtensionService } from './webExtension.service';
|
||||
|
@ -21,7 +21,8 @@ export class ExtensionService {
|
|||
extensionIDs: string[] = [];
|
||||
HOST = APP_CONFIG.EXTENSION_URL;
|
||||
installedList: ExtensionInfo[] = [];
|
||||
installedMap: Map<string, ExtensionInfo>;
|
||||
installedMap: Map<string, ExtensionInfo> = new Map();
|
||||
private requestPending: Subscription | null = null;
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private electron: ElectronService,
|
||||
|
@ -92,44 +93,65 @@ export class ExtensionService {
|
|||
isInstalled(name) {
|
||||
return this.installedList.includes(name);
|
||||
}
|
||||
public async requestList(type = 'list') {
|
||||
const result: any = await lastValueFrom(this.http.get(`${this.HOST}/list?locale=${this.language.systemLanguage}`), {
|
||||
defaultValue: []
|
||||
});
|
||||
const debugExtensions = [];
|
||||
public async requestList(type = 'list', queryParams = {}) {
|
||||
this.requestPending?.unsubscribe();
|
||||
return new Promise((resolve, reject) => {
|
||||
const params = JSON.parse(JSON.stringify({ locale: this.language.systemLanguage, ...queryParams }));
|
||||
|
||||
if (type !== 'init') {
|
||||
for (let i = 0; i < this.webExtensionService.debugExtensionNames.length; i++) {
|
||||
const name = this.webExtensionService.debugExtensionNames[i];
|
||||
const hasExist = this.installedList.some(val => val.name === name);
|
||||
if (hasExist) continue;
|
||||
debugExtensions.push(await this.webExtensionService.getDebugExtensionsPkgInfo(name));
|
||||
}
|
||||
}
|
||||
this.requestPending = this.http.get<any>(`${this.HOST}/list`, { params }).subscribe({
|
||||
next: async result => {
|
||||
const debugExtensions = [];
|
||||
const originData = structuredClone(result.data);
|
||||
|
||||
result.data = [
|
||||
...result.data.filter(val => this.installedList.every(childVal => childVal.name !== val.name)),
|
||||
//Local debug package
|
||||
...this.installedList.map(module => {
|
||||
const extension = result.data.find(it => it.name === module.name);
|
||||
if (extension) {
|
||||
module.i18n = extension.i18n;
|
||||
if (type !== 'init') {
|
||||
for (let i = 0; i < this.webExtensionService.debugExtensionNames.length; i++) {
|
||||
const name = this.webExtensionService.debugExtensionNames[i];
|
||||
const hasExist = this.installedList.some(val => val.name === name);
|
||||
if (hasExist) continue;
|
||||
debugExtensions.push(await this.webExtensionService.getDebugExtensionsPkgInfo(name));
|
||||
}
|
||||
}
|
||||
|
||||
result.data = [
|
||||
...result.data.filter(val => this.installedList.every(childVal => childVal.name !== val.name)),
|
||||
//Local debug package
|
||||
...this.installedList
|
||||
.filter(n => {
|
||||
const target = result.data.find(m => n.name === m.name);
|
||||
n.downloadCounts = target?.downloadCounts;
|
||||
return target;
|
||||
})
|
||||
.map(module => {
|
||||
const extension = result.data.find(it => it.name === module.name);
|
||||
if (extension) {
|
||||
module.i18n = extension.i18n;
|
||||
}
|
||||
return module;
|
||||
}),
|
||||
...debugExtensions
|
||||
];
|
||||
//Handle featue data
|
||||
result.data = result.data.map(module => {
|
||||
let result = this.parseExtensionInfo(module);
|
||||
return result;
|
||||
});
|
||||
|
||||
//Get debug extensions
|
||||
this.webExtensionService.debugExtensions = result.data.filter(val => val.isDebug);
|
||||
|
||||
this.store.setExtensionList(result.data);
|
||||
this.requestPending = null;
|
||||
resolve([result, originData]);
|
||||
},
|
||||
error: () => {
|
||||
this.requestPending = null;
|
||||
reject([]);
|
||||
}
|
||||
return module;
|
||||
}),
|
||||
...debugExtensions
|
||||
];
|
||||
//Handle featue data
|
||||
result.data = result.data.map(module => {
|
||||
let result = this.parseExtensionInfo(module);
|
||||
return result;
|
||||
});
|
||||
});
|
||||
|
||||
//Get debug extensions
|
||||
this.webExtensionService.debugExtensions = result.data.filter(val => val.isDebug);
|
||||
|
||||
this.store.setExtensionList(result.data);
|
||||
return result;
|
||||
// const result: any = await lastValueFrom(, {
|
||||
// defaultValue: []
|
||||
// });
|
||||
}
|
||||
async getDetail(name): Promise<any> {
|
||||
let result = {} as ExtensionInfo;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -45,17 +45,22 @@ const socket = (port = _post) => {
|
|||
const link = /^(wss:\/{2})|(ws:\/{2})\S+$/m.test(request.uri.trim())
|
||||
? request.uri.trim()
|
||||
: request.protocol + '://' + request.uri.trim().replace('//', '');
|
||||
ws = new WebSocket(link, {
|
||||
headers: request?.requestParams.headerParams
|
||||
?.filter(it => it.name && it.value)
|
||||
.reduce(
|
||||
(total, { name, value }) => ({
|
||||
...total,
|
||||
[name]: value
|
||||
}),
|
||||
{}
|
||||
)
|
||||
});
|
||||
try {
|
||||
ws = new WebSocket(link, {
|
||||
headers: request?.requestParams.headerParams
|
||||
?.filter(it => it.name && it.value)
|
||||
.reduce(
|
||||
(total, { name, value }) => ({
|
||||
...total,
|
||||
[name]: value
|
||||
}),
|
||||
{}
|
||||
)
|
||||
});
|
||||
} catch (error) {
|
||||
socket.emit('ws-client', { type: 'ws-connect-back', status: -1, content: error });
|
||||
}
|
||||
|
||||
ws.on('error', err => {
|
||||
socket.emit('ws-client', { type: 'ws-connect-back', status: -1, content: err });
|
||||
unlisten();
|
||||
|
|
Loading…
Reference in New Issue