Skip to content
Open
191 changes: 191 additions & 0 deletions entry/src/main/ets/components/IconPackManager.ets
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { common } from '@kit.AbilityKit';
import { AegisIconPack, IconPackRegistry } from '../utils/IconPackUtils';
import { DefaultToastDuration } from '../pages/Base';
import { parseInstalledIconPacks, refreshInstalledIconPacks } from '../entryability/EntryAbility';

@Component
export struct IconPackManager {
@StorageLink('selectedIconPackKey') selectedIconPackKey: string = IconPackRegistry.getDefaultPackKey();
@StorageLink('installedIconPacksJson') installedIconPacksJson: string = '[]';
@State expanded: boolean = false;

aboutToAppear(): void {
this.reloadPacks();
}

private getInstalledPacks(): AegisIconPack[] {
return parseInstalledIconPacks(this.installedIconPacksJson);
}

private async reloadPacks(): Promise<void> {
const context = getContext(this) as common.UIAbilityContext;
const packs = await refreshInstalledIconPacks(context);
if (this.selectedIconPackKey !== IconPackRegistry.getDefaultPackKey()
&& !packs.some((pack) => pack.getKey() === this.selectedIconPackKey && !pack.invalid)) {
this.selectedIconPackKey = IconPackRegistry.getDefaultPackKey();
}
}

private showToast(message: ResourceStr | string): void {
this.getUIContext().getPromptAction().showToast({
duration: DefaultToastDuration,
message
});
}

private getInstalledCount(): number {
return this.getInstalledPacks().filter((pack) => !pack.invalid).length;
}

private getPackName(pack: AegisIconPack): ResourceStr | string {
if (pack.invalid) {
return $r('app.string.icon_pack_invalid_name_format', pack.name);
}
return pack.name;
}

private getPackSubtitle(pack: AegisIconPack): ResourceStr | string {
if (pack.invalid) {
return $r('app.string.icon_pack_invalid_desc');
}
return $r('app.string.icon_pack_meta_format', pack.icons.length, `${pack.version}`);
}

private async onImportPressed(): Promise<void> {
const context = getContext(this) as common.UIAbilityContext;
try {
const paths = await IconPackRegistry.pickZipFiles(context);
if (paths.length === 0) {
return;
}
for (const path of paths) {
await IconPackRegistry.importFromZip(context, path);
}
await this.reloadPacks();
this.showToast($r('app.string.icon_pack_import_success'));
} catch (error) {
this.showToast($r('app.string.icon_pack_import_failed_format',
IconPackRegistry.formatImportError(error as Object)));
}
}

private async onRemovePressed(pack: AegisIconPack): Promise<void> {
try {
await IconPackRegistry.removePack(pack);
if (this.selectedIconPackKey === pack.getKey()) {
this.selectedIconPackKey = IconPackRegistry.getDefaultPackKey();
}
await this.reloadPacks();
this.showToast($r('app.string.icon_pack_remove_success'));
} catch (error) {
this.showToast($r('app.string.icon_pack_remove_failed_format',
IconPackRegistry.formatImportError(error as Object)));
}
}

@Builder
private packRow(value: string, name: ResourceStr | string, subtitle: ResourceStr | string, selected: boolean,
enabled: boolean, onSelect: () => void, onRemove?: () => void) {
Row() {
Radio({ value, group: 'icon-pack-group' })
.checked(selected)
.enabled(enabled)
.onChange((checked) => {
if (checked && enabled) {
onSelect();
}
})

Column() {
Text(name)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Start)
Text(subtitle)
.fontSize(12)
.textAlign(TextAlign.Start)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)

if (onRemove) {
Button($r('app.string.icon_pack_remove'))
.type(ButtonType.Capsule)
.buttonStyle(ButtonStyleMode.TEXTUAL)
.onClick(() => onRemove())
}
}
.width('100%')
.padding(12)
.borderRadius(12)
.backgroundColor($r("app.color.icon_pack_row_background"))
}

build() {
Column() {
Row() {
Text($r('app.string.icon_pack_title'))
.width('85%')
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Start)
Button(this.expanded ? $r('app.string.icon_pack_hide') : $r('app.string.icon_pack_manage'))
.type(ButtonType.Capsule)
.buttonStyle(ButtonStyleMode.TEXTUAL)
.onClick(() => {
this.expanded = !this.expanded;
})
}
.width('100%')
.alignItems(VerticalAlign.Center)

Row() {
Text($r('app.string.icon_pack_desc'))
.padding({ left: 10 })
.fontSize(12)
}
.width('100%')

if (this.expanded) {
Column({ space: 8 }) {
this.packRow(
IconPackRegistry.getDefaultPackKey(),
$r('app.string.icon_pack_builtin'),
$r('app.string.icon_pack_builtin_desc'),
this.selectedIconPackKey === IconPackRegistry.getDefaultPackKey(),
true,
() => {
this.selectedIconPackKey = IconPackRegistry.getDefaultPackKey();
}
)

ForEach(this.getInstalledPacks(), (pack: AegisIconPack) => {
this.packRow(
pack.getKey(),
this.getPackName(pack),
this.getPackSubtitle(pack),
this.selectedIconPackKey === pack.getKey(),
!pack.invalid,
() => {
this.selectedIconPackKey = pack.getKey();
},
() => this.onRemovePressed(pack)
)
}, (pack: AegisIconPack) => pack.getKey())

Row({ space: 12 }) {
Button($r('app.string.icon_pack_import'))
.type(ButtonType.Capsule)
.onClick(() => this.onImportPressed())
Text($r('app.string.icon_pack_installed_count_format', this.getInstalledCount()))
.fontSize(12)
.fontColor('#666666')
}
.margin({ top: 4 })
}
.width('100%')
.margin({ top: 12, left: 30, right: 20 })
}
}
.width('100%')
.padding({ top: 8, bottom: 8 })
}
}
86 changes: 86 additions & 0 deletions entry/src/main/ets/components/TokenIcon.ets
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { fileIo as fs } from '@kit.CoreFileKit';
import { AegisIconPack, IconPackRegistry } from '../utils/IconPackUtils';
import { parseInstalledIconPacks } from '../entryability/EntryAbility';

@Component
export struct TokenIcon {
@Prop issuer: string = '';
@Prop iconPath: string = '';
@StorageLink('selectedIconPackKey') selectedIconPackKey: string = IconPackRegistry.getDefaultPackKey();
@StorageLink('installedIconPacksJson') installedIconPacksJson: string = '[]';

private getInstalledPacks(): AegisIconPack[] {
return parseInstalledIconPacks(this.installedIconPacksJson);
}

private resolveIconPath(): string {
if (this.iconPath && this.isValidPath(this.iconPath)) {
return this.iconPath;
}
return IconPackRegistry.getIconPathByIssuer(this.issuer, this.selectedIconPackKey, this.getInstalledPacks());
}

private isValidPath(path: string): boolean {
if (path.startsWith('rawfile://')) {
return true;
}
if (path.startsWith('file://')) {
return fs.accessSync(path.slice(7));
}
return fs.accessSync(path);
}

private getImageSource(): ResourceStr | string | undefined {
const path = this.resolveIconPath();
if (!path) {
return undefined;
}
if (path.startsWith('rawfile://')) {
return $rawfile(path.slice(10));
}
return path;
}

private hasImageSource(): boolean {
return this.getImageSource() !== undefined;
}

private stringToColor(input: string): string {
let hash = 0;
for (let index = 0; index < input.length; index++) {
hash = input.charCodeAt(index) + ((hash << 5) - hash);
}

let color = '#';
for (let index = 0; index < 3; index++) {
const value = (hash >> (index * 8)) & 0xff;
const hex = `00${value.toString(16)}`;
color += hex.substring(hex.length - 2);
}
return color;
}

build() {
Stack() {
if (this.hasImageSource()) {
Image(this.getImageSource())
.width(40)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

svg 的大小不一,这里统一宽高,图标看起来也会大小不一

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

大佬,关于这个我想请教一下。就是我考虑把这些内置图标换掉,用aegis-icon的图标,大小应该会比较统一,但是感觉可能会有一些版权方面的问题,而且现在添加的也未必没有这方面的问题。要不还是就保留你之前的那些图标,我的那些都删掉吧,保留图标包的功能,把这方面的版权问题规避掉,都由用户来添加。

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一般没有版权问题

.height(40)
.objectFit(ImageFit.Contain)
.draggable(false)
} else {
Text(this.issuer.length > 0 ? this.issuer.charAt(0).toUpperCase() : '?')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
}
}
.width(50)
.height(50)
.padding(5)
.margin(10)
.backgroundColor(this.hasImageSource() ? $r('app.color.vendor_background_color') :
this.stringToColor(this.issuer || 'k'))
.borderRadius(20)
}
}
Loading