Organize banners, add LeftBanner
Previously there was InviteOffer, now there's a base Banner component, InviteBanner and LeftBanner.
This commit is contained in:
		
							
								
								
									
										2
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								TODO.md
									
									
									
									
									
								
							@@ -13,6 +13,7 @@
 | 
				
			|||||||
  - Graphic bug when resizing window vertically for side pane?
 | 
					  - Graphic bug when resizing window vertically for side pane?
 | 
				
			||||||
  - Fix tooltip hide()
 | 
					  - Fix tooltip hide()
 | 
				
			||||||
  -  not clickable?
 | 
					  -  not clickable?
 | 
				
			||||||
 | 
					  - Icons aren't reloaded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- UI
 | 
					- UI
 | 
				
			||||||
  - Use HRowLayout and its totalSpacing wherever possible
 | 
					  - Use HRowLayout and its totalSpacing wherever possible
 | 
				
			||||||
@@ -48,6 +49,7 @@
 | 
				
			|||||||
    it should be the peer's display name instead.
 | 
					    it should be the peer's display name instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Missing nio support
 | 
					- Missing nio support
 | 
				
			||||||
 | 
					  - Left room events
 | 
				
			||||||
  - `org.matrix.room.preview_urls` event
 | 
					  - `org.matrix.room.preview_urls` event
 | 
				
			||||||
  - `m.room.aliases` event
 | 
					  - `m.room.aliases` event
 | 
				
			||||||
  - Avatars
 | 
					  - Avatars
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,14 +22,15 @@ _POOLS: DefaultDict[str, ThreadPoolExecutor] = \
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Client(QObject):
 | 
					class Client(QObject):
 | 
				
			||||||
    roomInvited                    = pyqtSignal(str)
 | 
					    roomInvited = pyqtSignal([str, dict], [str])
 | 
				
			||||||
    roomInvited                    = pyqtSignal(str, dict)
 | 
					 | 
				
			||||||
    roomJoined  = pyqtSignal(str)
 | 
					    roomJoined  = pyqtSignal(str)
 | 
				
			||||||
    roomLeft                       = pyqtSignal(str)
 | 
					    roomLeft    = pyqtSignal([str, dict], [str])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    roomSyncPrevBatchTokenReceived = pyqtSignal(str, str)
 | 
					    roomSyncPrevBatchTokenReceived = pyqtSignal(str, str)
 | 
				
			||||||
    roomPastPrevBatchTokenReceived = pyqtSignal(str, str)
 | 
					    roomPastPrevBatchTokenReceived = pyqtSignal(str, str)
 | 
				
			||||||
    roomEventReceived              = pyqtSignal(str, str, dict)
 | 
					    roomEventReceived              = pyqtSignal(str, str, dict)
 | 
				
			||||||
    roomTypingUsersUpdated         = pyqtSignal(str, list)
 | 
					    roomTypingUsersUpdated         = pyqtSignal(str, list)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    messageAboutToBeSent = pyqtSignal(str, dict)
 | 
					    messageAboutToBeSent = pyqtSignal(str, dict)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -120,13 +121,13 @@ class Client(QObject):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        for room_id, room_info in response.rooms.invite.items():
 | 
					        for room_id, room_info in response.rooms.invite.items():
 | 
				
			||||||
            for ev in room_info.invite_state:
 | 
					            for ev in room_info.invite_state:
 | 
				
			||||||
                member_event = isinstance(ev, ne.InviteMemberEvent)
 | 
					                member_ev = isinstance(ev, ne.InviteMemberEvent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if member_event and ev.content["membership"] == "join":
 | 
					                if member_ev and ev.content["membership"] == "join":
 | 
				
			||||||
                    self.roomInvited.emit(room_id, ev.content)
 | 
					                    self.roomInvited.emit(room_id, ev.content)
 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self.roomInvited.emit(room_id)
 | 
					                self.roomInvited[str].emit(room_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for room_id, room_info in response.rooms.join.items():
 | 
					        for room_id, room_info in response.rooms.join.items():
 | 
				
			||||||
            self.roomJoined.emit(room_id)
 | 
					            self.roomJoined.emit(room_id)
 | 
				
			||||||
@@ -146,8 +147,15 @@ class Client(QObject):
 | 
				
			|||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    print("ephemeral event: ", ev)
 | 
					                    print("ephemeral event: ", ev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for room_id in response.rooms.leave:
 | 
					        for room_id, room_info in response.rooms.leave.items():
 | 
				
			||||||
            self.roomLeft.emit(room_id)
 | 
					            for ev in room_info.timeline.events:
 | 
				
			||||||
 | 
					                member_ev = isinstance(ev, ne.RoomMemberEvent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if member_ev and ev.content["membership"] in ("leave", "ban"):
 | 
				
			||||||
 | 
					                    self.roomLeft.emit(room_id, ev.__dict__)
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.roomLeft[str].emit(room_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @futurize()
 | 
					    @futurize()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
from typing import Any, Callable, Optional, Tuple, Union
 | 
					from typing import Any, Callable, Optional, Sequence, Tuple, Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
 | 
					from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -6,8 +6,9 @@ from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
 | 
				
			|||||||
class ListItem(QObject):
 | 
					class ListItem(QObject):
 | 
				
			||||||
    roles: Tuple[str, ...] = ()
 | 
					    roles: Tuple[str, ...] = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, *args, **kwargs):
 | 
					    def __init__(self, *args, no_update: Sequence[str] = (), **kwargs):
 | 
				
			||||||
        super().__init__()
 | 
					        super().__init__()
 | 
				
			||||||
 | 
					        self.no_update = no_update
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for role, value in zip(self.roles, args):
 | 
					        for role, value in zip(self.roles, args):
 | 
				
			||||||
            setattr(self, role, value)
 | 
					            setattr(self, role, value)
 | 
				
			||||||
@@ -17,8 +18,9 @@ class ListItem(QObject):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __repr__(self) -> str:
 | 
					    def __repr__(self) -> str:
 | 
				
			||||||
        return "%s(%s)" % (
 | 
					        return "%s(no_update=%s, %s)" % (
 | 
				
			||||||
            type(self).__name__,
 | 
					            type(self).__name__,
 | 
				
			||||||
 | 
					            self.no_update,
 | 
				
			||||||
            ", ".join((f"{r}={getattr(self, r)!r}" for r in self.roles)),
 | 
					            ", ".join((f"{r}={getattr(self, r)!r}" for r in self.roles)),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -63,7 +65,7 @@ class User(ListItem):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Room(ListItem):
 | 
					class Room(ListItem):
 | 
				
			||||||
    roles = ("roomId", "category", "displayName", "topic", "typingUsers",
 | 
					    roles = ("roomId", "category", "displayName", "topic", "typingUsers",
 | 
				
			||||||
             "inviter")
 | 
					             "inviter", "leftEvent")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    categoryChanged    = pyqtSignal(str)
 | 
					    categoryChanged    = pyqtSignal(str)
 | 
				
			||||||
    displayNameChanged = pyqtSignal("QVariant")
 | 
					    displayNameChanged = pyqtSignal("QVariant")
 | 
				
			||||||
@@ -75,7 +77,8 @@ class Room(ListItem):
 | 
				
			|||||||
    displayName = prop(str, "displayName", displayNameChanged)
 | 
					    displayName = prop(str, "displayName", displayNameChanged)
 | 
				
			||||||
    topic       = prop(str, "topic", topicChanged, "")
 | 
					    topic       = prop(str, "topic", topicChanged, "")
 | 
				
			||||||
    typingUsers = prop(list, "typingUsers", typingUsersChanged, [])
 | 
					    typingUsers = prop(list, "typingUsers", typingUsersChanged, [])
 | 
				
			||||||
    inviter     = prop("QVariantMap", "inviter")
 | 
					    inviter     = prop("QVariant", "inviter")
 | 
				
			||||||
 | 
					    leftEvent   = prop("QVariant", "leftEvent")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RoomEvent(ListItem):
 | 
					class RoomEvent(ListItem):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -148,6 +148,9 @@ class ListModel(QAbstractListModel):
 | 
				
			|||||||
        value = self._convert_new_value(value)
 | 
					        value = self._convert_new_value(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for role in self.roles:
 | 
					        for role in self.roles:
 | 
				
			||||||
 | 
					            if role in value.no_update:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            setattr(self._data[index], role, getattr(value, role))
 | 
					            setattr(self._data[index], role, getattr(value, role))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        qidx = QAbstractListModel.index(self, index, 0)
 | 
					        qidx = QAbstractListModel.index(self, index, 0)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ from .client import Client
 | 
				
			|||||||
from .model.items import Room, RoomEvent, User
 | 
					from .model.items import Room, RoomEvent, User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Inviter   = Optional[Dict[str, str]]
 | 
					Inviter   = Optional[Dict[str, str]]
 | 
				
			||||||
 | 
					LeftEvent = Optional[Dict[str, str]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SignalManager(QObject):
 | 
					class SignalManager(QObject):
 | 
				
			||||||
@@ -60,45 +61,60 @@ class SignalManager(QObject):
 | 
				
			|||||||
                      client:  Client,
 | 
					                      client:  Client,
 | 
				
			||||||
                      room_id: str,
 | 
					                      room_id: str,
 | 
				
			||||||
                      inviter: Inviter = None) -> None:
 | 
					                      inviter: Inviter = None) -> None:
 | 
				
			||||||
        self._add_room(
 | 
					
 | 
				
			||||||
            client, client.nio.invited_rooms[room_id], "Invites", inviter
 | 
					        self._add_room(client, room_id, client.nio.invited_rooms[room_id],
 | 
				
			||||||
        )
 | 
					                       "Invites", inviter=inviter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def onRoomJoined(self, client: Client, room_id: str) -> None:
 | 
					    def onRoomJoined(self, client: Client, room_id: str) -> None:
 | 
				
			||||||
        self._add_room(client, client.nio.rooms[room_id], "Rooms")
 | 
					        self._add_room(client, room_id, client.nio.rooms[room_id], "Rooms")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def onRoomLeft(self,
 | 
				
			||||||
 | 
					                   client:     Client,
 | 
				
			||||||
 | 
					                   room_id:    str,
 | 
				
			||||||
 | 
					                   left_event: LeftEvent = None) -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._add_room(client, room_id, client.nio.rooms.get(room_id), "Left",
 | 
				
			||||||
 | 
					                       left_event=left_event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _add_room(self,
 | 
					    def _add_room(self,
 | 
				
			||||||
                  client:     Client,
 | 
					                  client:     Client,
 | 
				
			||||||
 | 
					                  room_id:    str,
 | 
				
			||||||
                  room:       MatrixRoom,
 | 
					                  room:       MatrixRoom,
 | 
				
			||||||
                  category:   str,
 | 
					                  category:   str,
 | 
				
			||||||
                  inviter:  Inviter = None) -> None:
 | 
					                  inviter:    Inviter   = None,
 | 
				
			||||||
        model = self.backend.models.rooms[client.userId]
 | 
					                  left_event: LeftEvent = None) -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert not (inviter and left_event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        model     = self.backend.models.rooms[client.userId]
 | 
				
			||||||
 | 
					        no_update = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def get_displayname() -> Optional[str]:
 | 
				
			||||||
 | 
					            if not room:
 | 
				
			||||||
 | 
					                no_update.append("displayName")
 | 
				
			||||||
 | 
					                return room_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            name = room.name or room.canonical_alias
 | 
				
			||||||
 | 
					            if name:
 | 
				
			||||||
 | 
					                return name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def group_name() -> Optional[str]:
 | 
					 | 
				
			||||||
            name = room.group_name()
 | 
					            name = room.group_name()
 | 
				
			||||||
            return None if name == "Empty room?" else name
 | 
					            return None if name == "Empty room?" else name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        item = Room(
 | 
					        item = Room(
 | 
				
			||||||
            roomId      = room.room_id,
 | 
					            roomId      = room_id,
 | 
				
			||||||
            category    = category,
 | 
					            category    = category,
 | 
				
			||||||
            displayName = room.name or room.canonical_alias or group_name(),
 | 
					            displayName = get_displayname(),
 | 
				
			||||||
            topic       = room.topic,
 | 
					            topic       = room.topic if room else "",
 | 
				
			||||||
            inviter     = inviter,
 | 
					            inviter     = inviter,
 | 
				
			||||||
 | 
					            leftEvent   = left_event,
 | 
				
			||||||
 | 
					            no_update   = no_update,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model.updateOrAppendWhere("roomId", room.room_id, item)
 | 
					        model.updateOrAppendWhere("roomId", room_id, item)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def onRoomLeft(self, client: Client, room_id: str) -> None:
 | 
					 | 
				
			||||||
        rooms = self.backend.models.rooms[client.userId]
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            index = rooms.indexWhere("roomId", room_id)
 | 
					 | 
				
			||||||
        except ValueError:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            del rooms[index]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def onRoomSyncPrevBatchTokenReceived(
 | 
					    def onRoomSyncPrevBatchTokenReceived(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ Controls1.SplitView {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    StackView {
 | 
					    StackView {
 | 
				
			||||||
        function showRoom(userId, roomId, isInvite) {
 | 
					        function showRoom(userId, roomId) {
 | 
				
			||||||
            pageStack.replace(
 | 
					            pageStack.replace(
 | 
				
			||||||
                "chat/Root.qml", { userId: userId, roomId: roomId }
 | 
					                "chat/Root.qml", { userId: userId, roomId: roomId }
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
@@ -29,6 +29,7 @@ Controls1.SplitView {
 | 
				
			|||||||
        initialItem: Item {  // TODO: (test, remove)
 | 
					        initialItem: Item {  // TODO: (test, remove)
 | 
				
			||||||
            Keys.onPressed: pageStack.showRoom(
 | 
					            Keys.onPressed: pageStack.showRoom(
 | 
				
			||||||
                "@test_mary:matrix.org", "!TSXGsbBbdwsdylIOJZ:matrix.org"
 | 
					                "@test_mary:matrix.org", "!TSXGsbBbdwsdylIOJZ:matrix.org"
 | 
				
			||||||
 | 
					                //"@test_mary:matrix.org", "!TEXkdeErtVCMqClNfb:matrix.org"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										89
									
								
								harmonyqml/components/chat/Banner.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								harmonyqml/components/chat/Banner.qml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					import QtQuick 2.7
 | 
				
			||||||
 | 
					import QtQuick.Controls 2.2
 | 
				
			||||||
 | 
					import QtQuick.Layouts 1.4
 | 
				
			||||||
 | 
					import "../base" as Base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Rectangle {
 | 
				
			||||||
 | 
					    id: banner
 | 
				
			||||||
 | 
					    Layout.fillWidth: true
 | 
				
			||||||
 | 
					    Layout.preferredHeight: 32
 | 
				
			||||||
 | 
					    color: "#BBB"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    property alias avatarName: bannerAvatar.name
 | 
				
			||||||
 | 
					    property alias avatarSource: bannerAvatar.imageSource
 | 
				
			||||||
 | 
					    property alias labelText: bannerLabel.text
 | 
				
			||||||
 | 
					    property alias buttonModel: bannerRepeater.model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Base.HRowLayout {
 | 
				
			||||||
 | 
					        id: bannerRow
 | 
				
			||||||
 | 
					        anchors.fill: parent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Base.Avatar {
 | 
				
			||||||
 | 
					            id: bannerAvatar
 | 
				
			||||||
 | 
					            dimmension: banner.Layout.preferredHeight
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Base.HLabel {
 | 
				
			||||||
 | 
					            id: bannerLabel
 | 
				
			||||||
 | 
					            textFormat: Text.StyledText
 | 
				
			||||||
 | 
					            maximumLineCount: 1
 | 
				
			||||||
 | 
					            elide: Text.ElideRight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            visible:
 | 
				
			||||||
 | 
					                bannerRow.width - bannerAvatar.width - bannerButtons.width > 30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Layout.maximumWidth:
 | 
				
			||||||
 | 
					                bannerRow.width -
 | 
				
			||||||
 | 
					                bannerAvatar.width - bannerButtons.width -
 | 
				
			||||||
 | 
					                Layout.leftMargin - Layout.rightMargin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Layout.leftMargin: 10
 | 
				
			||||||
 | 
					            Layout.rightMargin: Layout.leftMargin
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Item { Layout.fillWidth: true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Base.HRowLayout {
 | 
				
			||||||
 | 
					            id: bannerButtons
 | 
				
			||||||
 | 
					            spacing: 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            function getButtonsWidth() {
 | 
				
			||||||
 | 
					                var total = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (var i = 0; i < bannerRepeater.count; i++) {
 | 
				
			||||||
 | 
					                    total += bannerRepeater.itemAt(i).implicitWidth
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return total
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            property bool compact:
 | 
				
			||||||
 | 
					                bannerRow.width <
 | 
				
			||||||
 | 
					                bannerAvatar.width +
 | 
				
			||||||
 | 
					                bannerLabel.implicitWidth +
 | 
				
			||||||
 | 
					                bannerLabel.Layout.leftMargin +
 | 
				
			||||||
 | 
					                bannerLabel.Layout.rightMargin +
 | 
				
			||||||
 | 
					                getButtonsWidth()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             property int displayMode:
 | 
				
			||||||
 | 
					                 compact ? Button.IconOnly : Button.TextBesideIcon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Repeater {
 | 
				
			||||||
 | 
					                id: bannerRepeater
 | 
				
			||||||
 | 
					                model: []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Base.HButton {
 | 
				
			||||||
 | 
					                    id: declineButton
 | 
				
			||||||
 | 
					                    text: modelData.text
 | 
				
			||||||
 | 
					                    iconName: modelData.iconName
 | 
				
			||||||
 | 
					                    icon.color: modelData.iconColor
 | 
				
			||||||
 | 
					                    icon.width: 32
 | 
				
			||||||
 | 
					                    display: bannerButtons.displayMode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Layout.maximumWidth: bannerButtons.compact ? height : -1
 | 
				
			||||||
 | 
					                    Layout.fillHeight: true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								harmonyqml/components/chat/InviteBanner.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								harmonyqml/components/chat/InviteBanner.qml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import QtQuick 2.7
 | 
				
			||||||
 | 
					import QtQuick.Controls 2.2
 | 
				
			||||||
 | 
					import QtQuick.Layouts 1.4
 | 
				
			||||||
 | 
					import "../base" as Base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Banner {
 | 
				
			||||||
 | 
					    property var inviter: null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    avatarName: inviter ? inviter.displayname : ""
 | 
				
			||||||
 | 
					    //avatarSource: inviter ? inviter.avatar_url : ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    labelText:
 | 
				
			||||||
 | 
					        (inviter ?
 | 
				
			||||||
 | 
					         ("<b>" + inviter.displayname + "</b>") : qsTr("Someone")) +
 | 
				
			||||||
 | 
					        " " + qsTr("invited you to join the room.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    buttonModel: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            text: "Accept",
 | 
				
			||||||
 | 
					            iconName: "accept",
 | 
				
			||||||
 | 
					            iconColor: Qt.hsla(0.45, 0.9, 0.3, 1),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            text: "Decline",
 | 
				
			||||||
 | 
					            iconName: "decline",
 | 
				
			||||||
 | 
					            iconColor: Qt.hsla(0.95, 0.9, 0.35, 1),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,84 +0,0 @@
 | 
				
			|||||||
import QtQuick 2.7
 | 
					 | 
				
			||||||
import QtQuick.Controls 2.2
 | 
					 | 
				
			||||||
import QtQuick.Layouts 1.4
 | 
					 | 
				
			||||||
import "../base" as Base
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Rectangle {
 | 
					 | 
				
			||||||
    id: inviteOffer
 | 
					 | 
				
			||||||
    Layout.fillWidth: true
 | 
					 | 
				
			||||||
    Layout.preferredHeight: 32
 | 
					 | 
				
			||||||
    color: "#BBB"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    property var inviter: null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Base.HRowLayout {
 | 
					 | 
				
			||||||
        id: inviteRow
 | 
					 | 
				
			||||||
        anchors.fill: parent
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Base.Avatar {
 | 
					 | 
				
			||||||
            id: inviteAvatar
 | 
					 | 
				
			||||||
            name: inviter ? inviter.displayname : ""
 | 
					 | 
				
			||||||
            dimmension: inviteOffer.Layout.preferredHeight
 | 
					 | 
				
			||||||
            //imageSource: inviter ? inviter.avatar_url : ""
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Base.HLabel {
 | 
					 | 
				
			||||||
            id: inviteLabel
 | 
					 | 
				
			||||||
            text: (inviter ?
 | 
					 | 
				
			||||||
                   ("<b>" + inviter.displayname + "</b>") : qsTr("Someone")) +
 | 
					 | 
				
			||||||
                  " " + qsTr("invited you to join the room.")
 | 
					 | 
				
			||||||
            textFormat: Text.StyledText
 | 
					 | 
				
			||||||
            maximumLineCount: 1
 | 
					 | 
				
			||||||
            elide: Text.ElideRight
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            visible:
 | 
					 | 
				
			||||||
                inviteRow.width - inviteAvatar.width - inviteButtons.width > 30
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Layout.maximumWidth:
 | 
					 | 
				
			||||||
                inviteRow.width -
 | 
					 | 
				
			||||||
                inviteAvatar.width - inviteButtons.width -
 | 
					 | 
				
			||||||
                Layout.leftMargin - Layout.rightMargin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Layout.leftMargin: 10
 | 
					 | 
				
			||||||
            Layout.rightMargin: Layout.leftMargin
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Item { Layout.fillWidth: true }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Base.HRowLayout {
 | 
					 | 
				
			||||||
            id: inviteButtons
 | 
					 | 
				
			||||||
            spacing: 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            property bool compact:
 | 
					 | 
				
			||||||
                inviteRow.width <
 | 
					 | 
				
			||||||
                inviteAvatar.width + inviteLabel.implicitWidth +
 | 
					 | 
				
			||||||
                acceptButton.implicitWidth + declineButton.implicitWidth
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
             property int displayMode:
 | 
					 | 
				
			||||||
                 compact ? Button.IconOnly : Button.TextBesideIcon
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Base.HButton {
 | 
					 | 
				
			||||||
                id: acceptButton
 | 
					 | 
				
			||||||
                text: qsTr("Accept")
 | 
					 | 
				
			||||||
                iconName: "accept"
 | 
					 | 
				
			||||||
                icon.color: Qt.hsla(0.45, 0.9, 0.3, 1)
 | 
					 | 
				
			||||||
                display: inviteButtons.displayMode
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                Layout.maximumWidth: inviteButtons.compact ? height : -1
 | 
					 | 
				
			||||||
                Layout.fillHeight: true
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Base.HButton {
 | 
					 | 
				
			||||||
                id: declineButton
 | 
					 | 
				
			||||||
                text: qsTr("Decline")
 | 
					 | 
				
			||||||
                iconName: "decline"
 | 
					 | 
				
			||||||
                icon.color: Qt.hsla(0.95, 0.9, 0.35, 1)
 | 
					 | 
				
			||||||
                icon.width: 32
 | 
					 | 
				
			||||||
                display: inviteButtons.displayMode
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                Layout.maximumWidth: inviteButtons.compact ? height : -1
 | 
					 | 
				
			||||||
                Layout.fillHeight: true
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										25
									
								
								harmonyqml/components/chat/LeftBanner.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								harmonyqml/components/chat/LeftBanner.qml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import QtQuick 2.7
 | 
				
			||||||
 | 
					import QtQuick.Controls 2.2
 | 
				
			||||||
 | 
					import QtQuick.Layouts 1.4
 | 
				
			||||||
 | 
					import "../base" as Base
 | 
				
			||||||
 | 
					import "utils.js" as ChatJS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Banner {
 | 
				
			||||||
 | 
					    property var leftEvent: null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    avatarName: ChatJS.getLeftBannerAvatarName(leftEvent, chatPage.userId)
 | 
				
			||||||
 | 
					    labelText: ChatJS.getLeftBannerText(leftEvent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    buttonModel: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            text: "Rejoin",
 | 
				
			||||||
 | 
					            iconName: "join",
 | 
				
			||||||
 | 
					            iconColor: Qt.hsla(0.13, 0.9, 0.35, 1),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            text: "Forget",
 | 
				
			||||||
 | 
					            iconName: "trash_can",
 | 
				
			||||||
 | 
					            iconColor: Qt.hsla(0.95, 0.9, 0.35, 1),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,8 +9,6 @@ ColumnLayout {
 | 
				
			|||||||
    readonly property var roomInfo:
 | 
					    readonly property var roomInfo:
 | 
				
			||||||
        Backend.models.rooms.get(userId).getWhere("roomId", roomId)
 | 
					        Backend.models.rooms.get(userId).getWhere("roomId", roomId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    property bool isInvite: roomInfo.category === "Invites"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    id: chatPage
 | 
					    id: chatPage
 | 
				
			||||||
    spacing: 0
 | 
					    spacing: 0
 | 
				
			||||||
    onFocusChanged: sendBox.setFocus()
 | 
					    onFocusChanged: sendBox.setFocus()
 | 
				
			||||||
@@ -20,19 +18,22 @@ ColumnLayout {
 | 
				
			|||||||
        topic: roomInfo.topic
 | 
					        topic: roomInfo.topic
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    MessageList {}
 | 
					    MessageList {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    TypingUsersBar {}
 | 
					    TypingUsersBar {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    InviteOffer {
 | 
					    InviteBanner {
 | 
				
			||||||
        visible: isInvite
 | 
					        visible: roomInfo.category === "Invites"
 | 
				
			||||||
        inviter: roomInfo.inviter
 | 
					        inviter: roomInfo.inviter
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SendBox {
 | 
					    SendBox {
 | 
				
			||||||
        id: sendBox
 | 
					        id: sendBox
 | 
				
			||||||
        visible: ! isInvite
 | 
					        visible: roomInfo.category === "Rooms"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LeftBanner {
 | 
				
			||||||
 | 
					        visible: roomInfo.category === "Left"
 | 
				
			||||||
 | 
					        leftEvent: roomInfo.leftEvent
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,6 +151,51 @@ function getMemberEventText(dict) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getLeftBannerText(leftEvent) {
 | 
				
			||||||
 | 
					    if (! leftEvent) {
 | 
				
			||||||
 | 
					        return "You are not member of this room."
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(JSON.stringify(leftEvent, null, 4))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var info   = leftEvent.content
 | 
				
			||||||
 | 
					    var prev   = leftEvent.prev_content
 | 
				
			||||||
 | 
					    var reason = info.reason ? (" Reason: " + info.reason) : ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (leftEvent.state_key === leftEvent.sender) {
 | 
				
			||||||
 | 
					        return (prev && prev.membership === "invite" ?
 | 
				
			||||||
 | 
					                "You declined to join this room." : "You left the room.") +
 | 
				
			||||||
 | 
					               reason
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (info.membership)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var name = Backend.getUserDisplayName(leftEvent.sender, false).result()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return "<b>" + name + "</b> " +
 | 
				
			||||||
 | 
					           (info.membership == "ban" ?
 | 
				
			||||||
 | 
					            "banned you from the room." :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            prev && prev.membership === "invite" ?
 | 
				
			||||||
 | 
					            "canceled your invitation." :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            prev && prev.membership == "ban" ?
 | 
				
			||||||
 | 
					            "unbanned you from the room." :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            "kicked you out of the room.") +
 | 
				
			||||||
 | 
					           reason
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getLeftBannerAvatarName(leftEvent, accountId) {
 | 
				
			||||||
 | 
					    if (! leftEvent || leftEvent.state_key == leftEvent.sender) {
 | 
				
			||||||
 | 
					        return Backend.getUserDisplayName(accountId, false).result()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Backend.getUserDisplayName(leftEvent.sender, false).result()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getTypingUsersText(users, ourAccountId) {
 | 
					function getTypingUsersText(users, ourAccountId) {
 | 
				
			||||||
    var names = []
 | 
					    var names = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								harmonyqml/icons/join.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								harmonyqml/icons/join.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M16 9v-4l8 7-8 7v-4h-8v-6h8zm-2 10v-.083c-1.178.685-2.542 1.083-4 1.083-4.411 0-8-3.589-8-8s3.589-8 8-8c1.458 0 2.822.398 4 1.083v-2.245c-1.226-.536-2.577-.838-4-.838-5.522 0-10 4.477-10 10s4.478 10 10 10c1.423 0 2.774-.302 4-.838v-2.162z"/></svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 339 B  | 
							
								
								
									
										1
									
								
								harmonyqml/icons/trash_can.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								harmonyqml/icons/trash_can.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3 6v18h18v-18h-18zm5 14c0 .552-.448 1-1 1s-1-.448-1-1v-10c0-.552.448-1 1-1s1 .448 1 1v10zm5 0c0 .552-.448 1-1 1s-1-.448-1-1v-10c0-.552.448-1 1-1s1 .448 1 1v10zm5 0c0 .552-.448 1-1 1s-1-.448-1-1v-10c0-.552.448-1 1-1s1 .448 1 1v10zm4-18v2h-20v-2h5.711c.9 0 1.631-1.099 1.631-2h5.315c0 .901.73 2 1.631 2h5.712z"/></svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 409 B  | 
		Reference in New Issue
	
	Block a user