Mercurial > public > lazybear
changeset 407:c804ce7a1560
Implementing Insider networking
author | Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> |
---|---|
date | Sun, 06 Jun 2021 13:11:41 +0200 |
parents | 09d05e48462f |
children | f9611c94d636 |
files | LazyBear.xcodeproj/project.pbxproj LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate LazyBear/Global Models/InsiderRosterModel.swift LazyBear/Global Models/SummaryInsiderModel.swift LazyBear/Global functions/ConvertEpoch.swift LazyBear/Views/Company/Chart.swift LazyBear/Views/Company/CompanyView.swift LazyBear/Views/Company/Helpers/InsiderRow.swift LazyBear/Views/Company/Helpers/NewsRow.swift LazyBear/Views/Company/Insiders.swift LazyBear/Views/Company/Networking/Company.swift LazyBear/Views/Company/Networking/InsidersResponse.swift |
diffstat | 12 files changed, 137 insertions(+), 76 deletions(-) [+] |
line wrap: on
line diff
--- a/LazyBear.xcodeproj/project.pbxproj Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear.xcodeproj/project.pbxproj Sun Jun 06 13:11:41 2021 +0200 @@ -35,7 +35,7 @@ 955E73392623568F005652FF /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955E73382623568F005652FF /* Home.swift */; }; 955E733C262356F3005652FF /* HomeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955E733B262356F3005652FF /* HomeResponse.swift */; }; 95602702265ABB440046F97E /* InsidersResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95602701265ABB440046F97E /* InsidersResponse.swift */; }; - 95602704265ABB990046F97E /* SummaryInsiderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95602703265ABB990046F97E /* SummaryInsiderModel.swift */; }; + 95602704265ABB990046F97E /* InsiderRosterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95602703265ABB990046F97E /* InsiderRosterModel.swift */; }; 95602706265ABC660046F97E /* Insiders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95602705265ABC660046F97E /* Insiders.swift */; }; 95606D082647005B0072C02C /* StockCharts in Frameworks */ = {isa = PBXBuildFile; productRef = 95606D072647005B0072C02C /* StockCharts */; }; 95613AD9264FC5A900D4CE8F /* Company.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95613AD8264FC5A900D4CE8F /* Company.swift */; }; @@ -131,7 +131,7 @@ 955E73382623568F005652FF /* Home.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Home.swift; sourceTree = "<group>"; }; 955E733B262356F3005652FF /* HomeResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeResponse.swift; sourceTree = "<group>"; }; 95602701265ABB440046F97E /* InsidersResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsidersResponse.swift; sourceTree = "<group>"; }; - 95602703265ABB990046F97E /* SummaryInsiderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SummaryInsiderModel.swift; sourceTree = "<group>"; }; + 95602703265ABB990046F97E /* InsiderRosterModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsiderRosterModel.swift; sourceTree = "<group>"; }; 95602705265ABC660046F97E /* Insiders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Insiders.swift; sourceTree = "<group>"; }; 95613AD8264FC5A900D4CE8F /* Company.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Company.swift; sourceTree = "<group>"; }; 95613ADC264FC6A200D4CE8F /* ChartResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartResponse.swift; sourceTree = "<group>"; }; @@ -299,7 +299,7 @@ 95A07F7526305AE3009865AA /* TradingDatesModel.swift */, 95613ADE264FC6FD00D4CE8F /* LatestNewsModel.swift */, 9594F03F2651355B00CFA8D4 /* HistoricalPricesModel.swift */, - 95602703265ABB990046F97E /* SummaryInsiderModel.swift */, + 95602703265ABB990046F97E /* InsiderRosterModel.swift */, ); path = "Global Models"; sourceTree = "<group>"; @@ -669,7 +669,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 95602704265ABB990046F97E /* SummaryInsiderModel.swift in Sources */, + 95602704265ABB990046F97E /* InsiderRosterModel.swift in Sources */, 950C36E3260FB6180081CF53 /* HapticsManager.swift in Sources */, 95FBE0DC2619CA7200440386 /* ProfileView.swift in Sources */, 95A5188626186F590002D27C /* PriceView.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/Global Models/InsiderRosterModel.swift Sun Jun 06 13:11:41 2021 +0200 @@ -0,0 +1,14 @@ +// +// InsiderRosterModel.swift +// LazyBear +// +// Created by Dennis Concepción Martín on 23/5/21. +// + +import SwiftUI + +struct InsiderRosterModel: Codable, Hashable { + var entityName: String + var position: Int + var reportDate: Int +}
--- a/LazyBear/Global Models/SummaryInsiderModel.swift Sat Jun 05 19:05:13 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -// -// SummaryInsiderModel.swift -// LazyBear -// -// Created by Dennis Concepción Martín on 23/5/21. -// - -import SwiftUI - -struct SummaryInsiderModel: Codable { - var date: Int - var fullName: String - var netTransacted: Int - var reportedTitle: String - var totalBought: Int - var totalSold: Int - var updated: Int -}
--- a/LazyBear/Global functions/ConvertEpoch.swift Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear/Global functions/ConvertEpoch.swift Sun Jun 06 13:11:41 2021 +0200 @@ -7,14 +7,27 @@ import SwiftUI -// Convert Epoch time to human date -func convertEpoch(_ miliseconds: Int) -> String { +/* + Convert Epoch time (in miliseconds) to human readable date + */ +func convertEpoch(_ miliseconds: Int, _ interval: Bool) -> String { let now = Date() // Current date + // TimeInterval() function must be in seconds, not in miliseconds - let articlePublished = Date(timeIntervalSince1970: TimeInterval(miliseconds/1000)) + let convertedDate = Date(timeIntervalSince1970: TimeInterval(miliseconds/1000)) + let formatter = DateComponentsFormatter() formatter.unitsStyle = .abbreviated - let humanDate = formatter.string(from: articlePublished, to: now)! + + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + + var humanDate = String() + if interval { + humanDate = formatter.string(from: convertedDate, to: now)! + } else { + humanDate = dateFormatter.string(from: convertedDate) + } return humanDate }
--- a/LazyBear/Views/Company/Chart.swift Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear/Views/Company/Chart.swift Sun Jun 06 13:11:41 2021 +0200 @@ -60,9 +60,12 @@ ) if let latestNews = company.chartData.latestNews { - ForEach(latestNews, id: \.self) { new in - NewsRow(new: new) + VStack(spacing: 20) { + ForEach(latestNews, id: \.self) { new in + NewsRow(new: new) + } } + .padding(.top) } } .onAppear { self.timer = Timer.publish(every: 10, on: .main, in: .common).autoconnect() } // Start timer
--- a/LazyBear/Views/Company/CompanyView.swift Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear/Views/Company/CompanyView.swift Sun Jun 06 13:11:41 2021 +0200 @@ -16,26 +16,39 @@ // Views @State private var showChartView = true + @State private var showInsiderView = false var body: some View { ScrollView { VStack { CompanyHeader(symbol: symbol, showViewSelector: $showViewSelector) + .padding(.bottom) // Chart View if showChartView { Chart(company: company, symbol: symbol) + } else if showInsiderView { + Insiders(company: company, symbol: symbol) } } .padding() } .actionSheet(isPresented: $showViewSelector) { ActionSheet(title: Text("Select an option"), buttons: [ - .default(Text("Chart & News")) { showChartView = true }, + .default(Text("Chart & News")) { resetViews(); showChartView = true }, + .default(Text("Insiders")) { resetViews(); showInsiderView = true }, .cancel() ]) } } + + /* + Hide all views to show later the one tapped + */ + private func resetViews() { + showChartView = false + showInsiderView = false + } } struct CompanyView_Previews: PreviewProvider {
--- a/LazyBear/Views/Company/Helpers/InsiderRow.swift Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear/Views/Company/Helpers/InsiderRow.swift Sun Jun 06 13:11:41 2021 +0200 @@ -10,19 +10,29 @@ struct InsiderRow: View { var percentageOfWidth: CGFloat + var insiderRoster: InsiderRosterModel var body: some View { RowShape() - .frame(height: 120) + .frame(height: 105) .overlay( VStack(alignment: .leading) { - Text("Dennis Concepcion") - .font(.title3) - .fontWeight(.semibold) + Text(insiderRoster.entityName.capitalized) + .lineLimit(1) + .font(.headline) + + Text("Last updated: \(convertEpoch(insiderRoster.reportDate, false))") + .opacity(0.5) + .font(.subheadline) + + HStack { + Spacer() + Text("\(insiderRoster.position) shares owned") + .font(.caption) + .opacity(0.5) + } - Text("Random guy") CapsuleChartView(percentageOfWidth: percentageOfWidth) - .padding(.top) } .padding() ,alignment: .leading @@ -32,6 +42,14 @@ struct InsiderRow_Previews: PreviewProvider { static var previews: some View { - InsiderRow(percentageOfWidth: 0.6) + InsiderRow( + percentageOfWidth: 0.6, + insiderRoster: + InsiderRosterModel( + entityName: "Dennis Concepcion", + position: 1230, + reportDate: 1234567 + ) + ) } }
--- a/LazyBear/Views/Company/Helpers/NewsRow.swift Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear/Views/Company/Helpers/NewsRow.swift Sun Jun 06 13:11:41 2021 +0200 @@ -13,48 +13,38 @@ var body: some View { Button(action: { showingSafariView = true }) { - VStack(alignment: .leading) { - Text(new.source.uppercased()) - .font(.caption) - .opacity(0.5) - - Text(new.headline) - .font(.headline) - - Text(new.summary) - .opacity(0.5) - .font(.subheadline) - .lineLimit(1) - .padding(.bottom, 5) - - let humanDate = convertDate() - Text("\(humanDate) ago") - .font(.caption2) - .opacity(0.5) - - Divider() - } + RowShape() + .frame(height: 140) + .overlay( + VStack(alignment: .leading) { + Text(new.source.uppercased()) + .font(.caption) + .opacity(0.5) + + Text(new.headline) + .font(.headline) + + Text(new.summary) + .opacity(0.5) + .font(.subheadline) + .lineLimit(1) + .padding(.bottom, 5) + + let humanDate = convertEpoch(new.datetime, true) + Text("\(humanDate) ago") + .font(.caption2) + .opacity(0.5) + + } + .padding() + ,alignment: .leading + ) } .buttonStyle(PlainButtonStyle()) .sheet(isPresented: $showingSafariView) { SFSafariViewWrapper(url: URL(string: new.url)!) } } - - /* - Convert Epoch time to human readable - */ - private func convertDate() -> String { - let now = Date() // Current date - // Time when the article was published. Divide new.datetime by 1,000 because - // TimeInterval() function must be in seconds, not in miliseconds - let articlePublished = Date(timeIntervalSince1970: TimeInterval(new.datetime)/1000) - let formatter = DateComponentsFormatter() - formatter.unitsStyle = .full - let humanDate = formatter.string(from: articlePublished, to: now)! - - return humanDate - } } struct NewsRow_Previews: PreviewProvider {
--- a/LazyBear/Views/Company/Insiders.swift Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear/Views/Company/Insiders.swift Sun Jun 06 13:11:41 2021 +0200 @@ -13,11 +13,37 @@ var body: some View { if company.showInsidersView { - + VStack(alignment: .leading) { + HStack { + Text("Top net buyers") + .font(.title3) + .fontWeight(.semibold) + + Spacer() + Button("See all", action: { }) + } + + if let insiderSummer = company.insidersData.insiderRoster { + + // Get total shares owned by the top 10 insiders + let totalPositions = insiderSummer.map { $0.position }.reduce(0, +) + VStack(alignment: .leading, spacing: 20) { + ForEach(insiderSummer.prefix(10), id: \.self) { insider in + + // Compute percentage of ownership for each insider + let percentage = Double(insider.position) / Double(totalPositions) + + InsiderRow(percentageOfWidth: CGFloat(percentage), insiderRoster: insider) + .onAppear { print(percentage) } + } + } + } + } } else { ProgressView() .onAppear { - // request API + let url = "https://api.lazybear.app/company/insiders/symbol=\(symbol)" + company.request(url, .initial, "insider") } } }
--- a/LazyBear/Views/Company/Networking/Company.swift Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear/Views/Company/Networking/Company.swift Sun Jun 06 13:11:41 2021 +0200 @@ -33,6 +33,8 @@ } else if view == "insider" { bazooka.request(url: url, model: InsidersResponse.self) { response in self.insidersData = response + + self.showInsidersView = true } } }
--- a/LazyBear/Views/Company/Networking/InsidersResponse.swift Sat Jun 05 19:05:13 2021 +0200 +++ b/LazyBear/Views/Company/Networking/InsidersResponse.swift Sun Jun 06 13:11:41 2021 +0200 @@ -8,9 +8,9 @@ import SwiftUI struct InsidersResponse: Codable { - var insiderSummary: [SummaryInsiderModel]? + var insiderRoster: [InsiderRosterModel]? private enum CodingKeys: String, CodingKey { - case insiderSummary = "insider_summary" + case insiderRoster = "insider_roster" } }