diff --git a/Sources/swiftarr/Controllers/AlertController.swift b/Sources/swiftarr/Controllers/AlertController.swift index eefa5cecd..6852c4574 100644 --- a/Sources/swiftarr/Controllers/AlertController.swift +++ b/Sources/swiftarr/Controllers/AlertController.swift @@ -167,7 +167,12 @@ struct AlertController: APIRouteCollection { } else { entry.forumMentionCount = value.int ?? 0 - entry.newForumMentionCount = max(0, entry.forumMentionCount - viewedCount) + // @TODO disabling newForumMentionCount until we can integrate smarts along the lines of + // "If we're marking this post as read and it contains an alertword, then increment the + // _viewed key for that word." + // Otherwise we can never clear the "new" alertword notifications. + // entry.newForumMentionCount = max(0, entry.forumMentionCount - viewedCount) + entry.newForumMentionCount = 0 } resultDict[word] = entry } diff --git a/Sources/swiftarr/Controllers/UserController.swift b/Sources/swiftarr/Controllers/UserController.swift index f88e79215..cc53b1d64 100644 --- a/Sources/swiftarr/Controllers/UserController.swift +++ b/Sources/swiftarr/Controllers/UserController.swift @@ -590,6 +590,7 @@ struct UserController: APIRouteCollection { try await AlertWord.query(on: req.db).filter(\.$word == cleanParam).first() ?? AlertWord(cleanParam) try await alertword.save(on: req.db) try await AlertWordPivot(alertword: alertword, userID: cacheUser.userID).save(on: req.db) + try await req.redis.addAlertword(parameter, userID: cacheUser.userID) let keywordPivots = try await AlertWordPivot.query(on: req.db).filter(\.$user.$id == cacheUser.userID) .with(\.$alertword).all() let keywords = keywordPivots.map { $0.alertword.word } @@ -632,6 +633,7 @@ struct UserController: APIRouteCollection { try await alertWord.delete(on: req.db) try await req.redis.removeAlertword(parameter) } + try await req.redis.removeUserAlertword(parameter, userID: user.userID); } let keywords = pivots.map { $0.alertword.word } return KeywordData(keywords: keywords.sorted()) diff --git a/Sources/swiftarr/Helpers/RedisWrapper.swift b/Sources/swiftarr/Helpers/RedisWrapper.swift index 1419a64ad..6ba8f7255 100644 --- a/Sources/swiftarr/Helpers/RedisWrapper.swift +++ b/Sources/swiftarr/Helpers/RedisWrapper.swift @@ -137,6 +137,10 @@ extension Request.Redis { static let alertwordsRedisKey = RedisKey("alertwords") + func getAlertwordUsersRedisKey(_ word: String) -> RedisKey { + return RedisKey("alertwordUsers-\(word)") + } + // Gets all alertwords set by all users. Used to perform a set intersection against the words in a new post or twarrt. func getAllAlertwords() async throws -> Set { let alertWordArray = try await smembers(of: Request.Redis.alertwordsRedisKey).get() @@ -144,17 +148,26 @@ extension Request.Redis { return alertSet } - func addAlertword(_ word: String) async throws { + func addAlertword(_ word: String, userID: UUID) async throws { let _ = try await sadd(word, to: Request.Redis.alertwordsRedisKey).get() + let _ = try await sadd(userID, to: getAlertwordUsersRedisKey(word)).get() } + // Delete an alertword. This should only be called when there are no more users who have + // this word in their list. func removeAlertword(_ word: String) async throws { let _ = try await srem(word, from: Request.Redis.alertwordsRedisKey).get() } + // Delete an alertword for a particular user. + func removeUserAlertword(_ word: String, userID: UUID) async throws { + let _ = try await srem(userID, from: getAlertwordUsersRedisKey(word)).get() + let _ = try await hdel("alertwordPost-\(word)", from: userHashRedisKey(userID: userID)).get() + } + // Gets a list of users that are alerting on a particular alertword func getUsersForAlertword(_ word: String) async throws -> [UUID] { - let userIDs = try await smembers(of: "alertwordUsers-\(word)", as: UUID.self).get().compactMap { $0 } + let userIDs = try await smembers(of: getAlertwordUsersRedisKey(word), as: UUID.self).get().compactMap { $0 } return userIDs } diff --git a/Sources/swiftarr/Image/Image.swift b/Sources/swiftarr/Image/Image.swift index fe2acd124..7a95a8985 100644 --- a/Sources/swiftarr/Image/Image.swift +++ b/Sources/swiftarr/Image/Image.swift @@ -356,9 +356,8 @@ public class GDImage { extension GDImage { public convenience init?(url: URL) { let inputFile = fopen(url.path, "rb") - defer { fclose(inputFile) } - guard inputFile != nil else { return nil } + defer { fclose(inputFile!) } let loadedImage: gdImagePtr? @@ -390,7 +389,7 @@ extension GDImage { // open our output file, then defer it to close let outputFile = fopen(url.path, "wb") - defer { fclose(outputFile) } + defer { fclose(outputFile!) } // write the correct output format based on the path extension switch fileType {