core, ios: chat tags (#5367)

* types and db

* migration module

* chat tag

* store method proposal

* profiles build

* update type

* update return type

* building

* working api

* update

* refactor

* attach tags to contact

* simplify

* attach chat tags to group info

* get chat tags with supplied user id

* get tags fix

* ios: chat tags poc (#5370)

* ios: chat tags poc

* updates to sheet

* temporary display for other option on swipe

* sheet height

* only show preset when it has matches

* changes

* worst emoji picker ever

* simplify tag casts and collapse

* open on create tag if no tags

* simple emoji text field

* nice emoji picker

* dismiss sheets on tag/untag

* semibold selection

* all preset tag and change collapsed icon on selection

* default selected tag (all)

* only apply tag filters on empty search

* + button when no custom lists

* reset selection of tag filter on profile changes

* edit tag (broken menu inside swiftui list)

* create list to end of list

* swipe changes

* remove context menu

* delete and edit on swipe actions

* tap unread filter deselects other filters

* remove delete tag if empty

* show tag creation sheet when + button pressed

* in memory tag edit

* color, size

* frame

* layout

* refactor

* remove code

* add unread to same unit

* fraction on long press

* nav fixes

* in memory list

* emoji picker improvements

* remove diff

* secondary plus

* stop flickering on chat tags load

* reuse string

* fix reset glitches

* delete destructive

* simplify?

* changes

* api updates

* fix styles on list via swipe

* fixed untag

* update schema

* move user tags loading to get users chat data

* move presets to model

* update preset tags when chats are updated

* style fixes and locate getPresetTags near tags model

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>

* deleted contacts and card should not match contact preset

* fix update presets on chat remove

* update migration indices

* fix migration

* not used chat model

* disable button on repeated list name or emoji

* no chats message for search fix

* fix edits and trim

* error in footer, not in alert

* styling fixes due to wrong place to attach sheet

* update library

* remove log

* idea for dynamic sheet height

* max fraction 62%

* minor fixes

* disable save button when no changes and while saving

* disable preset filter if it is no longer shown

* remove comments from schema

* fix emoji

* remove apiChatTagsResponse

* always read chat tags

* fix

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
This commit is contained in:
Diogo
2024-12-19 10:48:26 +00:00
committed by GitHub
parent a73fb89c44
commit fcb2d1dbac
25 changed files with 1311 additions and 116 deletions
+80 -6
View File
@@ -9,6 +9,7 @@
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Store.Shared where
@@ -391,14 +392,14 @@ type ContactRow' = (ProfileId, ContactName, Maybe Int64, ContactName, Text, Mayb
type ContactRow = Only ContactId :. ContactRow'
toContact :: VersionRangeChat -> User -> ContactRow :. MaybeConnectionRow -> Contact
toContact vr user ((Only contactId :. (profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. (contactGroupMemberId, contactGrpInvSent, uiThemes, chatDeleted, customData)) :. connRow) =
toContact :: VersionRangeChat -> User -> [ChatTagId] -> ContactRow :. MaybeConnectionRow -> Contact
toContact vr user chatTags ((Only contactId :. (profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. (contactGroupMemberId, contactGrpInvSent, uiThemes, chatDeleted, customData)) :. connRow) =
let profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
activeConn = toMaybeConnection vr connRow
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts, favorite}
incognito = maybe False connIncognito activeConn
mergedPreferences = contactUserPreferences user userPreferences preferences incognito
in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, uiThemes, chatDeleted, customData}
in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, chatTags, uiThemes, chatDeleted, customData}
getProfileById :: DB.Connection -> UserId -> Int64 -> ExceptT StoreError IO LocalProfile
getProfileById db userId profileId =
@@ -552,14 +553,14 @@ type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Maybe ImageD
type GroupMemberRow = ((Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, Bool, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId, ProfileId, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, Maybe Preferences))
toGroupInfo :: VersionRangeChat -> Int64 -> GroupInfoRow -> GroupInfo
toGroupInfo vr userContactId ((groupId, localDisplayName, displayName, fullName, description, image, hostConnCustomUserProfileId, enableNtfs_, sendRcpts, favorite, groupPreferences) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData) :. userMemberRow) =
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, description, image, hostConnCustomUserProfileId, enableNtfs_, sendRcpts, favorite, groupPreferences) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData) :. userMemberRow) =
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts, favorite}
fullGroupPreferences = mergeGroupPreferences groupPreferences
groupProfile = GroupProfile {displayName, fullName, description, image, groupPreferences}
businessChat = toBusinessChatInfo businessRow
in GroupInfo {groupId, localDisplayName, groupProfile, businessChat, fullGroupPreferences, membership, hostConnCustomUserProfileId, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, uiThemes, customData}
in GroupInfo {groupId, localDisplayName, groupProfile, businessChat, fullGroupPreferences, membership, hostConnCustomUserProfileId, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, chatTags, uiThemes, customData}
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId, profileId, displayName, fullName, image, contactLink, localAlias, preferences)) =
@@ -592,3 +593,76 @@ groupInfoQuery =
JOIN group_members mu ON mu.group_id = g.group_id
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|]
createChatTag :: DB.Connection -> User -> Maybe Text -> Text -> IO ChatTagId
createChatTag db User {userId} emoji text = do
DB.execute
db
[sql|
INSERT INTO chat_tags (user_id, chat_tag_emoji, chat_tag_text, tag_order)
VALUES (?,?,?, COALESCE((SELECT MAX(tag_order) + 1 FROM chat_tags WHERE user_id = ?), 1))
|]
(userId, emoji, text, userId)
insertedRowId db
deleteChatTag :: DB.Connection -> User -> ChatTagId -> IO ()
deleteChatTag db User {userId} tId =
DB.execute
db
[sql|
DELETE FROM chat_tags
WHERE user_id = ? AND chat_tag_id = ?
|]
(userId, tId)
updateChatTag :: DB.Connection -> User -> ChatTagId -> Maybe Text -> Text -> IO ()
updateChatTag db User {userId} tId emoji text =
DB.execute
db
[sql|
UPDATE chat_tags
SET chat_tag_emoji = ?, chat_tag_text = ?
WHERE user_id = ? AND chat_tag_id = ?
|]
(emoji, text, userId, tId)
updateChatTagOrder :: DB.Connection -> User -> ChatTagId -> Int -> IO ()
updateChatTagOrder db User {userId} tId order =
DB.execute
db
[sql|
UPDATE chat_tags
SET tag_order = ?
WHERE user_id = ? AND chat_tag_id = ?
|]
(order, userId, tId)
reorderChatTags :: DB.Connection -> User -> [ChatTagId] -> IO ()
reorderChatTags db user tIds =
forM_ (zip [1 ..] tIds) $ \(order, tId) ->
updateChatTagOrder db user tId order
getUserChatTags :: DB.Connection -> User -> IO [ChatTag]
getUserChatTags db User {userId} =
map toChatTag
<$> DB.query
db
[sql|
SELECT chat_tag_id, chat_tag_emoji, chat_tag_text
FROM chat_tags
WHERE user_id = ?
ORDER BY tag_order
|]
(Only userId)
where
toChatTag :: (ChatTagId, Maybe Text, Text) -> ChatTag
toChatTag (chatTagId, chatTagEmoji, chatTagText) = ChatTag {chatTagId, chatTagEmoji, chatTagText}
getGroupChatTags :: DB.Connection -> GroupId -> IO [ChatTagId]
getGroupChatTags db groupId =
map fromOnly <$> DB.query db "SELECT chat_tag_id FROM chat_tags_chats WHERE group_id = ?" (Only groupId)
addGroupChatTags :: DB.Connection -> GroupInfo -> IO GroupInfo
addGroupChatTags db g@GroupInfo {groupId} = do
chatTags <- getGroupChatTags db groupId
pure (g :: GroupInfo) {chatTags}