Newer
Older
bremer-ios-app / BremerApp / View / PlaylistView.swift
yhornisse on 10 Sep 2023 14 KB Initial Commit
//
//  PlaylistView.swift
//  Bremer
//
//  Created by yhornisse on 2023/07/17.
//

import SwiftUI

struct PlaylistTopView : View {
    
    @State var filterText = ""
    @State var showedLoginView  = false
    @State var playlistNewName = ""
    @State var playlistTargetSlug = ""
    @State var showedNewPlaylistDialog = false
    @State var showedUpdatePlaylistDialog = false
    @State var showedErrorAlert = false
    @State var showedDeletePlaylistDialog = false
    @State private var errorAlertTitle = ""
    @EnvironmentObject private var settingViewModel : SettingViewModel
    @ObservedObject var allPlaylistViewModel = AllPlaylistViewModel()
    
    var filterResults: [Playlist] {
        if filterText.isEmpty {
            return allPlaylistViewModel.playlists
        } else {
            return allPlaylistViewModel.playlists.filter { $0.name.contains(filterText) }
        }
    }
    
    var body : some View {
        NavigationView {
            VStack {
                if settingViewModel.getSetting().baseUrl == "" {
                    Text("Base URLを設定してください")
                } else if !allPlaylistViewModel.messageText.isEmpty {
                    if allPlaylistViewModel.hasError {
                        Text(allPlaylistViewModel.messageText)
                            .foregroundColor(.red)
                        if allPlaylistViewModel.messageText == "ログインしてください" {
                            LoginButton(action: {
                                self.showedLoginView = true
                            })
                        }
                    } else {
                        Text(allPlaylistViewModel.messageText)
                    }
                }
                List {
                    ForEach(filterResults, id:\.slug) { playlist in
                        NavigationLink(destination: PlaylistDescView(slug: playlist.slug)) {
                            Text(playlist.name)
                                .buttonStyle(.bordered)
                                .font(.system(size: 16))
                                .aspectRatio(contentMode: .fit)
                        }
                        .swipeActions(edge: .trailing) {
                            Button {
                                self.playlistTargetSlug = playlist.slug
                                self.showedDeletePlaylistDialog = true
                            } label: {
                                Text("削除")
                            }
                            .tint(.red)
                            Button {
                                self.playlistTargetSlug = playlist.slug
                                self.playlistNewName = playlist.name
                                self.showedUpdatePlaylistDialog = true
                            } label: {
                                Text("変更")
                            }
                            .tint(.green)
                        }
                    }
                }
                .listStyle(.plain)
                .searchable(text: $filterText, prompt: "検索キーワード")
                .keyboardType(.default)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        self.playlistNewName = ""
                        self.showedNewPlaylistDialog = true
                    }) {
                        Image(systemName: "plus.app")
                    }
                }
            }
            .alert("新規登録", isPresented: $showedNewPlaylistDialog) {
                NameAlertView(label: "プレイリスト", act: "追加", onOk: allPlaylistViewModel.createPlaylist) {(message: String) in
                    self.showedErrorAlert = true
                    self.errorAlertTitle = message
                }
            } message: {
                Text("プレイリスト名を入力してください")
            }
            .alert("更新", isPresented: $showedUpdatePlaylistDialog) {
                NameAlertView(label: "プレイリスト", act: "変更", input: playlistNewName) {(name: String) in allPlaylistViewModel.updatePlaylist(playlistTargetSlug, name)
                } onError : {(message: String) in
                    self.showedErrorAlert = true
                    self.errorAlertTitle = message
                }
            } message: {
                Text("プレイリスト名を入力してください")
            }
            .confirmationDialog(
                "プレイリスト削除",
                isPresented: $showedDeletePlaylistDialog,
                actions: {
                    Button("OK", role: .destructive){
                        allPlaylistViewModel.deletePlaylist(playlistTargetSlug)
                    }
                    Button("キャンセル", role: .cancel){
                        self.playlistNewName = ""
                    }
                }, message: {
                    Text( "本当にプレイリストを削除しますか?")
                })
            .alert(errorAlertTitle, isPresented: $showedErrorAlert, actions: {
                Button(action: {
                    // nop
                }, label: {
                    Text("確認")
                })
            })
        }
        .sheet(isPresented: $showedLoginView) {
            LoginWebView()
                .onDisappear {
                    allPlaylistViewModel.getAllPlaylist()
                }
        }
        .scrollDismissesKeyboard(.immediately)
        .onAppear {
            allPlaylistViewModel.bremerApiBaseUrl = settingViewModel.getSetting().baseApiUrl()
            allPlaylistViewModel.getAllPlaylist()
        }
    }
}

struct PlaylistDescView : View {
    @EnvironmentObject
    private var playlistAudioViewModel : PlaylistAudioViewModel
    @EnvironmentObject
    private var audioPlayerViewModel : AudioPlayerViewModel
    @EnvironmentObject
    private var configViewModel : SettingViewModel
    var slug : String
    
    var body : some View {
        VStack {
            if !playlistAudioViewModel.messageText.isEmpty {
                if (playlistAudioViewModel.hasError) {
                    VStack {
                        Text(playlistAudioViewModel.messageText)
                            .foregroundColor(.red)
                    }
                } else {
                    Text(playlistAudioViewModel.messageText)
                }
            }
            List {
                ForEach(playlistAudioViewModel.result, id:\.slug) { audio in
                    Button(action: {
                        audioPlayerViewModel.playAudioList(
                            audioList: playlistAudioViewModel.result, audio: audio, usedBy: "playlist")
                    }) {
                        Text("\(audio.name) - \(audio.album ?? "")")
                            .font(.system(size: 14))
                            .frame(height: 35, alignment: .center)
                            .aspectRatio(contentMode: .fit)
                    }
                    .swipeActions(edge: .trailing) {
                        Button {
                            // TODO 再生中の時は削除する必要ある
                            self.playlistAudioViewModel.deleteAudioInPlaylist(playlistSlug: slug, audioSlug: audio.slug)
                        } label: {
                            Text("削除")
                        }
                        .tint(.red)
                    }
                }
                .onMove {src, dst in
                    self.audioPlayerViewModel.reorderAudioList(from: src, to: dst, usedBy: "playlist")
                    self.playlistAudioViewModel.reorderPlaylist(from: src, to: dst)
                    self.playlistAudioViewModel.save(slug: slug)
                }
            }
            .listStyle(.plain)
            .keyboardType(.default)
            Spacer()
            Divider()
            AudioPlayerView(audioPlayerViewModel: audioPlayerViewModel)
        }
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                NavigationLink(destination: PlaylistAddView(slug: slug)){
                    Image(systemName: "plus.app")
                }
            }
        }
        .onAppear {
            self.playlistAudioViewModel.bremerApiBaseUrl = configViewModel.getSetting().baseApiUrl()
            self.playlistAudioViewModel.getAllAudio(slug: slug)
        }
    }
}

struct PlaylistAddView : View {
    @State
    var slug : String
    @State
    private var searchText : String = ""
    @State
    private var showedLoginView = false
    @EnvironmentObject
    private var playlistAudioViewModel : PlaylistAudioViewModel
    @EnvironmentObject
    private var audioPlayerViewModel : AudioPlayerViewModel
    @EnvironmentObject
    private var searchAudioViewModel : SearchAudioViewModel
    @EnvironmentObject
    private var configViewModel : SettingViewModel
    @State
    private var audioTargetSlug = ""
    @State
    private var showedErrorAlert = false
    @State
    private var errorAlertTitle = ""
    @State
    private var audioInfo = AudioInfo()
    @State
    private var showedToast = false
    @State
    private var toastMessage = ""
    @State
    private var showedUpdateAudioDialog = false
    @State
    var showedSearchAudioDialog = false
    @State
    var shouldShowSecondView = false
    
    var body : some View {
        NavigationView {
            ZStack {
                VStack {
                    if !searchAudioViewModel.messageText.isEmpty {
                        if (searchAudioViewModel.hasError) {
                            VStack {
                                Text(searchAudioViewModel.messageText)
                                    .foregroundColor(.red)
                                LoginButton(action: {
                                    self.showedLoginView = true
                                })
                            }
                        } else {
                            Text(searchAudioViewModel.messageText)
                        }
                    }
                    List {
                        ForEach(searchAudioViewModel.result, id:\.slug) { audio in
                            HStack {
                                Button(action: {
                                    audioPlayerViewModel.playAudioList(
                                        audioList: [audio], audio: audio, usedBy: "playlist-add")
                                }) {
                                    Text("\(audio.name) - \(audio.album ?? "")")
                                        .font(.system(size: 14))
                                        .frame(height: 35, alignment: .center)
                                        .aspectRatio(contentMode: .fit)
                                }
                                .buttonStyle(PlainButtonStyle())
                                Spacer()
                                Button(action: {
                                    playlistAudioViewModel.addAudio(audio: audio)
                                    audioPlayerViewModel.addAudio(audio: audio, usedBy: "playlist-\(slug)")
                                    playlistAudioViewModel.save(slug: self.slug)
                                    self.toastMessage = "\(audio.name) を追加"
                                    self.showedToast = true
                                }) {
                                    Image(systemName: "plus.app")
                                        .resizable()
                                        .scaledToFill()
                                        .frame(width: 33, height: 33)
                                }
                                .buttonStyle(PlainButtonStyle())
                            }
                            .swipeActions(edge: .trailing) {
                                Button(action: {
                                    self.audioTargetSlug = audio.slug
                                    self.audioInfo.audioName = audio.name
                                    self.audioInfo.artistName = audio.artist ?? ""
                                    self.audioInfo.albumName = audio.album ?? ""
                                    self.audioInfo.localPath = audio.localPath
                                    // self.showedUpdateAudioDialog = true
                                    self.shouldShowSecondView = true
                                }) {
                                    Text("変更")
                                }
                                .tint(.green)
                            }
                        }
                    }
                    .listStyle(.plain)
                    .searchable(text: $searchText, prompt: "検索キーワード")
                    .onSubmit(of: .search) {
                        searchAudioViewModel.searchAudio(keyword: searchText)
                    }
                }
                if showedToast {
                    VStack {
                        Spacer()
                        ToastView(message: toastMessage)
                    }
                    .onAppear(perform: {
                        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                            withAnimation {
                                self.showedToast = false
                            }
                        }
                    })
                }
            }
        }
        .sheet(isPresented: $showedLoginView) {
            LoginWebView()
        }
        .sheet(isPresented: $shouldShowSecondView) {
            RenameAudioView(audioInfo: self.audioInfo){(audioInfo : AudioInfo) in
                searchAudioViewModel.updateAudio(
                    slug: self.audioTargetSlug,
                    audioName: audioInfo.audioName,
                    artistName: audioInfo.artistName,
                    albumName: audioInfo.albumName)
            }
        }
        .scrollDismissesKeyboard(.immediately)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button(action: {
                    self.showedSearchAudioDialog = true
                }) {
                    Image(systemName: "text.magnifyingglass")
                }
            }
        }
        .alert("詳細検索", isPresented: $showedSearchAudioDialog) {
            SearchDetailAlertView(searchAction: searchAudioViewModel.searchAudio)
        } message: {
            Text("新しい名前を入力してください")
        }
        .onAppear {
            searchAudioViewModel.bremerApiBaseUrl = configViewModel.getSetting().baseApiUrl()
            searchAudioViewModel.result = []
        }
    }
}