Fixed a few bugs uncovered with further testing
Added some more logs to libSession build script and tweaked the stdout location Added shadow threads to the GarbageCollectionJob Changed the seed node retries to 2 because it's likely we will swap to another seed node pretty quickly which could resolve the issue Fixed a bug where the user could get kicked from a draft conversation if they get a contacts update before sending a message Fixed a bug where message status or media message download statuses would trigger the conversation to jump to the bottom
This commit is contained in:
parent
6685dc0572
commit
977c2051ed
|
@ -25,19 +25,32 @@
|
|||
# Need to set the path or we won't find cmake
|
||||
PATH=${PATH}:/usr/local/bin:/opt/homebrew/bin:/sbin/md5
|
||||
|
||||
# Direct the output to a log file
|
||||
exec > "${TARGET_BUILD_DIR}/libsession_util_output.log" 2>&1
|
||||
# Ensure the build directory exists (in case we need it before XCode creates it)
|
||||
mkdir -p "${TARGET_BUILD_DIR}"
|
||||
|
||||
# Remove any old build errors
|
||||
rm -rf "${TARGET_BUILD_DIR}/libsession_util_error.log"
|
||||
|
||||
# First ensure cmake is installed (store the error in a log and exit with a success status - xcode will output the error)
|
||||
echo "info: Validating build requirements"
|
||||
|
||||
if ! which cmake > /dev/null; then
|
||||
echo "error: cmake is required to build, please install (can install via homebrew with 'brew install cmake')." > "${TARGET_BUILD_DIR}/error.log"
|
||||
touch "${TARGET_BUILD_DIR}/libsession_util_error.log"
|
||||
echo "error: cmake is required to build, please install (can install via homebrew with 'brew install cmake')."
|
||||
echo "error: cmake is required to build, please install (can install via homebrew with 'brew install cmake')." > "${TARGET_BUILD_DIR}/libsession_util_error.log"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -d "${SRCROOT}/LibSession-Util" ] || [ ! -d "${SRCROOT}/LibSession-Util/src" ]; then
|
||||
touch "${TARGET_BUILD_DIR}/libsession_util_error.log"
|
||||
echo "error: Need to fetch LibSession-Util submodule."
|
||||
echo "error: Need to fetch LibSession-Util submodule." > "${TARGET_BUILD_DIR}/libsession_util_error.log"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate a hash of the libSession-util source files and check if they differ from the last hash
|
||||
echo "info: Checking for changes to source"
|
||||
|
||||
NEW_SOURCE_HASH=$(find "${SRCROOT}/LibSession-Util/src" -type f -exec md5 {} + | awk '{print $NF}' | sort | md5 | awk '{print $NF}')
|
||||
NEW_HEADER_HASH=$(find "${SRCROOT}/LibSession-Util/include" -type f -exec md5 {} + | awk '{print $NF}' | sort | md5 | awk '{print $NF}')
|
||||
|
||||
|
@ -68,7 +81,9 @@ if [ "${NEW_SOURCE_HASH}" != "${OLD_SOURCE_HASH}" ] || [ "${NEW_HEADER_HASH}" !=
|
|||
result=$(./utils/ios.sh "libsession-util" false)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "error: Failed to build libsession-util (See details in '${TARGET_BUILD_DIR}/pre-action-output.log')." > "${TARGET_BUILD_DIR}/error.log"
|
||||
touch "${TARGET_BUILD_DIR}/libsession_util_error.log"
|
||||
echo "error: Failed to build libsession-util (See details in '${TARGET_BUILD_DIR}/pre-action-output.log')."
|
||||
echo "error: Failed to build libsession-util (See details in '${TARGET_BUILD_DIR}/pre-action-output.log')." > "${TARGET_BUILD_DIR}/libsession_util_error.log"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
@ -76,6 +91,10 @@ if [ "${NEW_SOURCE_HASH}" != "${OLD_SOURCE_HASH}" ] || [ "${NEW_HEADER_HASH}" !=
|
|||
echo "${NEW_SOURCE_HASH}" > "${TARGET_BUILD_DIR}/libsession_util_source_hash.log"
|
||||
echo "${NEW_HEADER_HASH}" > "${TARGET_BUILD_DIR}/libsession_util_header_hash.log"
|
||||
echo "${ARCHS[*]}" > "${TARGET_BUILD_DIR}/libsession_util_archs.log"
|
||||
echo ""
|
||||
echo "info: Build complete"
|
||||
else
|
||||
echo "info: Build is up-to-date"
|
||||
fi
|
||||
|
||||
# Move the target-specific libSession-util build to the parent build directory (so XCode can have a reference to a single build)
|
||||
|
|
|
@ -734,6 +734,11 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
|
|||
initialIsBlocked: (viewModel.threadData.threadIsBlocked == true)
|
||||
)
|
||||
|
||||
messageRequestDescriptionLabel.text = (updatedThreadData.threadRequiresApproval == false ?
|
||||
"MESSAGE_REQUESTS_INFO".localized() :
|
||||
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO".localized()
|
||||
)
|
||||
|
||||
let messageRequestsViewWasVisible: Bool = (
|
||||
messageRequestStackView.isHidden == false
|
||||
)
|
||||
|
@ -865,6 +870,8 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
|
|||
self.viewModel.updateInteractionData(updatedData)
|
||||
self.tableView.reloadData()
|
||||
|
||||
// If we just sent a message then we want to jump to the bottom of the conversation instantly
|
||||
if didSendMessageBeforeUpdate {
|
||||
// We need to dispatch to the next run loop because it seems trying to scroll immediately after
|
||||
// triggering a 'reloadData' doesn't work
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
|
@ -875,6 +882,7 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
|
|||
self?.scrollButton.alpha = 0
|
||||
self?.unreadCountView.alpha = 0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -41,10 +41,6 @@ class SessionTableViewModel<NavItemId: Equatable, Section: SessionTableSection,
|
|||
|
||||
open var title: String { preconditionFailure("abstract class - override in subclass") }
|
||||
open var emptyStateTextPublisher: AnyPublisher<String?, Never> { Just(nil).eraseToAnyPublisher() }
|
||||
open var settingsData: [SectionModel] { preconditionFailure("abstract class - override in subclass") }
|
||||
open var observableSettingsData: ObservableData {
|
||||
preconditionFailure("abstract class - override in subclass")
|
||||
}
|
||||
open var footerView: AnyPublisher<UIView?, Never> { Just(nil).eraseToAnyPublisher() }
|
||||
open var footerButtonInfo: AnyPublisher<SessionButton.Info?, Never> {
|
||||
Just(nil).eraseToAnyPublisher()
|
||||
|
|
|
@ -296,6 +296,34 @@ public enum GarbageCollectionJob: JobExecutor {
|
|||
.filter(PendingReadReceipt.Columns.serverExpirationTimestamp <= timestampNow)
|
||||
.deleteAll(db)
|
||||
}
|
||||
|
||||
if finalTypesToCollect.contains(.shadowThreads) {
|
||||
// Shadow threads are thread records which were created to start a conversation that
|
||||
// didn't actually get turned into conversations (ie. the app was closed or crashed
|
||||
// before the user sent a message)
|
||||
let thread: TypedTableAlias<SessionThread> = TypedTableAlias()
|
||||
let contact: TypedTableAlias<Contact> = TypedTableAlias()
|
||||
let openGroup: TypedTableAlias<OpenGroup> = TypedTableAlias()
|
||||
let closedGroup: TypedTableAlias<ClosedGroup> = TypedTableAlias()
|
||||
|
||||
try db.execute(literal: """
|
||||
DELETE FROM \(SessionThread.self)
|
||||
WHERE \(Column.rowID) IN (
|
||||
SELECT \(thread.alias[Column.rowID])
|
||||
FROM \(SessionThread.self)
|
||||
LEFT JOIN \(Contact.self) ON \(contact[.id]) = \(thread[.id])
|
||||
LEFT JOIN \(OpenGroup.self) ON \(openGroup[.threadId]) = \(thread[.id])
|
||||
LEFT JOIN \(ClosedGroup.self) ON \(closedGroup[.threadId]) = \(thread[.id])
|
||||
WHERE (
|
||||
\(contact[.id]) IS NULL AND
|
||||
\(openGroup[.threadId]) IS NULL AND
|
||||
\(closedGroup[.threadId]) IS NULL AND
|
||||
\(thread[.shouldBeVisible]) = false AND
|
||||
\(SQL("\(thread[.id]) != \(getUserHexEncodedPublicKey(db))"))
|
||||
)
|
||||
)
|
||||
""")
|
||||
}
|
||||
},
|
||||
completion: { _, _ in
|
||||
// Dispatch async so we can swap from the write queue to a read one (we are done writing)
|
||||
|
@ -450,6 +478,7 @@ extension GarbageCollectionJob {
|
|||
case orphanedAttachmentFiles
|
||||
case orphanedProfileAvatars
|
||||
case expiredPendingReadReceipts
|
||||
case shadowThreads
|
||||
}
|
||||
|
||||
public struct Details: Codable {
|
||||
|
|
|
@ -206,6 +206,28 @@ public extension Message {
|
|||
}
|
||||
}
|
||||
|
||||
static func threadId(forMessage message: Message, destination: Message.Destination) -> String {
|
||||
switch destination {
|
||||
case .contact(let publicKey):
|
||||
// Extract the 'syncTarget' value if there is one
|
||||
let maybeSyncTarget: String?
|
||||
|
||||
switch message {
|
||||
case let message as VisibleMessage: maybeSyncTarget = message.syncTarget
|
||||
case let message as ExpirationTimerUpdate: maybeSyncTarget = message.syncTarget
|
||||
default: maybeSyncTarget = nil
|
||||
}
|
||||
|
||||
return (maybeSyncTarget ?? publicKey)
|
||||
|
||||
case .closedGroup(let groupPublicKey): return groupPublicKey
|
||||
case .openGroup(let roomToken, let server, _, _, _):
|
||||
return OpenGroup.idFor(roomToken: roomToken, server: server)
|
||||
|
||||
case .openGroupInbox(_, _, let blindedPublicKey): return blindedPublicKey
|
||||
}
|
||||
}
|
||||
|
||||
static func processRawReceivedMessage(
|
||||
_ db: Database,
|
||||
rawMessage: SnodeReceivedMessage
|
||||
|
|
|
@ -382,6 +382,12 @@ public final class OpenGroupManager {
|
|||
.filter(id: openGroupId)
|
||||
.deleteAll(db)
|
||||
|
||||
// Remove any MessageProcessRecord entries (we will want to reprocess all OpenGroup messages
|
||||
// if they get re-added)
|
||||
_ = try? ControlMessageProcessRecord
|
||||
.filter(ControlMessageProcessRecord.Columns.threadId == openGroupId)
|
||||
.deleteAll(db)
|
||||
|
||||
// Remove the open group (no foreign key to the thread so it won't auto-delete)
|
||||
if server?.lowercased() != OpenGroupAPI.defaultServer.lowercased() {
|
||||
_ = try? OpenGroup
|
||||
|
|
|
@ -961,16 +961,8 @@ public final class MessageSender {
|
|||
}
|
||||
}
|
||||
|
||||
let threadId: String = {
|
||||
switch destination {
|
||||
case .contact(let publicKey): return publicKey
|
||||
case .closedGroup(let groupPublicKey): return groupPublicKey
|
||||
case .openGroup(let roomToken, let server, _, _, _):
|
||||
return OpenGroup.idFor(roomToken: roomToken, server: server)
|
||||
|
||||
case .openGroupInbox(_, _, let blindedPublicKey): return blindedPublicKey
|
||||
}
|
||||
}()
|
||||
// Extract the threadId from the message
|
||||
let threadId: String = Message.threadId(forMessage: message, destination: destination)
|
||||
|
||||
// Prevent ControlMessages from being handled multiple times if not supported
|
||||
try? ControlMessageProcessRecord(
|
||||
|
|
|
@ -210,7 +210,7 @@ internal extension SessionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
// Delete any contact/thread records which aren't in the config message
|
||||
/// Delete any contact/thread records which aren't in the config message
|
||||
let syncedContactIds: [String] = targetContactData
|
||||
.map { $0.key }
|
||||
.appending(userPublicKey)
|
||||
|
@ -226,7 +226,28 @@ internal extension SessionUtil {
|
|||
.select(.id)
|
||||
.asRequest(of: String.self)
|
||||
.fetchAll(db)
|
||||
let combinedIds: [String] = contactIdsToRemove.appending(contentsOf: threadIdsToRemove)
|
||||
|
||||
/// When the user opens a brand new conversation this creates a "draft conversation" which has a hidden thread but no
|
||||
/// contact record, when we receive a contact update this "draft conversation" would be included in the
|
||||
/// `threadIdsToRemove` which would result in the user getting kicked from the screen and the thread removed, we
|
||||
/// want to avoid this (as it's essentially a bug) so find any conversations in this state and remove them from the list that
|
||||
/// will be pruned
|
||||
let threadT: TypedTableAlias<SessionThread> = TypedTableAlias()
|
||||
let contactT: TypedTableAlias<Contact> = TypedTableAlias()
|
||||
let draftConversationIds: [String] = try SQLRequest<String>("""
|
||||
SELECT \(threadT[.id])
|
||||
FROM \(SessionThread.self)
|
||||
LEFT JOIN \(Contact.self) ON \(contactT[.id]) = \(threadT[.id])
|
||||
WHERE (
|
||||
\(SQL("\(threadT[.id]) IN \(threadIdsToRemove)")) AND
|
||||
\(contactT[.id]) IS NULL
|
||||
)
|
||||
""").fetchAll(db)
|
||||
|
||||
/// Consolidate the ids which should be removed
|
||||
let combinedIds: [String] = contactIdsToRemove
|
||||
.appending(contentsOf: threadIdsToRemove)
|
||||
.filter { !draftConversationIds.contains($0) }
|
||||
|
||||
if !combinedIds.isEmpty {
|
||||
SessionUtil.kickFromConversationUIIfNeeded(removedThreadIds: combinedIds)
|
||||
|
|
|
@ -784,7 +784,6 @@ public extension SessionThreadViewModel {
|
|||
static func homeFilterSQL(userPublicKey: String) -> SQL {
|
||||
let thread: TypedTableAlias<SessionThread> = TypedTableAlias()
|
||||
let contact: TypedTableAlias<Contact> = TypedTableAlias()
|
||||
let interaction: TypedTableAlias<Interaction> = TypedTableAlias()
|
||||
|
||||
return """
|
||||
\(thread[.shouldBeVisible]) = true AND (
|
||||
|
@ -848,7 +847,6 @@ public extension SessionThreadViewModel {
|
|||
let interaction: TypedTableAlias<Interaction> = TypedTableAlias()
|
||||
|
||||
let aggregateInteractionLiteral: SQL = SQL(stringLiteral: "aggregateInteraction")
|
||||
let timestampMsColumnLiteral: SQL = SQL(stringLiteral: Interaction.Columns.timestampMs.name)
|
||||
let closedGroupUserCountTableLiteral: SQL = SQL(stringLiteral: "\(ViewModel.closedGroupUserCountString)_table")
|
||||
let groupMemberGroupIdColumnLiteral: SQL = SQL(stringLiteral: GroupMember.Columns.groupId.name)
|
||||
let profileIdColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.id.name)
|
||||
|
|
|
@ -1105,7 +1105,7 @@ public final class SnodeAPI {
|
|||
.compactMap { $0.value }
|
||||
.asSet()
|
||||
}
|
||||
.retry(4)
|
||||
.retry(2)
|
||||
.handleEvents(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
|
|
Loading…
Reference in New Issue