@@ -1654,6 +1654,140 @@ private struct RoomOptionsSheet: View {
16541654 }
16551655}
16561656
1657+ private struct TopicChatPickerSheet : View {
1658+ @ ObservedObject var model : AppModel
1659+ let roomID : String
1660+ let onClose : ( ) -> Void
1661+ @ State private var topicTitle = " "
1662+
1663+ private var room : AppRoomSummary ? {
1664+ model . state ? . rooms . first ( where : { $0 . roomId == roomID } )
1665+ }
1666+
1667+ private var topics : [ AppTopicSummary ] {
1668+ model . topics ( for : roomID )
1669+ }
1670+
1671+ var body : some View {
1672+ NavigationStack {
1673+ List {
1674+ Section {
1675+ HStack ( spacing : 8 ) {
1676+ TextField ( " New topic " , text : $topicTitle )
1677+ . textInputAutocapitalization ( . sentences )
1678+ . submitLabel ( . done )
1679+ . onSubmit ( createTopic )
1680+
1681+ Button ( action : createTopic ) {
1682+ Image ( systemName : " plus " )
1683+ }
1684+ . disabled ( topicTitle . trimmingCharacters ( in : . whitespacesAndNewlines ) . isEmpty )
1685+ . accessibilityLabel ( " Create topic " )
1686+ }
1687+ }
1688+
1689+ if topics . isEmpty {
1690+ ContentUnavailableView ( " No topics " , systemImage : " sidebar.left " )
1691+ . listRowSeparator ( . hidden )
1692+ } else {
1693+ ForEach ( topics , id : \. topicId ) { topic in
1694+ Section {
1695+ if topic . chats . isEmpty {
1696+ Button {
1697+ _ = model . startChat ( in : topic )
1698+ onClose ( )
1699+ } label : {
1700+ Label ( " New chat " , systemImage : " square.and.pencil " )
1701+ }
1702+ } else {
1703+ ForEach ( topic . chats , id : \. chatId ) { chat in
1704+ Button {
1705+ model . openChat ( chat , in : topic )
1706+ onClose ( )
1707+ } label : {
1708+ TopicChatRow (
1709+ chat : chat ,
1710+ selected : model . state ? . selectedChatId == chat . chatId
1711+ && model . state ? . selectedTopicId == topic . topicId
1712+ )
1713+ }
1714+ . buttonStyle ( . plain )
1715+ }
1716+ }
1717+ } header : {
1718+ HStack {
1719+ Text ( topic . title )
1720+ Spacer ( )
1721+ Button {
1722+ _ = model . startChat ( in : topic )
1723+ onClose ( )
1724+ } label : {
1725+ Image ( systemName : " square.and.pencil " )
1726+ }
1727+ . accessibilityLabel ( " New chat in \( topic . title ) " )
1728+ }
1729+ }
1730+ }
1731+ }
1732+ }
1733+ . navigationTitle ( room ? . displayName ?? " Chats " )
1734+ . navigationBarTitleDisplayMode ( . inline )
1735+ . toolbar {
1736+ ToolbarItem ( placement : . topBarTrailing ) {
1737+ Button ( " Done " , action : onClose )
1738+ }
1739+ }
1740+ }
1741+ }
1742+
1743+ private func createTopic ( ) {
1744+ let title = topicTitle . trimmingCharacters ( in : . whitespacesAndNewlines )
1745+ guard ! title . isEmpty else { return }
1746+ if model . createTopic ( roomID : roomID , title : title ) {
1747+ topicTitle = " "
1748+ }
1749+ }
1750+ }
1751+
1752+ private struct TopicChatRow : View {
1753+ let chat : AppChatSummary
1754+ let selected : Bool
1755+
1756+ var body : some View {
1757+ HStack ( spacing : 10 ) {
1758+ Image ( systemName : selected ? " checkmark.circle.fill " : " bubble.left " )
1759+ . foregroundStyle ( selected ? Color . accentColor : Color . secondary )
1760+ . frame ( width : 22 )
1761+
1762+ VStack ( alignment : . leading , spacing : 3 ) {
1763+ Text ( chat . title )
1764+ . font ( . body )
1765+ . foregroundStyle ( . primary )
1766+ . lineLimit ( 1 )
1767+
1768+ if ! chat . lastMessagePreview . trimmingCharacters ( in : . whitespacesAndNewlines ) . isEmpty {
1769+ Text ( chat . lastMessagePreview )
1770+ . font ( . caption )
1771+ . foregroundStyle ( . secondary )
1772+ . lineLimit ( 1 )
1773+ }
1774+ }
1775+
1776+ Spacer ( )
1777+
1778+ if chat . unreadCount > 0 {
1779+ Text ( " \( chat . unreadCount ) " )
1780+ . font ( . caption2 . weight ( . semibold ) )
1781+ . foregroundStyle ( . white )
1782+ . padding ( . horizontal , 7 )
1783+ . padding ( . vertical , 3 )
1784+ . background ( Capsule ( ) . fill ( Color . accentColor ) )
1785+ }
1786+ }
1787+ . contentShape ( Rectangle ( ) )
1788+ }
1789+ }
1790+
16571791private struct RoomThreadView : View {
16581792 @ ObservedObject var model : AppModel
16591793 @ ObservedObject var people : NostrPeopleModel
@@ -1673,6 +1807,7 @@ private struct RoomThreadView: View {
16731807 @ State private var showMediaGallery = false
16741808 @ State private var showRoomDetails = false
16751809 @ State private var showRoomOptions = false
1810+ @ State private var showChatPicker = false
16761811 @ State private var showAddPeople = false
16771812 @ State private var composerText = " "
16781813 @ State private var selectedPhotoItems : [ PhotosPickerItem ] = [ ]
@@ -1691,6 +1826,28 @@ private struct RoomThreadView: View {
16911826 model . projection ( for : roomID )
16921827 }
16931828
1829+ private var selectedTopic : AppTopicSummary ? {
1830+ model . selectedTopic ( in : roomID )
1831+ }
1832+
1833+ private var selectedChat : AppChatSummary ? {
1834+ selectedTopic . flatMap { model . selectedChat ( in : $0 ) }
1835+ }
1836+
1837+ private var chatNavigationTitle : String {
1838+ if let title = selectedChat ? . title . trimmingCharacters ( in : . whitespacesAndNewlines ) ,
1839+ ! title . isEmpty
1840+ {
1841+ return title
1842+ }
1843+ if let title = selectedTopic ? . title . trimmingCharacters ( in : . whitespacesAndNewlines ) ,
1844+ ! title . isEmpty
1845+ {
1846+ return title
1847+ }
1848+ return room ? . displayName ?? " Chat "
1849+ }
1850+
16941851 private var mediaGalleryItems : [ ChatMediaGalleryItem ] {
16951852 guard let gallery = model . state ? . mediaGallery ,
16961853 gallery . roomId == roomID
@@ -1777,10 +1934,20 @@ private struct RoomThreadView: View {
17771934 }
17781935 }
17791936 . background ( Color ( . systemGroupedBackground ) . ignoresSafeArea ( ) )
1780- . navigationTitle ( room ? . displayName ?? " Chat " )
1937+ . navigationTitle ( chatNavigationTitle )
17811938 . navigationBarTitleDisplayMode ( . inline )
17821939 . chatNavigationBarChrome ( )
17831940 . toolbar {
1941+ ToolbarItem ( placement : . topBarLeading ) {
1942+ Button {
1943+ showChatPicker = true
1944+ } label : {
1945+ Image ( systemName : " line.3.horizontal " )
1946+ }
1947+ . accessibilityLabel ( " Chats " )
1948+ . accessibilityIdentifier ( " ChatPickerButton " )
1949+ }
1950+
17841951 if let room , room . state == . connected {
17851952 ToolbarItem ( placement : . topBarTrailing ) {
17861953 Button {
@@ -1826,6 +1993,12 @@ private struct RoomThreadView: View {
18261993 )
18271994 . presentationDetents ( [ . medium ] )
18281995 }
1996+ . sheet ( isPresented : $showChatPicker ) {
1997+ TopicChatPickerSheet ( model : model , roomID : roomID ) {
1998+ showChatPicker = false
1999+ }
2000+ . presentationDetents ( [ . medium , . large ] )
2001+ }
18292002 . sheet ( isPresented : $showAddPeople ) {
18302003 if let room {
18312004 ChatPeoplePickerSheet ( model : model , people : people , existingRoom : room ) { _ in }
0 commit comments