Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ad6085b
Merge pull request #2 from mormaer/master
tht7 Jun 23, 2023
beca6e1
- changed all the NavigationLinks to the new iOS 16 style "value" ori…
Jun 23, 2023
b991e4e
- found some more places that need updating to the new value based Na…
Jun 23, 2023
d96a32d
- made APIPostView.id truly unique, that way when we update anything …
Jun 23, 2023
26dd81c
Merge branch 'mormaer:master' into master-master
tht7 Jun 23, 2023
79fd314
Merge remote-tracking branch 'origin/master-master' into open-links-i…
Jun 23, 2023
1686eaa
Merge branch 'mormaer:master' into master-master
tht7 Jun 23, 2023
24125ad
Merge remote-tracking branch 'origin/master-master' into open-links-i…
Jun 23, 2023
d11bdbe
- missed a file during merge
Jun 23, 2023
12e68c6
- added a little toast message while resolving the link
Jun 24, 2023
8b6895e
- added a new place to save changing account preference
Jun 24, 2023
57d68e3
Some bugfixes
Jun 24, 2023
b120413
Merge branch 'open-links-in-app' into faceId
Jun 24, 2023
01717dc
Merge branch 'mormaer:master' into master-master
tht7 Jun 26, 2023
1fa1a5a
Merge remote-tracking branch 'origin/master-master' into faceId
Jun 26, 2023
df0efac
The Bio lock screen now uses iOS's thiccMaterial
Jun 26, 2023
bf8784e
Merge branch 'mormaer:master' into master-master
tht7 Jun 27, 2023
5700dac
created new CachedImageWithNsfwFilter that should be uniform in our a…
Jun 27, 2023
a79d696
Merge remote-tracking branch 'origin/better-ish-image-handling' into …
Jun 27, 2023
425a2d4
- removed commented test code
Jun 27, 2023
1a46ebe
Merge branch 'mormaer:master' into master-master
tht7 Jun 27, 2023
038ab8e
Merge remote-tracking branch 'origin/master-master' into better-ish-i…
Jun 27, 2023
b71e976
- merged the linter
Jun 27, 2023
de5d145
Merge remote-tracking branch 'origin/better-ish-image-handling' into …
Jun 27, 2023
c2adc8e
- merged the linter
Jun 27, 2023
45ed7d4
Merge branch 'mormaer:master' into master-master
tht7 Jun 28, 2023
9d5bd2d
Merge branch 'mormaer:master' into master-master
tht7 Jun 28, 2023
be2d0a7
Merge branch 'mormaer:master' into master-master
tht7 Jun 29, 2023
cf28b07
Merge remote-tracking branch 'origin/master-master' into faceId
Jun 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Mlem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@
6D8F08FF2A4029AE003EB4FD /* Community List View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D8F08FE2A4029AE003EB4FD /* Community List View.swift */; };
6D91D4552A415994006B8F9A /* CommunityListSidebarEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D91D4542A415994006B8F9A /* CommunityListSidebarEntry.swift */; };
6D91D4582A4159D8006B8F9A /* CommunityListRowViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D91D4572A4159D8006B8F9A /* CommunityListRowViews.swift */; };
B131BB9B2A4721D400951C92 /* View - BioLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B131BB9A2A4721D400951C92 /* View - BioLock.swift */; };
B131BB9D2A472CA800951C92 /* Account Preference.swift in Sources */ = {isa = PBXBuildFile; fileRef = B131BB9C2A472CA800951C92 /* Account Preference.swift */; };
B14E93C02A45CA3400D6DA93 /* Post Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E93BF2A45CA3400D6DA93 /* Post Link.swift */; };
B14E93C22A45D3B300D6DA93 /* Community Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E93C12A45D3B300D6DA93 /* Community Link.swift */; };
B14E93C52A470D5100D6DA93 /* AlertToast in Frameworks */ = {isa = PBXBuildFile; productRef = B14E93C42A470D5100D6DA93 /* AlertToast */; };
Expand Down Expand Up @@ -387,6 +389,8 @@
6D8F08FE2A4029AE003EB4FD /* Community List View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Community List View.swift"; sourceTree = "<group>"; };
6D91D4542A415994006B8F9A /* CommunityListSidebarEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommunityListSidebarEntry.swift; sourceTree = "<group>"; };
6D91D4572A4159D8006B8F9A /* CommunityListRowViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommunityListRowViews.swift; sourceTree = "<group>"; };
B131BB9A2A4721D400951C92 /* View - BioLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View - BioLock.swift"; sourceTree = "<group>"; };
B131BB9C2A472CA800951C92 /* Account Preference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account Preference.swift"; sourceTree = "<group>"; };
B14E93BF2A45CA3400D6DA93 /* Post Link.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Post Link.swift"; sourceTree = "<group>"; };
B14E93C12A45D3B300D6DA93 /* Community Link.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Community Link.swift"; sourceTree = "<group>"; };
B1A26FE02A44AAB200B91A32 /* Navigation getter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Navigation getter.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -515,6 +519,7 @@
63F0C7A52A05225100A18C5D /* Saved Account.swift */,
63CE4E722A06F5A100405271 /* Access Token.swift */,
63E5D3932A13CF3600EC1FBD /* Favorite Community.swift */,
B131BB9C2A472CA800951C92 /* Account Preference.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -606,6 +611,7 @@
CD69F56E2A41EDF50028D4F7 /* Swipey Actions.swift */,
B1A26FE02A44AAB200B91A32 /* Navigation getter.swift */,
B1A26FE22A45B11800B91A32 /* View - Handle Lemmy Links.swift */,
B131BB9A2A4721D400951C92 /* View - BioLock.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -1394,6 +1400,7 @@
63F0C7AD2A05235400A18C5D /* Encode for Saving.swift in Sources */,
63344C712A098060001BC616 /* Sidebar.swift in Sources */,
63E5D3822A13831800EC1FBD /* Community Search View.swift in Sources */,
B131BB9D2A472CA800951C92 /* Account Preference.swift in Sources */,
637218572A3A2AAD008C4816 /* APISiteView.swift in Sources */,
B14E93C02A45CA3400D6DA93 /* Post Link.swift in Sources */,
63AF452D2A2F37EB00E0266F /* Own Account Tracker.swift in Sources */,
Expand Down Expand Up @@ -1459,6 +1466,7 @@
632E8EE627EE63D3007E8D75 /* Upvote.swift in Sources */,
B1A26FE32A45B11800B91A32 /* View - Handle Lemmy Links.swift in Sources */,
6D405B052A43E82300C65F9C /* Sidebar Header.swift in Sources */,
B131BB9B2A4721D400951C92 /* View - BioLock.swift in Sources */,
63E5D38D2A13BCDE00EC1FBD /* Community Search Result Tracker.swift in Sources */,
6372184E2A3A2AAD008C4816 /* APIPersonView.swift in Sources */,
6363D5FA27EE1BDA00E34822 /* Settings View.swift in Sources */,
Expand Down Expand Up @@ -1668,6 +1676,7 @@
INFOPLIST_FILE = Mlem/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Mlem;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
INFOPLIST_KEY_NSFaceIDUsageDescription = "Use FaceID to lock your account";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand Down Expand Up @@ -1703,6 +1712,7 @@
INFOPLIST_FILE = Mlem/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Mlem;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
INFOPLIST_KEY_NSFaceIDUsageDescription = "Use FaceID to lock your account";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand Down
1 change: 1 addition & 0 deletions Mlem/App Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct AppConstants
// MARK: - Files
private static let applicationSupportDirectoryPath: URL = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
static let savedAccountsFilePath: URL = applicationSupportDirectoryPath.appendingPathComponent("Saved Accounts", conformingTo: .json)
static let savedAccountsPreferenceFilePath: URL = applicationSupportDirectoryPath.appendingPathComponent("Saved Account Preferance", conformingTo: .json)
static let filteredKeywordsFilePath: URL = applicationSupportDirectoryPath.appendingPathComponent("Blocked Keywords", conformingTo: .json)
static let favoriteCommunitiesFilePath: URL = applicationSupportDirectoryPath.appendingPathComponent("Favorite Communities", conformingTo: .json)

Expand Down
2 changes: 2 additions & 0 deletions Mlem/App State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ class AppState: ObservableObject
@Published var toast: AlertToast?

@Published var criticalErrorType: CriticalError = .shittyInternet

@Published var locked: Bool = false
}
87 changes: 87 additions & 0 deletions Mlem/Extensions/View - BioLock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// View - BioLock.swift
// Mlem
//
// Created by tht7 on 24/06/2023.
//

import Foundation
import SwiftUI
import LocalAuthentication


struct HandleAccountSecurity: ViewModifier {
@EnvironmentObject var accountsTracker: SavedAccountTracker
@EnvironmentObject var appState: AppState
let account: SavedAccount?

@State var context = LAContext()

func body(content: Content) -> some View {
if let account = account {
if accountsTracker.accountPreferences[account.id]?.requiresSecurity == true {
Comment on lines +20 to +21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else condition is idempotent, condense these into a single check

ZStack {
content
if appState.locked {
// security lock
Color.clear.background(.thickMaterial)
VStack {
Image(systemName: "lock")
Text("Locked- tap here to unlock")
}
.onTapGesture {
Task(priority: .userInitiated) {
await unlock()
}
}
}
}
} else {
content
}
} else {
content
}
}


func unlock() async {
var error: NSError?
let reason = "Unlock your account"
// Check for biometric authentication
// permissions
var permissions = context.canEvaluatePolicy(
.deviceOwnerAuthentication,
error: &error
)

if permissions {
do {
if try await context.evaluatePolicy(
// .deviceOwnerAuthentication allows
// biometric or passcode authentication
.deviceOwnerAuthentication,
localizedReason: reason
) {
await MainActor.run {
withAnimation {
appState.locked = false
}
}
}
} catch {
print(String(describing: error))
}
}
else {
print(String(describing: error))
// Handle permission denied or error
}
}
}

extension View {
func handleAccountSecurity(account: SavedAccount?) -> some View {
modifier(HandleAccountSecurity(account: account))
}
}
6 changes: 6 additions & 0 deletions Mlem/Extensions/View - Handle Lemmy Links.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,23 @@ struct HandleLemmyLinksDisplay: ViewModifier {
.navigationDestination(for: APICommunityView.self) { context in
if let account = account {
CommunityView(account: account, community: context.community)
.handleAccountSecurity(account: account)
} else {
Text("You must be signed in to view this community")
}
}
.navigationDestination(for: APICommunity.self) { community in
if let account = account {
CommunityView(account: account, community: community)
.handleAccountSecurity(account: account)
} else {
Text("You must be signed in to view this community")
}
}
.navigationDestination(for: CommunityLinkWithContext.self) { context in
if let account = account {
CommunityView(account: account, community: context.community, feedType: context.feedType)
.handleAccountSecurity(account: account)
} else {
Text("You must be signed in to view this community")
}
Expand All @@ -44,6 +47,7 @@ struct HandleLemmyLinksDisplay: ViewModifier {
post: post,
feedType: .constant(.all)
)
.handleAccountSecurity(account: account)
} else {
Text("You must be signed in to view this post")
}
Expand All @@ -55,13 +59,15 @@ struct HandleLemmyLinksDisplay: ViewModifier {
post: post.post,
feedType: post.feedType
).environmentObject(post.postTracker)
.handleAccountSecurity(account: account)
} else {
Text("You must be signed in to view this post")
}
}
.navigationDestination(for: APIPerson.self) { user in
if let account = account {
UserView(userID: user.id, account: account)
.handleAccountSecurity(account: account)
} else {
Text("You must be signed in to view this user")
}
Expand Down
4 changes: 3 additions & 1 deletion Mlem/Logic/File Management/Decode Data from File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal enum DecodingError: Error

internal enum WhatToDecode
{
case accounts, filteredKeywords, favoriteCommunities
case accounts, filteredKeywords, favoriteCommunities, accountPreferences
}

func decodeFromFile(fromURL: URL, whatToDecode: WhatToDecode) throws -> any Codable
Expand All @@ -32,6 +32,8 @@ func decodeFromFile(fromURL: URL, whatToDecode: WhatToDecode) throws -> any Coda
return try JSONDecoder().decode([String].self, from: rawData)
case .favoriteCommunities:
return try JSONDecoder().decode([FavoriteCommunity].self, from: rawData)
case .accountPreferences:
return try JSONDecoder().decode([Int: AccountPreference].self, from: rawData)
}
}
catch let decodingError
Expand Down
51 changes: 50 additions & 1 deletion Mlem/MlemApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,26 @@ struct MlemApp: App
print("Failed while encoding accounts to data: \(encodingError)")
}
}
.onChange(of: accountsTracker.accountPreferences) { newValue in
print("now saving account Preferences")
do
{
let encodedSavedAccounts: Data = try encodeForSaving(object: newValue)

do
{
try writeDataToFile(data: encodedSavedAccounts, fileURL: AppConstants.savedAccountsPreferenceFilePath)
}
catch let writingError
{
print("Failed while saving data to file: \(writingError)")
}
}
catch let encodingError
{
print("Failed while encoding accounts to data: \(encodingError)")
}
}
.onChange(of: filtersTracker.filteredKeywords)
{ newValue in
print("Change detected in filtered keywords: \(newValue)")
Expand Down Expand Up @@ -193,11 +213,40 @@ struct MlemApp: App
print("Failed while creating empty file: \(emptyFileCreationError)")
}
}


if FileManager.default.fileExists(atPath: AppConstants.savedAccountsPreferenceFilePath.path)
{
print("Favorite communities file exists, will attempt to load favorite communities")
do
{
accountsTracker.accountPreferences = try decodeFromFile(fromURL: AppConstants.savedAccountsPreferenceFilePath, whatToDecode: .accountPreferences) as! [Int: AccountPreference]
}
catch let accountPreferencesDecodingError
{
print("Failed while decoding account Preferences: \(accountPreferencesDecodingError)")
}
}
else
{
print("Account Preferences file does not exist, will try to create it")

do
{
try createEmptyFile(at: AppConstants.savedAccountsPreferenceFilePath)
}
catch let emptyFileCreationError
{
print("Failed while creating empty file: \(emptyFileCreationError)")
}
}

// set app theme to user preference
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
windowScene?.windows.first?.overrideUserInterfaceStyle = lightOrDarkMode
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
appState.locked = true
}
}
}
}
22 changes: 22 additions & 0 deletions Mlem/Models/Account Preference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Account Preference.swift
// Mlem
//
// Created by tht7 on 24/06/2023.
//

import Foundation
import SwiftUI

struct AccountPreference: Decodable, Identifiable, Hashable, Equatable, Encodable {
var id: Int {
get { self.hashValue }
}

var requiresSecurity: Bool?

func hash(into hasher: inout Hasher) {
hasher.combine(requiresSecurity)
}

}
2 changes: 2 additions & 0 deletions Mlem/Models/Trackers/Saved Account Tracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ import Foundation
class SavedAccountTracker: ObservableObject
{
@Published var savedAccounts: [SavedAccount] = .init()

@Published var accountPreferences: [Int: AccountPreference] = .init()
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct CommunityListView: View
{
let account: SavedAccount
@EnvironmentObject var favoritedCommunitiesTracker: FavoriteCommunitiesTracker
@EnvironmentObject var accountsTracker: SavedAccountTracker

@State var subscribedCommunities = [APICommunity]()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ struct AccountsPage: View

// now we reset the account
appState.currentActiveAccount = nil


appState.locked = true

if shouldDisplayFirstUser, let firstAccount = accountsTracker.savedAccounts.first {
// I know this looks super odd but it give SwiftUI just a bit of time to get ahold of itself
Task {
Expand All @@ -85,8 +87,13 @@ struct AccountsPage: View
.navigationDestination(for: SavedAccount.self) { account in
CommunityListView(account: account)
.onAppear {
let shouldUnlock = !(accountsTracker.accountPreferences[account.id]?.requiresSecurity ?? false)
if appState.locked && shouldUnlock {
appState.locked = false
}
appState.currentActiveAccount = account
}
.handleAccountSecurity(account: account)
}
.navigationTitle("Accounts")
.navigationBarTitleDisplayMode(.inline)
Expand Down Expand Up @@ -137,6 +144,7 @@ struct AccountsPage: View
let savedAccountToRemove: SavedAccount = accountsTracker.savedAccounts[index]

accountsTracker.savedAccounts.remove(at: index)
accountsTracker.accountPreferences.removeValue(forKey: savedAccountToRemove.id)

// MARK: - Purge the account information from the Keychain

Expand Down
Loading