| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- import { WebdavManager } from '../common/util/WebdavManager';
- import { WebDavAccount } from '../viewmodel/WebDavAccount';
- import { Song } from '../viewmodel/Song';
- import { WebdavManagerStates } from '../common/enums/WebdavManagerStates';
- import Logger from '../common/util/Logger';
- import { promptAction, router, window } from '@kit.ArkUI';
- import { CommonConstants } from '../common/constants/CommonConstants';
- import { VideoItem } from '../viewmodel/VideoItem';
- import { GlobalContext } from '../common/util/GlobalContext';
- import { display } from '@kit.ArkUI';
- import { FileInfo } from '../viewmodel/FileInfo';
- import { emitter } from '@kit.BasicServicesKit';
- import { EventConstants } from '../common/constants/EventConstants';
- import { LazyDataSource } from '../common/util/LazyDataSource';
- /**
- * 歌单播放事件数据
- */
- interface PlaylistEventData {
- playlistId: string;
- playlistName: string;
- songCount: number;
- startIndex: number;
- songFilePaths: string[];
- // 移除webDavAuthInfo,现在直接使用videoItems中的webdav_account_id
- }
- const TAG = 'heanup WebDavMainPage';
- // URL解码函数
- function decodeUrlEncodedString(encodedStr: string): string {
- try {
- return decodeURIComponent(encodedStr);
- } catch (error) {
- // 如果解码失败,返回原始字符串
- return encodedStr;
- }
- }
- @Preview
- @Entry
- @Component
- export struct WebDavMainPage {
- @State webdavManager: WebdavManager = WebdavManager.getInstance();
- @State accounts: WebDavAccount[] = [];
- @Prop @Watch('onSwitchAccount') selectedAccount: WebDavAccount;
- @State songs: VideoItem[] = [];
- @State dataSource:LazyDataSource<VideoItem> = new LazyDataSource(this.songs)
- @Link mType: number;
- @Link offsetX: number;
- @Link isShowDrawer: boolean;
- @State isLoading: boolean = false;
- @State webDavFiles: FileInfo[] = []; // 直接在页面中保存文件列表副本
- @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
- @StorageProp('isDarkMode') isDarkMode: boolean = false;
- @State topRectHeight: number = 0; // 顶部安全区高度
- @State breadcrumbs:string[] = []//面包屑导航
- @State visibleFoldersState: FileInfo[] = []; // 改为普通状态变量
- async onSwitchAccount(){
- console.log('onecold 切换账户:', this.selectedAccount.name);
- this.songs = [];
- this.visibleFoldersState = [];
- this.updateListData(this.songs)
-
- // 注意:不再需要清空全局上下文,因为LocalMusic已改用实例变量缓存
- // 当新账户加载时,新的认证信息会自动覆盖旧的
- Logger.info(TAG, 'heanup 切换账户,准备加载新账户文件');
-
- this.isLoading = true;
- await this.webdavManager.loadFilesInfoFromAccount(this.selectedAccount)
- .catch((error: Error) => {
- Logger.error(TAG, '加载文件失败: ' + error.message);
- this.isLoading = false;
- });
- this.breadcrumbs = this.webdavManager.getBreadcrumbs();
- }
- updateListData(mList:Array<VideoItem>){
- this.dataSource.pushArrayData(mList)
- }
- // 更新可见文件夹列表
- private updateVisibleFolders(): void {
- try {
- // 安全检查webDavFiles
- if (!this.webDavFiles || !Array.isArray(this.webDavFiles)) {
- this.visibleFoldersState = [];
- return;
- }
- const allFolders = this.webDavFiles.filter(f => f.isDirectory);
- const visible: FileInfo[] = [];
- for (let i = 0; i < allFolders.length; i++) {
- const folder = allFolders[i];
- // 安全检查folder对象
- if (!folder || typeof folder.fileName !== 'string') {
- continue;
- }
- let shouldShow = this.isDirectChildOfCurrentPath(folder);
- if (shouldShow) {
- visible.push(folder);
- }
- }
- console.log('更新文件夹列表:', visible);
- this.visibleFoldersState = visible;
- } catch (error) {
- Logger.error(TAG, '更新文件夹列表失败:', error.toString());
- this.visibleFoldersState = [];
- }
- }
- // 对话框控制器
- private accountDialogController: CustomDialogController | null = null;
- // 保存事件处理器引用,用于取消订阅
- private eventHandler: (event: string) => void = (event: string) => {
- this.handleWebdavEvent(event);
- };
- aboutToAppear(): void {
- // 获取顶部安全区高度
- this.getTopRectHeight();
- // 加载账户列表
- this.loadAccounts();
- this.loadFiles()
- this.breadcrumbs = this.webdavManager.getBreadcrumbs();
- // 订阅WebDAV状态变化
- this.webdavManager.subscribe(this.eventHandler);
- }
- // 获取顶部安全区高度
- private getTopRectHeight(): void {
- window.getLastWindow(getContext(this), (err, data) => {
- if (err.code) {
- Logger.error(TAG, '获取窗口失败: ' + JSON.stringify(err));
- return;
- }
- const area = data.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
- this.topRectHeight = px2vp(area.topRect.height);
- Logger.info(TAG, '顶部安全区高度: ' + this.topRectHeight);
- });
- }
- aboutToDisappear(): void {
- // 取消订阅
- this.webdavManager.unsubscribe(this.eventHandler);
- }
- // 处理WebDAV事件
- private handleWebdavEvent(event: string): void {
- switch (event) {
- case WebdavManagerStates.LoadFilesInfoSucceed:
- this.songs = this.webdavManager.webDavSongs;
- this.updateListData(this.songs)
- // 直接引用webdavManager的数组,避免@Observed序列化问题
- this.webDavFiles = this.webdavManager.webDavFiles;
- this.isLoading = false;
- // 更新可见文件夹列表
- this.updateVisibleFolders();
- promptAction.showToast({
- message: '加载成功: ' + this.webDavFiles.filter(f => f.isDirectory).length + '个文件夹, ' + this.songs.length + '首歌曲'
- });
- break;
- case WebdavManagerStates.LoadFilesInfoFailed:
- this.isLoading = false;
- this.getUIContext().getPromptAction().showToast({ message: '加载失败' });
- break;
- case WebdavManagerStates.InsertAccountSucceed:
- case WebdavManagerStates.EditAccountSucceed:
- case WebdavManagerStates.RemoveAccountSucceed:
- this.loadAccounts();
- break;
- }
- }
- // 加载账户列表
- private loadAccounts(): void {
- this.accounts = this.webdavManager.getAllWebDavAccounts();
- }
- // 加载文件列表
- private loadFiles(): void {
- if (!this.selectedAccount) {
- this.getUIContext().getPromptAction().showToast({ message: '请先选择账户' });
- return;
- }
- this.isLoading = true;
- this.webdavManager.loadFilesInfoFromWebdav()
- .catch((error: Error) => {
- Logger.error(TAG, '加载文件失败: ' + error.message);
- this.isLoading = false;
- });
- }
- // 进入文件夹
- private enterFolder(folder: FileInfo): void {
- this.isLoading = true;
- this.webdavManager.enterFolder(folder)
- .catch((error: Error) => {
- Logger.error(TAG, '进入文件夹失败: ' + error.message);
- this.isLoading = false;
- });
- }
- // 检查是否为当前目录的直接子项
- private isDirectChildOfCurrentPath(folder: FileInfo): boolean {
- const currentPath = this.webdavManager.currentPath || '';
- // 如果在根目录(currentPath为空或'/'),显示所有第一级文件夹
- if (currentPath === '' || currentPath === '/') {
- const folderPath = folder.href.replace(/\/$/, ''); // 去掉尾部斜杠
- return folder.href.startsWith('/') &&
- folder.href !== '/' &&
- !folderPath.substring(1).includes('/');
- }
- // 非根目录情况,计算相对路径
- let relativePath = folder.href;
- if (currentPath !== '/') {
- relativePath = folder.href.replace(currentPath, '');
- }
- relativePath = relativePath.replace(/^\//, '').replace(/\/$/, '');
- // 只有相对路径不为空且不包含/时才认为是直接子项
- return relativePath !== '' && !relativePath.includes('/');
- }
- // 返回上级目录
- private goBack(): void {
- if(this.webdavManager.canGoBack()){
- this.isLoading = true;
- this.webdavManager.goBack()
- .catch((error: Error) => {
- Logger.error(TAG, '返回失败: ' + error.message);
- this.isLoading = false;
- });
- this.breadcrumbs = this.webdavManager.getBreadcrumbs();
- }
- }
- // 切换账户
- private switchAccount(account: WebDavAccount): void {
- this.selectedAccount = account;
- this.songs = [];
- this.updateListData(this.songs)
- }
- // 播放WebDAV歌曲
- private playSong(song: VideoItem, index: number): void {
- try {
- Logger.info(TAG, `heanup === WebDAV playSong 方法被调用 ===`);
- Logger.info(TAG, 'heanup 播放指定歌曲: ' + song.name + ', 索引: ' + index);
- Logger.info(TAG, 'heanup 歌曲列表长度: ' + this.songs.length);
- // 检查歌曲是否有webdav_account_id
- Logger.info(TAG, `heanup 播放歌曲的webdav_account_id: ${song.webdav_account_id || '未设置'}`);
- // 确保所有歌曲都设置了正确的webdav_account_id
- if (this.selectedAccount && this.selectedAccount.id) {
- const accountIds = this.songs.map(s => s.webdav_account_id).filter(id => id);
- Logger.info(TAG, `heanup 当前歌曲列表中有 ${accountIds.length}/${this.songs.length} 首歌曲设置了webdav_account_id`);
- // 如果发现歌曲缺少webdav_account_id,立即设置
- this.songs.forEach((item, idx) => {
- if (!item.webdav_account_id) {
- item.webdav_account_id = this.selectedAccount!.id.toString();
- Logger.info(TAG, `heanup 为歌曲 "${item.name}" 设置webdav_account_id: ${item.webdav_account_id}`);
- }
- });
- }
- // 直接使用当前的VideoItem数组
- const videoItems: VideoItem[] = this.songs;
- const songFilePaths: string[] = [];
- for (let i = 0; i < this.songs.length; i++) {
- const item = this.songs[i];
- songFilePaths.push(item.filePath); // 使用filePath作为文件路径
- }
- // 保存WebDAV歌曲数据到全局上下文
- const globalContext = GlobalContext.getContext();
- globalContext.setObject('videoItems', videoItems);
- globalContext.setObject('currentPlayIndex', index);
- Logger.info(TAG, 'heanup 已将WebDAV歌曲列表保存到全局上下文,长度:' + videoItems.length);
- // 发送播放事件,类似歌单播放的方式
- const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY };
- const playlistData: PlaylistEventData = {
- playlistId: 'webdav-playlist', // 使用特殊的ID标识WebDAV播放列表
- playlistName: 'WebDAV - ' + (this.selectedAccount?.name || '未知账户'),
- songCount: this.songs.length,
- startIndex: index,
- songFilePaths: songFilePaths
- // 移除webDavAuthInfo,现在直接使用videoItems中的webdav_account_id
- };
- Logger.info(TAG, 'heanup 准备发送WebDAV播放事件,eventId: ' + eventPlaylistPlay.eventId);
- Logger.info(TAG, 'heanup 发送WebDAV播放事件数据: ' + JSON.stringify(playlistData));
- const eventData: emitter.EventData = {
- data: playlistData
- };
- emitter.emit(eventPlaylistPlay, eventData);
- // 跳转到首页播放器
- this.getUIContext()?.animateTo({ duration: 555 }, () => {
- this.mType =0
- })
- } catch (error) {
- const err = error as Error;
- Logger.error(TAG, '播放歌曲失败: ' + err.message);
- this.getUIContext().getPromptAction().showToast({ message: '播放失败' });
- }
- }
- // 导航到指定层级的面包屑路径
- private navigateToBreadcrumb(breadcrumbIndex: number): void {
- try {
- this.isLoading = true;
- this.webdavManager.navigateToBreadcrumb(breadcrumbIndex).then((path: string) => {
- this.webdavManager.enterFolderFromPath(path)
- this.breadcrumbs = this.webdavManager.getBreadcrumbs();
- })
- .catch((error: Error) => {
- Logger.error(TAG, '导航到面包屑路径失败: ' + error.message);
- this.isLoading = false;
- });
- } catch (error) {
- Logger.error(TAG, '导航到面包屑路径异常: ' + (error as Error).message);
- this.isLoading = false;
- }
- }
- build() {
- Column() {
- // 顶部安全区和标题栏
- Column() {
- Blank()
- .height(this.topRectHeight + 5)
- .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
- .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
- // 标题栏
- Row() {
- Image($r('app.media.menu'))
- .width(24)
- .height(24)
- .margin({ left: 12, right: 8 })
- .onClick(() => {
- this.getUIContext()?.animateTo({ duration: 555 }, () => {
- this.isShowDrawer = !this.isShowDrawer
- this.offsetX = 0
- })
- });
- Text(this.selectedAccount.name || 'WebDav')
- .fontSize(18)
- .fontColor(Color.White)
- .fontWeight(FontWeight.Medium)
- .textAlign(TextAlign.Center)
- }
- .height(48)
- .width('100%')
- .alignItems(VerticalAlign.Center)
- .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
- }
- // 内容区域
- if (this.accounts.length === 0) {
- this.buildEmptyView();
- } else {
- this.buildContentView();
- }
- }
- .width('100%')
- .height('100%')
- .backgroundColor($r('app.color.start_window_background'))
- }
- // 空状态视图
- @Builder
- buildEmptyView() {
- Column({ space: 20 }) {
- Image($r('app.media.cloudDisk'))
- .width(120)
- .height(120)
- .opacity(0.3)
- Text('暂无WebDAV账户')
- .fontSize(16)
- .fontColor($r('app.color.index_tab_font_color'))
- .opacity(0.6)
- }
- .justifyContent(FlexAlign.Center)
- .width('100%')
- .layoutWeight(1)
- }
- // 内容视图
- @Builder
- buildContentView() {
- Column() {
- // 加载按钮和面包屑导航
- Column({ space: 8 }) {
- // 账户信息显示
- // if (this.selectedAccount) {
- // Row({ space: 12 }) {
- // // 账户封面
- // Stack() {
- // if (this.selectedAccount.coverPath) {
- // Image(this.selectedAccount.coverPath)
- // .width(20)
- // .height(20)
- // .borderRadius(10)
- // .objectFit(ImageFit.Cover)
- // .border({ width: 2, color: this.themeColor })
- // } else {
- //
- // Image($r('app.media.cloudDisk'))
- // .width(20)
- // .height(20)
- // .fillColor(this.themeColor)
- // }
- // }
- //
- // // 账户信息
- // Column({ space: 4 }) {
- // Text(this.selectedAccount.name || '未知账户')
- // .fontSize(16)
- // .fontWeight(FontWeight.Medium)
- // .fontColor(this.isDarkMode ? '#FFFFFF' : $r('app.color.text_color'))
- // .maxLines(1)
- // .textOverflow({ overflow: TextOverflow.Ellipsis })
- //
- // Text(`${this.selectedAccount.host}:${this.selectedAccount.port}`)
- // .fontSize(12)
- // .fontColor(this.isDarkMode ? '#8E8E93' : $r('app.color.index_tab_font_color'))
- // .opacity(0.7)
- // .maxLines(1)
- // .textOverflow({ overflow: TextOverflow.Ellipsis })
- // }
- // .alignItems(HorizontalAlign.Start)
- // .layoutWeight(1)
- //
- // }
- // .width('100%')
- // .padding({ left: 4, right: 4, top: 8, bottom: 8 })
- // .backgroundColor(this.isDarkMode ? 'rgba(44,44,46,0.8)' : 'rgba(248,248,248,0.8)')
- // .borderRadius(8)
- // .margin({ bottom: 8 })
- // }
- //
- // 面包屑导航
- if (this.webdavManager.currentPath !== '') {
- Row({ space: 8 }) {
- Button({ type: ButtonType.Circle }) {
- Image(this.webdavManager.canGoBack()?$r('app.media.back'):$r('app.media.cloudDisk'))
- .width(15)
- .height(15)
- .fillColor(Color.White)
- }
- .width(20)
- .height(20)
- .backgroundColor(this.themeColor)
- .onClick(() => this.goBack())
- Row({ space: 4 }) {
- ForEach(this.breadcrumbs, (crumb: string, index: number) => {
- Row() {
- Text(crumb)
- .fontSize(15)
- .fontColor(this.themeColor)
- .maxLines(1)
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- }
- .onClick(() => {
- this.navigateToBreadcrumb(index);
- })
- // 添加分隔符(除了最后一个元素)
- if (index < this.breadcrumbs.length - 1) {
- Text('/')
- .fontSize(15)
- .fontColor($r('app.color.index_tab_font_color'))
- .opacity(0.6)
- }
- })
- }
- .layoutWeight(1)
- }
- .width('100%')
- .padding({ left: 4, right: 4 })
- }
- // 统计信息
- if (this.webDavFiles.length > 0) {
- Row() {
- Text(this.visibleFoldersState.length + ' 个文件夹, ' + this.songs.length + ' 首歌曲')
- .fontSize(13)
- .fontColor($r('app.color.index_tab_font_color'))
- .opacity(0.6)
- .layoutWeight(1)
- .textAlign(TextAlign.Start)
- Blank()
- }
- .padding({ left: 4, right: 4 })
- }
- }
- .width('100%')
- .padding(12)
- .margin({ top: 8 })
- // 加载状态
- Row() {
- LoadingProgress()
- .width(30)
- .height(30)
- .color(this.themeColor)
- Text('加载中...')
- .fontSize(14)
- .fontColor($r('app.color.index_tab_font_color'))
- .margin({ left: 12 })
- }
- .padding(20)
- .visibility(this.isLoading?Visibility.Visible:Visibility.None)
- .opacity(this.isLoading ? 1 : 0)
- .animation({
- duration: 500,
- curve: 'ease-in-out' // 可选动画曲线
- })
- // 文件列表(文件夹 + 歌曲)
- if (this.webDavFiles.length > 0) {
- List({ space: 0 }) {
- // 显示文件夹 - 只显示当前目录下的直接子文件夹
- ForEach(this.visibleFoldersState, (folder: FileInfo) => {
- ListItem() {
- this.buildFolderItem(folder)
- }
- .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 600 }),
- TransitionEffect.scale({ x: 0, y: 0 })))
- .clickEffect({ level: ClickEffectLevel.MIDDLE })
- })
- // 显示歌曲
- LazyForEach(this.dataSource, (song: VideoItem, index: number) => {
- ListItem() {
- this.buildSongItem(song, index)
- }
- .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 600 }),
- TransitionEffect.scale({ x: 0, y: 0 })))
- .clickEffect({ level: ClickEffectLevel.MIDDLE })
- })
- }
- .layoutWeight(1)
- .divider({ strokeWidth: 1, color: this.isDarkMode ? '#333333' :'#EEEEEE' })
- .margin({ top: 4 })
- } else if (!this.isLoading) {
- Column() {
- Text('暂无内容')
- .fontSize(14)
- .fontColor($r('app.color.index_tab_font_color'))
- .opacity(0.6)
- Text('点击"加载文件列表"按钮加载')
- .fontSize(12)
- .fontColor($r('app.color.index_tab_font_color'))
- .opacity(0.4)
- .margin({ top: 8 })
- }
- .justifyContent(FlexAlign.Center)
- .layoutWeight(1)
- }
- }
- .layoutWeight(1)
- }
- // 文件夹列表项
- @Builder
- buildFolderItem(folder: FileInfo) {
- Button({ type: ButtonType.Normal, stateEffect: false }) {
- Row({ space: 2 }) {
- SymbolGlyph($r('sys.symbol.folder'))
- .fontSize(48)
- .fontColor([this.themeColor])
- .alignSelf(ItemAlign.Center)
- .margin({ left: 8, right: 6 })
- // 文件夹信息
- Column({ space: 4 }) {
- Text(decodeUrlEncodedString(folder.fileName.replace('/', '')))
- .fontSize(15)
- .fontColor($r('app.color.index_tab_font_color'))
- .maxLines(1)
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- Text('文件夹')
- .fontSize(13)
- .fontColor($r('app.color.index_tab_font_color'))
- .opacity(0.6)
- }
- .alignItems(HorizontalAlign.Start)
- .layoutWeight(1)
- }
- }
- .reuseId('dir_item')
- .width('100%')
- .padding(12)
- .backgroundColor(Color.Transparent)
- // .backgroundColor($r('app.color.start_window_background'))
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
- .onClick(() => {
- this.enterFolder(folder);
- this.breadcrumbs = this.webdavManager.getBreadcrumbs();
- })
- }
- // 歌曲列表项
- @Builder
- buildSongItem(song: VideoItem, index: number) {
- Button({ type: ButtonType.Normal, stateEffect: false }) {
- Row({ space: 12 }) {
- // 序号
- // 歌曲封面
- Image(song.pixelMap)
- .width(48)
- .height(48)
- .borderRadius(4)
- .alt($r('app.media.music_red'))
- .fillColor(this.themeColor)
- .objectFit(ImageFit.Cover)
- .margin({ left: 8 })
- // 歌曲信息
- Column({ space: 4 }) {
- Text(decodeUrlEncodedString(song.name))
- .fontSize(15)
- .fontColor($r('app.color.index_tab_font_color'))
- .maxLines(1)
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- Row(){
- Text(song.artist)
- .fontSize(13)
- .fontColor($r('app.color.index_tab_font_color'))
- .opacity(0.6)
- .maxLines(1)
- .visibility(song.artist?Visibility.Visible:Visibility.None)
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- Text(decodeUrlEncodedString(song.size||"")+' '+song.cTime)
- .fontSize(13)
- .fontColor($r('app.color.index_tab_font_color'))
- .opacity(0.6)
- .maxLines(1)
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- }
- }
- .alignItems(HorizontalAlign.Start)
- .layoutWeight(1)
- // 播放图标
- Image($r('app.media.ic_play'))
- .width(20)
- .height(20)
- .fillColor($r('app.color.index_tab_font_color'))
- .opacity(0.4)
- }
- }
- .width('100%')
- .padding(12)
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
- .backgroundColor(Color.Transparent)
- .onClick(() => {
- this.playSong(song, index);
- })
- }
- }
|