Skip to content

Commit f08383c

Browse files
Merge pull request #195 from TelemetryDeck/OrganisationSwitcher
OrgSwitcher
2 parents 9d8984f + 7305a1e commit f08383c

File tree

8 files changed

+188
-90
lines changed

8 files changed

+188
-90
lines changed

APIClient/APIClient.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ final class APIClient: ObservableObject {
5454
}
5555
}
5656

57+
@AppStorage("currentOrganisationID") var _currentOrganisationID: String?
58+
5759
@Published var registrationStatus: RegistrationStatus?
5860

5961
@Published var userToken: UserTokenDTO? {
@@ -387,6 +389,9 @@ extension APIClient {
387389
request.httpMethod = httpMethod
388390
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
389391
request.setValue(userToken?.bearerTokenAuthString, forHTTPHeaderField: "Authorization")
392+
if let currentOrgID = _currentOrganisationID {
393+
request.setValue(currentOrgID, forHTTPHeaderField: "td-organization-id")
394+
}
390395

391396
if let httpBody = httpBody {
392397
request.httpBody = httpBody

APIClient/DTOs/OrganizationInfo.swift

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// OrganizationInfo.swift
3+
// Telemetry Viewer
4+
//
5+
// Created by Lukas on 29.05.24.
6+
//
7+
8+
import Foundation
9+
import SwiftUI
10+
11+
struct OrganizationInfo: Codable, Equatable {
12+
var id: UUID
13+
var name: String
14+
var stripeCustomerID: String?
15+
var stripeMaxSignals: Double?
16+
var maxSignalsMultiplier: Double?
17+
var resolvedMaxSignals: Int64
18+
var isInRestrictedMode: Bool
19+
var countryCode: String?
20+
var referralCode: String
21+
var usagePercentage: Double?
22+
var isSuperOrg: Bool
23+
var apps: [AppInfo]
24+
var basePermissions: AppAccessLevel
25+
var roleOrganizationPermissions: AppAccessLevel?
26+
27+
var appIDs: [UUID] {
28+
apps.map { app in
29+
app.id
30+
}
31+
}
32+
33+
}
34+
35+
public enum AppAccessLevel: String, Codable, Comparable {
36+
case none
37+
case read
38+
case write
39+
case administrate
40+
41+
public static func < (lhs: AppAccessLevel, rhs: AppAccessLevel) -> Bool {
42+
switch lhs {
43+
case .none:
44+
switch rhs {
45+
case .none:
46+
false
47+
case .read:
48+
true
49+
case .write:
50+
true
51+
case .administrate:
52+
true
53+
}
54+
case .read:
55+
switch rhs {
56+
case .none:
57+
false
58+
case .read:
59+
false
60+
case .write:
61+
true
62+
case .administrate:
63+
true
64+
}
65+
case .write:
66+
switch rhs {
67+
case .none:
68+
false
69+
case .read:
70+
false
71+
case .write:
72+
false
73+
case .administrate:
74+
true
75+
}
76+
case .administrate:
77+
false
78+
}
79+
}
80+
}

Services/AppService.swift

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -75,42 +75,4 @@ class AppService: ObservableObject {
7575
}
7676
}
7777
}
78-
79-
func create(appNamed name: String, callback: ((Result<AppInfo, TransferError>) -> Void)? = nil) {
80-
let url = api.urlForPath(apiVersion: .v3, "apps")
81-
82-
api.post(["name": name], to: url) { [unowned self] (result: Result<AppInfo, TransferError>) in
83-
84-
if let app = try? result.get() {
85-
appDictionary[app.id] = app
86-
orgService.organization?.appIDs.append(app.id)
87-
}
88-
89-
callback?(result)
90-
}
91-
}
92-
93-
func update(appID: UUID, newName: String, callback: ((Result<AppInfo, TransferError>) -> Void)? = nil) {
94-
let url = api.urlForPath(apiVersion: .v3, "apps", appID.uuidString)
95-
96-
api.patch(["name": newName], to: url) { [unowned self] (result: Result<AppInfo, TransferError>) in
97-
98-
if let app = try? result.get() {
99-
appDictionary[app.id] = app
100-
}
101-
102-
callback?(result)
103-
}
104-
}
105-
106-
func delete(appID: UUID, callback: ((Result<[String: String], TransferError>) -> Void)? = nil) {
107-
let url = api.urlForPath(apiVersion: .v2, "apps", appID.uuidString)
108-
109-
api.delete(url) { [unowned self] (result: Result<[String: String], TransferError>) in
110-
111-
appDictionary[appID] = nil
112-
orgService.organization?.appIDs = (orgService.organization?.appIDs.filter { $0 != appID })!
113-
callback?(result)
114-
}
115-
}
11678
}

Services/OrgService.swift

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,7 @@ class OrgService: ObservableObject {
2222
self.errorService = errors
2323
}
2424

25-
// Wasn't getting called before -> Never leaving loading state.
26-
// For now called when retrieveOrganisations also called
27-
// Maybe move getCode into the retrieve Func?
2825
func getOrganisation() {
29-
let locallyCachedOrganization = retrieveFromDisk()
30-
self.organization = locallyCachedOrganization
31-
3226
self.loadingState = .loading
3327

3428
Task {
@@ -57,8 +51,6 @@ class OrgService: ObservableObject {
5751
switch result {
5852
case let .success(org):
5953

60-
self.saveToDisk(org: org)
61-
6254
continuation.resume(returning: org)
6355

6456
case let .failure(error):
@@ -68,32 +60,10 @@ class OrgService: ObservableObject {
6860
}
6961
}
7062
}
71-
}
72-
73-
/// this is interesting, do we want this for more than the org?
74-
private extension OrgService {
75-
var organizationCacheFilePath: URL {
76-
let fileManager = FileManager.default
77-
let urls = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)
78-
let cachesDirectoryUrl = urls[0]
79-
let fileUrl = cachesDirectoryUrl.appendingPathComponent("telemetrydeck.organization.json")
80-
let filePath = fileUrl.path
81-
82-
if !fileManager.fileExists(atPath: filePath) {
83-
let contents = Data()
84-
fileManager.createFile(atPath: filePath, contents: contents)
85-
}
8663

87-
return fileUrl
88-
}
89-
90-
func saveToDisk(org: DTOv2.Organization) {
91-
guard let data = try? JSONEncoder.telemetryEncoder.encode(org) else { return }
92-
try? data.write(to: self.organizationCacheFilePath, options: .atomic)
93-
}
94-
95-
func retrieveFromDisk() -> DTOv2.Organization? {
96-
guard let data = try? Data(contentsOf: organizationCacheFilePath) else { return nil }
97-
return try? JSONDecoder.telemetryDecoder.decode(DTOv2.Organization.self, from: data)
64+
func allOrganizations() async throws -> [OrganizationInfo]{
65+
let url = api.urlForPath(apiVersion: .v3, "organizations")
66+
return try await api.get(url: url)
9867
}
9968
}
69+

Shared/Empty Status Views/NoAppSelectedView.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@ struct NoAppSelectedView: View {
2222
Text("To start, create your first App. You can use that App's unique identifier to send signals from your code.")
2323
.foregroundColor(.grayColor)
2424
VStack {
25-
Button("Create First App") {
26-
appService.create(appNamed: "New App")
27-
}
28-
.buttonStyle(SmallPrimaryButtonStyle())
29-
3025
Button("Documentation: Sending Signals") {
3126
#if os(macOS)
3227
NSWorkspace.shared.open(URL(string: "https://telemetrydeck.com/pages/quickstart.html")!)

Shared/Navigational Structure/LeftSidebarView.swift

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,35 @@ struct LeftSidebarView: View {
3636
case editApp(app: UUID)
3737
}
3838

39+
func getApps(organization: DTOv2.Organization?) {
40+
Task {
41+
for appID in organization?.appIDs ?? [] {
42+
if let app = try? await appService.retrieveApp(withID: appID) {
43+
DispatchQueue.main.async {
44+
app.insightGroupIDs.forEach { groupID in
45+
if !(groupService.groupsDictionary.keys.contains(groupID)) {
46+
groupService.retrieveGroup(with: groupID)
47+
}
48+
}
49+
appService.appDictionary[app.id] = app
50+
}
51+
}
52+
}
53+
}
54+
}
55+
3956
var body: some View {
4057
List {
4158
Section {
4259
if let organization = orgService.organization {
4360
ForEach(organization.appIDs, id: \.self) { appID in
4461
section(for: appID)
4562
}
63+
.onChange(of: orgService.organization) {
64+
getApps(organization: orgService.organization)
65+
}
4666
.task {
47-
for appID in organization.appIDs {
48-
if let app = try? await appService.retrieveApp(withID: appID) {
49-
DispatchQueue.main.async {
50-
app.insightGroupIDs.forEach { groupID in
51-
if !(groupService.groupsDictionary.keys.contains(groupID)) {
52-
groupService.retrieveGroup(with: groupID)
53-
}
54-
}
55-
appService.appDictionary[app.id] = app
56-
}
57-
}
58-
}
67+
getApps(organization: orgService.organization)
5968
}
6069
}
6170

@@ -64,7 +73,7 @@ struct LeftSidebarView: View {
6473
}
6574

6675
Section {
67-
LoadingStateIndicator(loadingState: orgService.loadingState, title: orgService.organization?.name)
76+
OrganisationSwitcher()
6877

6978
#if os(iOS)
7079
Button {

Telemetry Viewer.xcodeproj/project.pbxproj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@
163163
80427FF62BFE2AF4007E89CC /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80427FDF2BFE2AF4007E89CC /* UserInfo.swift */; };
164164
80427FF72BFE2AF4007E89CC /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80427FDF2BFE2AF4007E89CC /* UserInfo.swift */; };
165165
80427FF82BFE2AF4007E89CC /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80427FDF2BFE2AF4007E89CC /* UserInfo.swift */; };
166+
804385CF2C073763004E3285 /* OrganizationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804385CE2C073763004E3285 /* OrganizationInfo.swift */; };
167+
804385D02C073763004E3285 /* OrganizationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804385CE2C073763004E3285 /* OrganizationInfo.swift */; };
168+
804385D12C073763004E3285 /* OrganizationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804385CE2C073763004E3285 /* OrganizationInfo.swift */; };
169+
804385D22C073763004E3285 /* OrganizationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804385CE2C073763004E3285 /* OrganizationInfo.swift */; };
170+
804385D32C073763004E3285 /* OrganizationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804385CE2C073763004E3285 /* OrganizationInfo.swift */; };
171+
804385D42C073763004E3285 /* OrganizationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804385CE2C073763004E3285 /* OrganizationInfo.swift */; };
172+
804385D62C0739D0004E3285 /* OrganisationSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804385D52C0739D0004E3285 /* OrganisationSwitcher.swift */; };
166173
8083DD692C05C9C300596926 /* ClusterPieChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8083DD682C05C9C300596926 /* ClusterPieChart.swift */; };
167174
8083DD6B2C05C9FE00596926 /* PieChartTopN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8083DD6A2C05C9FE00596926 /* PieChartTopN.swift */; };
168175
8083DD6D2C05EA0100596926 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8083DD6C2C05EA0100596926 /* Extensions.swift */; };
@@ -516,6 +523,8 @@
516523
80427FDD2BFE2AF4007E89CC /* InsightGroupInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsightGroupInfo.swift; sourceTree = "<group>"; };
517524
80427FDE2BFE2AF4007E89CC /* InsightInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsightInfo.swift; sourceTree = "<group>"; };
518525
80427FDF2BFE2AF4007E89CC /* UserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfo.swift; sourceTree = "<group>"; };
526+
804385CE2C073763004E3285 /* OrganizationInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganizationInfo.swift; sourceTree = "<group>"; };
527+
804385D52C0739D0004E3285 /* OrganisationSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganisationSwitcher.swift; sourceTree = "<group>"; };
519528
8083DD682C05C9C300596926 /* ClusterPieChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClusterPieChart.swift; sourceTree = "<group>"; };
520529
8083DD6A2C05C9FE00596926 /* PieChartTopN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PieChartTopN.swift; sourceTree = "<group>"; };
521530
8083DD6C2C05EA0100596926 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
@@ -855,6 +864,7 @@
855864
80427FDD2BFE2AF4007E89CC /* InsightGroupInfo.swift */,
856865
80427FDE2BFE2AF4007E89CC /* InsightInfo.swift */,
857866
80427FDF2BFE2AF4007E89CC /* UserInfo.swift */,
867+
804385CE2C073763004E3285 /* OrganizationInfo.swift */,
858868
);
859869
path = DTOs;
860870
sourceTree = "<group>";
@@ -1142,6 +1152,7 @@
11421152
DC9A92AF255C1CD100E92C89 /* AutoCompletingTextField.swift */,
11431153
2B21FCD926FDC9F900A8A55B /* InsightsGrid.swift */,
11441154
DCDC6EFE253EDA4C0012D9A7 /* FilterEditView.swift */,
1155+
804385D52C0739D0004E3285 /* OrganisationSwitcher.swift */,
11451156
2B21FCDC26FDCA6A00A8A55B /* GroupView.swift */,
11461157
2B21FCD426FDC33F00A8A55B /* InsightGroupsView.swift */,
11471158
DCE239FC24D3687C00053370 /* Telemetry_ViewerApp_iOS.swift */,
@@ -1551,6 +1562,7 @@
15511562
C51CB78B27566279005A3FB9 /* InsightService.swift in Sources */,
15521563
C5F4D37028ACF64700EBB667 /* ChartDataSet.swift in Sources */,
15531564
80427FEC2BFE2AF4007E89CC /* InsightGroupInfo.swift in Sources */,
1565+
804385D42C073763004E3285 /* OrganizationInfo.swift in Sources */,
15541566
C51CB78927566275005A3FB9 /* ErrorService.swift in Sources */,
15551567
C51CB7922756646E005A3FB9 /* TelemetrySignalTypes.swift in Sources */,
15561568
C5F4D37128ACF64700EBB667 /* ChartDataPoint.swift in Sources */,
@@ -1613,6 +1625,7 @@
16131625
C5F4D33528ACF48000EBB667 /* LineChartView.swift in Sources */,
16141626
C51CB78527566258005A3FB9 /* ConditionalViewModifier.swift in Sources */,
16151627
C5F4D33D28ACF48000EBB667 /* RawChartView.swift in Sources */,
1628+
804385D22C073763004E3285 /* OrganizationInfo.swift in Sources */,
16161629
C51CB77F27566243005A3FB9 /* Color.swift in Sources */,
16171630
C5F4D33128ACF48000EBB667 /* ChartBottomView.swift in Sources */,
16181631
C51CB7912756646D005A3FB9 /* TelemetrySignalTypes.swift in Sources */,
@@ -1675,6 +1688,7 @@
16751688
C5F4D33428ACF48000EBB667 /* LineChartView.swift in Sources */,
16761689
C5CE3D57271AFCE2005232EC /* Buttonstyles.swift in Sources */,
16771690
C5F4D33C28ACF48000EBB667 /* RawChartView.swift in Sources */,
1691+
804385D12C073763004E3285 /* OrganizationInfo.swift in Sources */,
16781692
2B6431A72739A5BB009A33C4 /* AsyncOperation.swift in Sources */,
16791693
C5F4D33028ACF48000EBB667 /* ChartBottomView.swift in Sources */,
16801694
C581F4E0271B22FD0031E99C /* Color+Hex.swift in Sources */,
@@ -1699,6 +1713,7 @@
16991713
2B64319C2739A5BB009A33C4 /* Data+JSONPrettyPrint.swift in Sources */,
17001714
C5F4D36E28ACF64600EBB667 /* ChartDataSet.swift in Sources */,
17011715
80427FEB2BFE2AF4007E89CC /* InsightGroupInfo.swift in Sources */,
1716+
804385D32C073763004E3285 /* OrganizationInfo.swift in Sources */,
17021717
2B6431AC2739A5BB009A33C4 /* Caching.swift in Sources */,
17031718
C51CB78F27566296005A3FB9 /* ConditionalViewModifier.swift in Sources */,
17041719
C5F4D36F28ACF64600EBB667 /* ChartDataPoint.swift in Sources */,
@@ -1721,6 +1736,7 @@
17211736
C5F4D35628ACF48000EBB667 /* ChartDataSet.swift in Sources */,
17221737
C5F4D33E28ACF48000EBB667 /* RoundedCorners.swift in Sources */,
17231738
2B21FCD526FDC33F00A8A55B /* InsightGroupsView.swift in Sources */,
1739+
804385D62C0739D0004E3285 /* OrganisationSwitcher.swift in Sources */,
17241740
DCB02B722502781A00304964 /* LoginView.swift in Sources */,
17251741
2B379D2126FBC3A300714BE6 /* IconFinderService.swift in Sources */,
17261742
C5CD589D2810368400671359 /* ThreeCirclesInATrenchcode.swift in Sources */,
@@ -1743,6 +1759,7 @@
17431759
2B781B8326F4A5E30062DBDC /* StatusMessageBanner.swift in Sources */,
17441760
DCE23A0F24D3687D00053370 /* Telemetry_ViewerApp_iOS.swift in Sources */,
17451761
DCF7CD40254A08B900BFA23B /* LexiconView.swift in Sources */,
1762+
804385CF2C073763004E3285 /* OrganizationInfo.swift in Sources */,
17461763
80AD3E9E2BFF305100BBD7EB /* ClusterBarChart.swift in Sources */,
17471764
2B3CC0DC264D4AFE0038B528 /* LexiconService.swift in Sources */,
17481765
80AD3EA52BFF33FF00BBD7EB /* LineChartTimeSeries.swift in Sources */,
@@ -1928,6 +1945,7 @@
19281945
2B46280F27286D2500515530 /* TestingModeToggle.swift in Sources */,
19291946
2B1D469926CC4C5D008814A9 /* ErrorService.swift in Sources */,
19301947
2B61B94F264C08D4003F62C4 /* SignalsService.swift in Sources */,
1948+
804385D02C073763004E3285 /* OrganizationInfo.swift in Sources */,
19311949
C5F4D33F28ACF48000EBB667 /* RoundedCorners.swift in Sources */,
19321950
DC627EF025A35B6A00C1DF33 /* EmptyAppView.swift in Sources */,
19331951
6351A78A277C9EDA003AF559 /* InsightDisplayMode+Extensions.swift in Sources */,

0 commit comments

Comments
 (0)