c77fb700创建于 2025年1月16日历史提交
/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { util } from '@kit.ArkTS';
import { curves } from '@kit.ArkUI';
import { avSession } from '@kit.AVSessionKit';
import { AudioPlayerService } from '@ohos/audioplayer';
import { BreakpointType, BreakpointTypeEnum, CommonConstants, ImageUtil, Logger } from '@ohos/utils';
import ZonesItem from '../model/ZonesItem';
import { ChallengeConstants as Const } from '../constants/ChallengeConstants';
import { ZoneDetailView } from '../views/ZoneDetailView';
import { SpeakPlayerButton } from './SpeakPlayerButton';

const resourceManager = getContext().resourceManager;

export enum ModalActionType {
  ROUTE = 0,
  NAVIGATION
}

const TAG = '[ZoneDetailModal]';

@Component
export struct ZoneDetailModal {
  clickAction: Function = (actionType: ModalActionType) => {
  };
  currentHeight: number = 0;
  currentPositionX: number = 0;
  minPositionX: number = 24;
  maxPositionX: number = 0;
  @Link showDetail: boolean;
  @Prop distance: string = '';
  @State detailSheetMinHeight: number = 0;
  @State detailSheetMaxHeight: number = 0;
  @Consume('positionX') positionX: number;
  @Link detailSheetHeight: number;
  @StorageProp('currentBreakpoint') @Watch('handleShowChange') currentBreakpoint: string = BreakpointTypeEnum.MD;
  @StorageProp('deviceWidth') @Watch('handleShowChange') deviceWidth: number =
    AppStorage.get('deviceWidth') as number;
  @State detailSheetWidth: Length =
    this.currentBreakpoint === BreakpointTypeEnum.SM ? CommonConstants.FULL_PERCENT : Const.MODAL_MAX_WIDTH;
  @Consume('introductionData') selectedZone: ZonesItem;
  interpolatingCurve: ICurve = curves.interpolatingSpring(0, 1, 328, 36);

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

  // Recalculate the size of the pop-up window when the modal pop-up window is changed.
  handleShowChange() {
    this.detailSheetWidth =
      this.currentBreakpoint === BreakpointTypeEnum.SM ? this.deviceWidth :
      Const.MODAL_MAX_WIDTH;
    this.detailSheetMinHeight = this.detailSheetWidth * Const.IMG_ASPECT_RATIO + Const.MODAL_BOTTOM_HEIGHT;
    this.detailSheetHeight = this.detailSheetMinHeight;
    let tempHeight: number =
      (AppStorage.get('deviceHeight') as number) - (AppStorage.get('statusBarHeight') as number) - 8 -
        (AppStorage.get('naviIndicatorHeight') as number);
    if (this.currentBreakpoint !== BreakpointTypeEnum.LG) {
      tempHeight -= Const.TABBAR_HEIGHT;
    }
    if (this.currentBreakpoint === BreakpointTypeEnum.MD) {
      tempHeight -= 20;
    }
    this.detailSheetMaxHeight = tempHeight;
    this.maxPositionX = this.deviceWidth - Const.MODAL_MAX_WIDTH - this.minPositionX;
    if (this.currentBreakpoint === BreakpointTypeEnum.LG) {
      this.maxPositionX -= Const.TABBAR_WIDTH;
    }
    Logger.info(TAG, `maxHeight:${this.detailSheetMaxHeight}   minHeight: ${this.detailSheetMinHeight}`);
  }

  @Builder
  DragBar() {
    Column() {
      Row()
        .height($r('app.float.drag_bar_height'))
        .width($r('app.float.drag_bar_width'))
        .backgroundColor($r('sys.color.ohos_id_color_fourth'))
        .borderRadius($r('app.float.drag_bar_border_radius'))
        .margin({
          top: $r('app.float.sm_padding_margin'),
          bottom: $r('app.float.xs_padding_margin')
        })
    }
    .gesture(
      PanGesture({
        direction: this.currentBreakpoint === BreakpointTypeEnum.SM ? PanDirection.None : PanDirection.Horizontal
      })
        .onActionStart((event: GestureEvent) => {
          Logger.info(TAG, `Bar PanGesture start:   ${JSON.stringify(event)}`);
          this.currentPositionX = this.positionX;
        })
        .onActionUpdate((event: GestureEvent) => {
          Logger.info(TAG, `Bar PanGesture update:   ${JSON.stringify(event)}`);
          let offsetX: number = event.offsetX;
          animateTo({
            curve: this.interpolatingCurve
          }, () => {
            this.positionX = this.currentPositionX + offsetX;
          });
        })
        .onActionEnd((event: GestureEvent) => {
          Logger.info(TAG, `Bar PanGesture end:   ${JSON.stringify(event)}`);
          let rightThreshold: number = (this.maxPositionX - this.minPositionX) / 2 + this.minPositionX;
          animateTo({
            curve: this.interpolatingCurve
          }, () => {
            this.positionX = this.positionX > rightThreshold ? this.maxPositionX : this.minPositionX;
          });
        }))
    .width(CommonConstants.FULL_PERCENT)
    .backgroundColor(Color.Transparent)
  }

  // get text of whole zone item
  getAllText(): string {
    const contentText: string = this.selectedZone.content.map((res: ResourceStr) => typeof res === 'string' ?
      res : resourceManager.getStringSync(res)).join('');
    return contentText + resourceManager.getStringSync(this.selectedZone.buildingInformation);
  }

  build() {
    Column() {
      Stack({ alignContent: Alignment.TopEnd }) {
        ZoneDetailView({
          detailSheetHeight: $detailSheetHeight,
          detailSheetMinHeight: this.detailSheetMinHeight,
          detailSheetMaxHeight: this.detailSheetMaxHeight
        })
        this.DragBar()
        Button({ type: ButtonType.Circle }) {
          Image($r('app.media.ic_modal_close'))
            .height($r('app.float.sm_icon_size'))
        }
        .width($r('app.float.md_btn_height'))
        .height($r('app.float.md_btn_height'))
        .backgroundColor($r('app.color.btn_bg_color'))
        .backgroundBlurStyle(BlurStyle.Thin, { colorMode: ThemeColorMode.DARK, adaptiveColor: AdaptiveColor.AVERAGE })
        .margin({ top: $r('app.float.lg_padding_margin'), right: $r('app.float.lg_padding_margin') })
        .onClick(() => {
          this.showDetail = false;
          this.detailSheetHeight = this.detailSheetMinHeight;
          AudioPlayerService.getInstance().stop();
        })

        SpeakPlayerButton({
          speakText: this.getAllText(),
          onInitSpeak: async () => {
            const swiperImagePixMap = await ImageUtil.getPixmapFromMedia(this.selectedZone.swiperPic);
            AppStorage.setOrCreate<avSession.AVMetadata>('avMetadata', {
              assetId: util.generateRandomUUID(false),
              mediaImage: swiperImagePixMap
            })
          }
        })
          .margin({ top: $r('app.float.lg_padding_margin'), right: 72 })
      }
      .width(CommonConstants.FULL_PERCENT)
      .layoutWeight(1)

      Column({ space: CommonConstants.SPACE_12 }) {
        Row() {
          Text(this.selectedZone.title)
            .fontColor($r('sys.color.ohos_id_color_text_primary'))
            .fontSize($r('app.float.duration_font_size'))
            .fontWeight(FontWeight.Medium)
          Text(this.distance ? $r('app.string.distance_from', this.distance) : '')
            .margin({ left: $r('app.float.sm_padding_margin') })
            .fontSize($r('app.float.small_font_size'))
            .fontColor($r('sys.color.ohos_id_color_text_tertiary'))
        }
        .width(CommonConstants.FULL_PERCENT)

        Row({ space: CommonConstants.SPACE_8 }) {
          Button($r('app.string.routes'))
            .layoutWeight(1)
            .height($r('app.float.location_img_size'))
            .onClick(() => this.clickAction(ModalActionType.ROUTE))
          Image($r('app.media.ic_navigation'))
            .height($r('app.float.location_img_size'))
            .aspectRatio(1)
            .onClick(() => this.clickAction(ModalActionType.NAVIGATION))
        }
      }
      .height($r('app.float.detail_info_height'))
      .padding({
        top: $r('app.float.md_padding_margin'),
        bottom: $r('app.float.md_padding_margin'),
        left: $r('app.float.lg_padding_margin'),
        right: $r('app.float.lg_padding_margin')
      })
      .backgroundColor($r('sys.color.ohos_id_color_panel_bg'))
    }
    .borderRadius({
      topLeft: $r('app.float.border_radius_xxl'),
      topRight: $r('app.float.border_radius_xxl'),
      bottomLeft: new BreakpointType<Length>({
        sm: 0,
        md: $r('app.float.border_radius_xxl'),
        lg: $r('app.float.border_radius_xxl')
      }).getValue(this.currentBreakpoint),
      bottomRight: new BreakpointType<Length>({
        sm: 0,
        md: $r('app.float.border_radius_xxl'),
        lg: $r('app.float.border_radius_xxl')
      }).getValue(this.currentBreakpoint)
    })
    .clip(true)
    .animation({ curve: this.interpolatingCurve })
    .opacity(this.showDetail ? 1 : 0)
    .backgroundColor($r('sys.color.ohos_id_color_panel_bg'))
    .height(this.showDetail ? this.detailSheetHeight : 0)
    .width(this.currentBreakpoint === BreakpointTypeEnum.SM ? '100%' : Const.MODAL_MAX_WIDTH)
    .shadow({ radius: $r('app.float.md_border_radius'), color: $r('app.color.shadow_color'), offsetY: 8 })
    .margin({
      bottom: new BreakpointType<Length>({
        sm: 0,
        md: $r('app.float.xl_padding_margin'),
        lg: (AppStorage.get('naviIndicatorHeight') as number)
      }).getValue(this.currentBreakpoint),
      left: this.currentBreakpoint === BreakpointTypeEnum.SM ? 0 : this.positionX
    })
    .gesture(
      GestureGroup(GestureMode.Parallel,
        SwipeGesture({ direction: SwipeDirection.Vertical })
          .onAction((event: GestureEvent) => {
            animateTo({
              curve: this.interpolatingCurve
            }, () => {
              if (event.speed > 1000) {
                if (event.angle < 0 && this.detailSheetHeight < this.detailSheetMaxHeight) {
                  // Swipe up.
                  this.detailSheetHeight = this.detailSheetMaxHeight;

                } else {
                  // Swipe down.
                  if (this.detailSheetHeight > this.detailSheetMinHeight) {
                    this.detailSheetHeight = this.detailSheetMinHeight;
                  } else {
                    this.showDetail = false;
                  }
                }
              }
              Logger.info(TAG, `SwipeGesture speed: ${event.speed} , angle: ${event.angle}`)
            })
          }),
        PanGesture({ direction: PanDirection.Vertical })
          .onActionStart(() => {
            this.currentHeight = this.detailSheetHeight;
          })
          .onActionUpdate((event: GestureEvent) => {
            Logger.info(TAG, `PanGesture update:   ${JSON.stringify(event)}`);
            let offsetY: number = event.offsetY;
            animateTo({
              curve: this.interpolatingCurve
            }, () => {
              this.detailSheetHeight = this.currentHeight - offsetY;
            });
          })
          .onActionEnd((event: GestureEvent) => {
            Logger.info(TAG, `PanGesture end:   ${JSON.stringify(event)}`);
            let highThreshold: number = (this.detailSheetMaxHeight - this.detailSheetMinHeight) / 2;
            let lowThreshold: number = this.detailSheetMinHeight / 2;
            animateTo({
              curve: this.interpolatingCurve
            }, () => {
              if (this.detailSheetHeight > this.detailSheetMaxHeight) {
                this.detailSheetHeight = this.detailSheetMaxHeight;
              } else if (this.detailSheetHeight < this.detailSheetMinHeight) {
                if (this.detailSheetHeight < lowThreshold) {
                  this.showDetail = false;
                  this.detailSheetHeight = this.detailSheetMinHeight;
                } else {
                  this.detailSheetHeight = this.detailSheetMinHeight;
                }
              } else {
                let distance = Math.abs(this.detailSheetHeight - this.detailSheetMinHeight);
                this.detailSheetHeight =
                  distance > highThreshold ? this.detailSheetMaxHeight : this.detailSheetMinHeight;
              }
            })
          })
      )
    )
  }
}