Mercurial > public > lazybear
changeset 389:db8bc3ed526a
Implementing add to watchlist feature from SearchView
author | Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> |
---|---|
date | Sun, 25 Apr 2021 16:42:26 +0200 |
parents | 79c39987aaa4 |
children | 6303385b3629 |
files | LazyBear.xcodeproj/project.pbxproj LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate LazyBear/Views/Global Helpers/Company list/ExtensiveList.swift LazyBear/Views/Global Helpers/Company list/Helpers/ToolbarMenu.swift LazyBear/Views/Global Helpers/ExtensiveList.swift LazyBear/Views/Profile/Helpers/RenameSheet.swift LazyBear/Views/Profile/ProfileView.swift LazyBear/Views/Search/CompanyList.swift LazyBear/Views/Search/Helpers/SearchedCompanyItem.swift LazyBear/Views/Search/SearchView.swift |
diffstat | 10 files changed, 274 insertions(+), 157 deletions(-) [+] |
line wrap: on
line diff
--- a/LazyBear.xcodeproj/project.pbxproj Sat Apr 24 17:44:02 2021 +0200 +++ b/LazyBear.xcodeproj/project.pbxproj Sun Apr 25 16:42:26 2021 +0200 @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 950272CD2635AACD003E779D /* ToolbarMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950272CC2635AACD003E779D /* ToolbarMenu.swift */; }; 950C36E3260FB6180081CF53 /* HapticsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950C36E2260FB6180081CF53 /* HapticsManager.swift */; }; 950C57132629EF9100F234FE /* LazyBearTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950C57122629EF9100F234FE /* LazyBearTests.swift */; }; 950C57232629EFC200F234FE /* LazyBearUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950C57222629EFC200F234FE /* LazyBearUITests.swift */; }; @@ -77,6 +78,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 950272CC2635AACD003E779D /* ToolbarMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarMenu.swift; sourceTree = "<group>"; }; 950C36E2260FB6180081CF53 /* HapticsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticsManager.swift; sourceTree = "<group>"; }; 950C57102629EF9100F234FE /* LazyBearTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LazyBearTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 950C57122629EF9100F234FE /* LazyBearTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyBearTests.swift; sourceTree = "<group>"; }; @@ -163,6 +165,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 950272CA2635AA9F003E779D /* Company list */ = { + isa = PBXGroup; + children = ( + 95721DB3262787EF00EC527B /* ExtensiveList.swift */, + 950272CB2635AABC003E779D /* Helpers */, + ); + path = "Company list"; + sourceTree = "<group>"; + }; + 950272CB2635AABC003E779D /* Helpers */ = { + isa = PBXGroup; + children = ( + 950272CC2635AACD003E779D /* ToolbarMenu.swift */, + ); + path = Helpers; + sourceTree = "<group>"; + }; 950C57112629EF9100F234FE /* LazyBearTests */ = { isa = PBXGroup; children = ( @@ -324,12 +343,12 @@ 95893DD22613CAB5003698C5 /* Global Helpers */ = { isa = PBXGroup; children = ( + 950272CA2635AA9F003E779D /* Company list */, 95ECCA5F261216D500A67EFA /* LineView.swift */, 95ECCA5C2612169200A67EFA /* LineShape.swift */, 95A5188526186F590002D27C /* PriceView.swift */, 9550444826111FC9000E0BCB /* StockRow.swift */, 9550444B26111FED000E0BCB /* StockItem.swift */, - 95721DB3262787EF00EC527B /* ExtensiveList.swift */, 95BD2FB226341D36008B6752 /* BlurBackground.swift */, ); path = "Global Helpers"; @@ -554,6 +573,7 @@ 9550444326111E7A000E0BCB /* SectorRow.swift in Sources */, 95721DB826278EC100EC527B /* CurrencyListItem.swift in Sources */, 95ECCA60261216D500A67EFA /* LineView.swift in Sources */, + 950272CD2635AACD003E779D /* ToolbarMenu.swift in Sources */, 9550443A26111B2B000E0BCB /* HomeView.swift in Sources */, 95672B9825DDA54700DCBE4A /* Persistence.swift in Sources */, 95A7C0742616409D003E2EC1 /* ParseJSON.swift in Sources */,
Binary file LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Views/Global Helpers/Company list/ExtensiveList.swift Sun Apr 25 16:42:26 2021 +0200 @@ -0,0 +1,138 @@ +// +// ExtensiveList.swift +// LazyBear +// +// Created by Dennis Concepción Martín on 14/4/21. +// + +import SwiftUI + +struct ExtensiveList: View { + var listName: String + var list: [String: QuoteModel]? + var intradayPrices: [String: [IntradayPriceModel]]? + var latestCurrencies: [String: CurrencyModel]? + var addOnDelete: Bool + + @Environment(\.presentationMode) private var presentationMode + @Environment(\.managedObjectContext) private var moc + @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) + var watchlistCompany: FetchedResults<WatchlistCompany> + + @State private var isEditMode: EditMode = .inactive + @State private var showRenameAction = false + @State private var showDeleteAlert = false + @State private var showSearchView = false + + var body: some View { + NavigationView { + ZStack { + VStack { + if let list = list { + List { + ForEach(Array(list.keys.sorted()), id: \.self) { companySymbol in + StockItem(symbol: companySymbol, + company: list[companySymbol]!, + intradayPrices: intradayPrices?[companySymbol], + orientation: .horizontal, + hidePriceView: self.isEditMode == .active // Hide on EditMode + ) + + } + .onDelete(perform: addOnDelete ? deleteCompany: nil) + } + } + + if let latestCurrencies = latestCurrencies { + List(Array(latestCurrencies.keys.sorted()), id: \.self) { currencySymbol in + CurrencyListItem(currencySymbol: currencySymbol, currency: latestCurrencies[currencySymbol]!) + + } + } + } + + // Blur background + Color(.black) + .edgesIgnoringSafeArea(.all) + .opacity(showRenameAction ? 0.2: 0) + .animation(.easeInOut) + .onTapGesture { showRenameAction = false } + + // Show rename Action Sheet + RenameSheet(listName: listName, showRenameAction: $showRenameAction, presentationMode: presentationMode) + .offset(y: showRenameAction ? 0: 700) + .animation(.easeInOut) + } + // Show delete list alert + .alert(isPresented: $showDeleteAlert) { + Alert( + title: Text("Are you sure you want to delete this list?"), + message: Text("This action can't be undo"), + primaryButton: .destructive(Text("Delete")) { deleteList() }, + secondaryButton: .cancel() + ) + } + .sheet(isPresented: $showSearchView) { + SearchView() + } + .navigationTitle(listName) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + if addOnDelete { + EditButton() + } else { + Button(action: { presentationMode.wrappedValue.dismiss() }) { + Image(systemName: "multiply") + .imageScale(.large) + } + } + } + ToolbarItem(placement: .navigationBarTrailing) { + if addOnDelete { + ToolbarMenu(showRenameAction: $showRenameAction, + showSearchView: $showSearchView, + showDeleteAlert: $showDeleteAlert + ) + } + } + } + .environment(\.editMode, self.$isEditMode) // Always after Toolbar + } + } + + // Delete company from watchlist + private func deleteCompany(at offsets: IndexSet) { + for index in offsets { + let company = watchlistCompany[index] + moc.delete(company) + } + do { + try moc.save() + print("Company deleted") + } catch { + print(error.localizedDescription) + } + } + + // Remove entire watchlist + private func deleteList() { + let selectedWatchlist = watchlistCompany.filter({ $0.watchlist == listName }) + for company in selectedWatchlist { + moc.delete(company) + } + do { + try moc.save() + print("List deleted") + presentationMode.wrappedValue.dismiss() // Dismiss view + } catch { + print(error.localizedDescription) + } + } +} + +struct ExtensiveList_Previews: PreviewProvider { + static var previews: some View { + ExtensiveList(listName: "List name", addOnDelete: false) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Views/Global Helpers/Company list/Helpers/ToolbarMenu.swift Sun Apr 25 16:42:26 2021 +0200 @@ -0,0 +1,44 @@ +// +// ToolbarMenu.swift +// LazyBear +// +// Created by Dennis Concepción Martín on 25/4/21. +// + +import SwiftUI + +struct ToolbarMenu: View { + @Binding var showRenameAction: Bool + @Binding var showSearchView: Bool + @Binding var showDeleteAlert: Bool + + var body: some View { + Menu { + Section { + Button(action: { showRenameAction = true }) { + Label("Rename list", systemImage: "square.and.pencil") + } + + Button(action: { showSearchView = true }) { + Label("Add company", systemImage: "plus") + } + } + + Section(header: Text("Secondary actions")) { + Button(action: { showDeleteAlert = true }) { + Label("Delete list", systemImage: "trash") + } + } + } + label: { + Label("Options", systemImage: "ellipsis.circle") + .imageScale(.large) + } + } +} + +struct ToolbarMenu_Previews: PreviewProvider { + static var previews: some View { + ToolbarMenu(showRenameAction: .constant(false), showSearchView: .constant(false), showDeleteAlert: .constant(false)) + } +}
--- a/LazyBear/Views/Global Helpers/ExtensiveList.swift Sat Apr 24 17:44:02 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ -// -// ExtensiveList.swift -// LazyBear -// -// Created by Dennis Concepción Martín on 14/4/21. -// - -import SwiftUI - -struct ExtensiveList: View { - var listName: String - var list: [String: QuoteModel]? - var intradayPrices: [String: [IntradayPriceModel]]? - var latestCurrencies: [String: CurrencyModel]? - var addOnDelete: Bool - - @Environment(\.presentationMode) private var presentationMode - @Environment(\.managedObjectContext) private var moc - @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) - var watchlistCompany: FetchedResults<WatchlistCompany> - - @State private var isEditMode: EditMode = .inactive - @State private var showRenameAction = false - @State private var showDeleteAlert = false - - var body: some View { - NavigationView { - ZStack { - VStack { - if let list = list { - List { - ForEach(Array(list.keys.sorted()), id: \.self) { companySymbol in - StockItem(symbol: companySymbol, - company: list[companySymbol]!, - intradayPrices: intradayPrices?[companySymbol], - orientation: .horizontal, - hidePriceView: self.isEditMode == .active // Hide on EditMode - ) - - } - .onDelete(perform: addOnDelete ? deleteCompany: nil) - } - } - - if let latestCurrencies = latestCurrencies { - List(Array(latestCurrencies.keys.sorted()), id: \.self) { currencySymbol in - CurrencyListItem(currencySymbol: currencySymbol, currency: latestCurrencies[currencySymbol]!) - - } - } - } - - // Blur background - Color(.black) - .edgesIgnoringSafeArea(.all) - .opacity(showRenameAction ? 0.2: 0) - .animation(.easeInOut) - .onTapGesture { showRenameAction = false } - - // Show rename Action Sheet - RenameSheet(listName: listName, showRenameAction: $showRenameAction, presentationMode: presentationMode) - .offset(y: showRenameAction ? 0: 700) - .animation(.easeInOut) - } - // Show delete list alert - .alert(isPresented: $showDeleteAlert) { - Alert( - title: Text("Are you sure you want to delete this list?"), - message: Text("This action can't be undo"), - primaryButton: .destructive(Text("Delete")) { deleteList() }, - secondaryButton: .cancel() - ) - } - .navigationTitle(listName) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .cancellationAction) { - if addOnDelete { - EditButton() - } else { - Button(action: { presentationMode.wrappedValue.dismiss() }) { - Image(systemName: "multiply") - .imageScale(.large) - } - } - } - ToolbarItem(placement: .navigationBarTrailing) { - if addOnDelete { - Menu { - Section { - Button(action: { self.showRenameAction = true }) { - Label("Rename list", systemImage: "square.and.pencil") - } - - Button(action: { print("Add company") }) { - Label("Add company", systemImage: "plus") - } - } - - Section(header: Text("Secondary actions")) { - Button(action: { self.showDeleteAlert = true }) { - Label("Delete list", systemImage: "trash") - } - } - } - label: { - Label("Options", systemImage: "ellipsis.circle") - .imageScale(.large) - } - } - } - } - .environment(\.editMode, self.$isEditMode) // Always after Toolbar - } - } - - // Delete company from watchlist - private func deleteCompany(at offsets: IndexSet) { - for index in offsets { - let company = watchlistCompany[index] - moc.delete(company) - } - do { - try moc.save() - print("Company deleted") - } catch { - // Error - } - } - - // Remove entire watchlist - private func deleteList() { - let selectedWatchlist = watchlistCompany.filter({ $0.watchlist == listName }) - for company in selectedWatchlist { - moc.delete(company) - } - do { - try moc.save() - print("List deleted") - presentationMode.wrappedValue.dismiss() // Dismiss view - } catch { - print(error.localizedDescription) - } - } -} - -struct ExtensiveList_Previews: PreviewProvider { - static var previews: some View { - ExtensiveList(listName: "List name", addOnDelete: false) - } -}
--- a/LazyBear/Views/Profile/Helpers/RenameSheet.swift Sat Apr 24 17:44:02 2021 +0200 +++ b/LazyBear/Views/Profile/Helpers/RenameSheet.swift Sun Apr 25 16:42:26 2021 +0200 @@ -19,7 +19,7 @@ var watchlistCompany: FetchedResults<WatchlistCompany> var body: some View { - RoundedRectangle(cornerRadius: 20) + RoundedRectangle(cornerRadius: 15) .frame(width: 280, height: 180) .foregroundColor(Color(.secondarySystemBackground)) .overlay( @@ -32,10 +32,10 @@ Spacer() TextField("Technologies, banks...", text: $newListName) - .padding(8) + .padding(7) .background( Color(.systemBackground) - .cornerRadius(8) + .cornerRadius(7) ) Divider() @@ -67,7 +67,7 @@ ) .background( BlurBackground(style: .systemMaterial) - .clipShape(RoundedRectangle(cornerRadius: 20)) + .clipShape(RoundedRectangle(cornerRadius: 15)) ) }
--- a/LazyBear/Views/Profile/ProfileView.swift Sat Apr 24 17:44:02 2021 +0200 +++ b/LazyBear/Views/Profile/ProfileView.swift Sun Apr 25 16:42:26 2021 +0200 @@ -12,6 +12,10 @@ @ObservedObject var profile = Profile() @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) var watchlistCompanies: FetchedResults<WatchlistCompany> + + // Refresh view when watchlistCompanies change + @State private var refreshing = false + private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave) var body: some View { if profile.showView { @@ -34,6 +38,10 @@ .listRowInsets(EdgeInsets()) } } + // The listener for refresh the view + .onReceive(self.didSave) { _ in + self.refreshing.toggle() + } } .navigationTitle("My profile") .navigationBarTitleDisplayMode(.inline)
--- a/LazyBear/Views/Search/CompanyList.swift Sat Apr 24 17:44:02 2021 +0200 +++ b/LazyBear/Views/Search/CompanyList.swift Sun Apr 25 16:42:26 2021 +0200 @@ -12,7 +12,9 @@ var body: some View { List(searchResult, id: \.self) { company in - SearchedCompanyItem(company: company) + NavigationLink(destination: Text("Hello")) { + SearchedCompanyItem(company: company) + } } .listStyle(GroupedListStyle()) }
--- a/LazyBear/Views/Search/Helpers/SearchedCompanyItem.swift Sat Apr 24 17:44:02 2021 +0200 +++ b/LazyBear/Views/Search/Helpers/SearchedCompanyItem.swift Sun Apr 25 16:42:26 2021 +0200 @@ -10,8 +10,28 @@ struct SearchedCompanyItem: View { var company: SearchResponse + @Environment(\.managedObjectContext) private var moc + @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) + var watchlistCompany: FetchedResults<WatchlistCompany> + + @State private var showingActionSheet = false + var body: some View { + let watchlistSymbols = watchlistCompany.map { $0.symbol } HStack { + Button(action: { self.showingActionSheet = true }) { + if watchlistSymbols.contains(company.symbol!) { + Image(systemName: "star.fill") + .foregroundColor(.yellow) + .imageScale(.large) + } else { + Image(systemName: "star") + .foregroundColor(.yellow) + .imageScale(.large) + } + } + .buttonStyle(PlainButtonStyle()) + VStack(alignment: .leading) { Text(company.symbol!.uppercased()) .fontWeight(.semibold) @@ -29,6 +49,41 @@ Text(company.region!) } } + .actionSheet(isPresented: $showingActionSheet) { + ActionSheet(title: Text("Your watchlists"), message: Text("Select"), buttons: generateButtons()) + } + } + + // Get watchlist names -> generate buttons + private func generateButtons() -> [ActionSheet.Button] { + var actionButtons = [ActionSheet.Button]() + let watchlists = Set(watchlistCompany.map { $0.watchlist }) + + for watchlistName in watchlists { + actionButtons.append( + .default(Text(watchlistName)) { + addCompany(company.symbol!, company.securityName!, watchlistName) + } + ) + } + + actionButtons.append(.cancel()) + + return actionButtons + } + + // Add to watchlist + private func addCompany(_ symbol: String, _ name: String, _ watchlist: String) { + let watchlistCompany = WatchlistCompany(context: moc) + watchlistCompany.symbol = symbol + watchlistCompany.name = name + watchlistCompany.watchlist = watchlist + do { + try moc.save() + print("Company saved") + } catch { + print(error.localizedDescription) + } } }
--- a/LazyBear/Views/Search/SearchView.swift Sat Apr 24 17:44:02 2021 +0200 +++ b/LazyBear/Views/Search/SearchView.swift Sun Apr 25 16:42:26 2021 +0200 @@ -10,6 +10,7 @@ struct SearchView: View { @ObservedObject var search = Search() + @Environment(\.presentationMode) private var presentationMode @State private var searchedText = String() var body: some View {