Add account settings page
Display name change working
This commit is contained in:
parent
eeea0af4cd
commit
751a27157c
9
TODO.md
9
TODO.md
|
@ -1,4 +1,12 @@
|
||||||
|
- ElidedLabel component
|
||||||
|
- Can set `Layout.fillWidth: true` to elide/wrap
|
||||||
|
- Use childrenRect stuff
|
||||||
- Rename theme.bottomElementsHeight
|
- Rename theme.bottomElementsHeight
|
||||||
|
- Account delegate name color
|
||||||
|
- If avatar is set, name color from average color?
|
||||||
|
- normalSpacing in Theme
|
||||||
|
- Qt.AlignCenter instead of V | H
|
||||||
|
- banner button repair
|
||||||
|
|
||||||
- Qt 5.12
|
- Qt 5.12
|
||||||
- New input handlers
|
- New input handlers
|
||||||
|
@ -57,7 +65,6 @@
|
||||||
|
|
||||||
- Client improvements
|
- Client improvements
|
||||||
- [debug mode](https://docs.python.org/3/library/asyncio-dev.html)
|
- [debug mode](https://docs.python.org/3/library/asyncio-dev.html)
|
||||||
- More intelligent thumbnails downloading for different sizes
|
|
||||||
- Filtering rooms: search more than display names?
|
- Filtering rooms: search more than display names?
|
||||||
- Initial sync filter and lazy load, see weechat-matrix `_handle_login()`
|
- Initial sync filter and lazy load, see weechat-matrix `_handle_login()`
|
||||||
- See also `handle_response()`'s `keys_query` request
|
- See also `handle_response()`'s `keys_query` request
|
||||||
|
|
51
src/icons/cancel.svg
Normal file
51
src/icons/cancel.svg
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg14"
|
||||||
|
sodipodi:docname="invite_decline.svg"
|
||||||
|
inkscape:version="">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="640"
|
||||||
|
inkscape:window-height="480"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="9.8333333"
|
||||||
|
inkscape:cx="6.9152542"
|
||||||
|
inkscape:cy="17.084746"
|
||||||
|
inkscape:current-layer="svg14" />
|
||||||
|
<path
|
||||||
|
d="M23 20.168l-8.185-8.187 8.185-8.174-2.832-2.807-8.182 8.179-8.176-8.179-2.81 2.81 8.186 8.196-8.186 8.184 2.81 2.81 8.203-8.192 8.18 8.192z"
|
||||||
|
id="path12"
|
||||||
|
style="fill:#ab0938;fill-opacity:1" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
51
src/icons/save.svg
Normal file
51
src/icons/save.svg
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="invite_accept.svg"
|
||||||
|
inkscape:version="">
|
||||||
|
<metadata
|
||||||
|
id="metadata10">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs8" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="640"
|
||||||
|
inkscape:window-height="480"
|
||||||
|
id="namedview6"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="9.8333333"
|
||||||
|
inkscape:cx="-28.271186"
|
||||||
|
inkscape:cy="12"
|
||||||
|
inkscape:current-layer="svg4" />
|
||||||
|
<path
|
||||||
|
d="M9 21.035l-9-8.638 2.791-2.87 6.156 5.874 12.21-12.436 2.843 2.817z"
|
||||||
|
id="path2"
|
||||||
|
style="fill:#0d8967;fill-opacity:1" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -17,35 +17,48 @@ import nio
|
||||||
import pyotherside
|
import pyotherside
|
||||||
from nio.api import ResizingMethod
|
from nio.api import ResizingMethod
|
||||||
|
|
||||||
from .app import App
|
|
||||||
|
|
||||||
Size = Tuple[int, int]
|
Size = Tuple[int, int]
|
||||||
ImageData = Tuple[bytearray, Size, int] # last int: pyotherside format enum
|
ImageData = Tuple[bytearray, Size, int] # last int: pyotherside format enum
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Thumbnail:
|
class Thumbnail:
|
||||||
provider: "ImageProvider" = field()
|
# pylint: disable=no-member
|
||||||
id: str = field()
|
provider: "ImageProvider" = field()
|
||||||
width: int = field()
|
mxc: str = field()
|
||||||
height: int = field()
|
width: int = field()
|
||||||
|
height: int = field()
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
self.id = re.sub(r"#auto$", "", self.id)
|
self.mxc = re.sub(r"#auto$", "", self.mxc)
|
||||||
|
|
||||||
if not re.match(r"^(crop|scale)/mxc://.+/.+", self.id):
|
if not re.match(r"^mxc://.+/.+", self.mxc):
|
||||||
raise ValueError(f"Invalid image ID: {self.id}")
|
raise ValueError(f"Invalid mxc URI: {self.mxc}")
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server_size(self) -> Tuple[int, int]:
|
||||||
|
# https://matrix.org/docs/spec/client_server/latest#thumbnails
|
||||||
|
|
||||||
|
if self.width > 640 or self.height > 480:
|
||||||
|
return (800, 600)
|
||||||
|
|
||||||
|
if self.width > 320 or self.height > 240:
|
||||||
|
return (640, 480)
|
||||||
|
|
||||||
|
if self.width > 96 or self.height > 96:
|
||||||
|
return (320, 240)
|
||||||
|
|
||||||
|
if self.width > 32 or self.height > 32:
|
||||||
|
return (96, 96)
|
||||||
|
|
||||||
|
return (32, 32)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def resize_method(self) -> ResizingMethod:
|
def resize_method(self) -> ResizingMethod:
|
||||||
return ResizingMethod.crop \
|
return ResizingMethod.scale \
|
||||||
if self.id.startswith("crop/") else ResizingMethod.scale
|
if self.width > 96 or self.height > 96 else ResizingMethod.crop
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mxc(self) -> str:
|
|
||||||
return re.sub(r"^(crop|scale)/", "", self.id)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -55,11 +68,12 @@ class Thumbnail:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def local_path(self) -> Path:
|
def local_path(self) -> Path:
|
||||||
|
# pylint: disable=bad-string-format-type
|
||||||
parsed = urlparse(self.mxc)
|
parsed = urlparse(self.mxc)
|
||||||
name = "%s.%d.%d.%s" % (
|
name = "%s.%03d.%03d.%s" % (
|
||||||
parsed.path.lstrip("/"),
|
parsed.path.lstrip("/"),
|
||||||
self.width,
|
self.server_size[0],
|
||||||
self.height,
|
self.server_size[1],
|
||||||
self.resize_method.value,
|
self.resize_method.value,
|
||||||
)
|
)
|
||||||
return self.provider.cache / parsed.netloc / name
|
return self.provider.cache / parsed.netloc / name
|
||||||
|
@ -74,16 +88,16 @@ class Thumbnail:
|
||||||
response = await client.thumbnail(
|
response = await client.thumbnail(
|
||||||
server_name = parsed.netloc,
|
server_name = parsed.netloc,
|
||||||
media_id = parsed.path.lstrip("/"),
|
media_id = parsed.path.lstrip("/"),
|
||||||
width = self.width,
|
width = self.server_size[0],
|
||||||
height = self.height,
|
height = self.server_size[1],
|
||||||
method = self.resize_method,
|
method = self.resize_method,
|
||||||
)
|
)
|
||||||
body = response.body
|
body = response.body
|
||||||
|
|
||||||
if response.content_type not in ("image/jpeg", "image/png"):
|
if response.content_type not in ("image/jpeg", "image/png"):
|
||||||
with BytesIO(body) as in_, BytesIO() as out:
|
with BytesIO(body) as img_in, BytesIO() as img_out:
|
||||||
PILImage.open(in_).save(out, "PNG")
|
PILImage.open(img_in).save(img_out, "PNG")
|
||||||
body = out.getvalue()
|
body = img_out.getvalue()
|
||||||
|
|
||||||
self.local_path.parent.mkdir(parents=True, exist_ok=True)
|
self.local_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
@ -99,8 +113,10 @@ class Thumbnail:
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
body = await self.download()
|
body = await self.download()
|
||||||
|
|
||||||
size = (self.width, self.height)
|
with BytesIO(body) as img_in:
|
||||||
return (bytearray(body), size , pyotherside.format_data)
|
real_size = PILImage.open(img_in).size
|
||||||
|
|
||||||
|
return (bytearray(body), real_size, pyotherside.format_data)
|
||||||
|
|
||||||
|
|
||||||
class ImageProvider:
|
class ImageProvider:
|
||||||
|
@ -112,10 +128,10 @@ class ImageProvider:
|
||||||
|
|
||||||
|
|
||||||
def get(self, image_id: str, requested_size: Size) -> ImageData:
|
def get(self, image_id: str, requested_size: Size) -> ImageData:
|
||||||
width = 128 if requested_size[0] < 1 else requested_size[0]
|
if requested_size[0] < 1 or requested_size[1] < 1:
|
||||||
height = width if requested_size[1] < 1 else requested_size[1]
|
raise ValueError(f"width or height < 1: {requested_size!r}")
|
||||||
thumb = Thumbnail(self, image_id, width, height)
|
|
||||||
|
|
||||||
return asyncio.run_coroutine_threadsafe(
|
return asyncio.run_coroutine_threadsafe(
|
||||||
thumb.get_data(), self.app.loop
|
Thumbnail(self, image_id, *requested_size).get_data(),
|
||||||
|
self.app.loop
|
||||||
).result()
|
).result()
|
||||||
|
|
|
@ -7,11 +7,14 @@ import "../Base"
|
||||||
import "../utils.js" as Utils
|
import "../utils.js" as Utils
|
||||||
|
|
||||||
HRectangle {
|
HRectangle {
|
||||||
|
id: avatar
|
||||||
|
implicitWidth: theme.avatar.size
|
||||||
|
implicitHeight: theme.avatar.size
|
||||||
|
|
||||||
property string name: ""
|
property string name: ""
|
||||||
property var imageUrl: null
|
property var imageUrl: null
|
||||||
property var toolTipImageUrl: imageUrl
|
property var toolTipImageUrl: imageUrl
|
||||||
property int dimension: theme.avatar.size
|
property alias fillMode: avatarImage.fillMode
|
||||||
property bool hidden: false
|
|
||||||
|
|
||||||
onImageUrlChanged: if (imageUrl) { avatarImage.source = imageUrl }
|
onImageUrlChanged: if (imageUrl) { avatarImage.source = imageUrl }
|
||||||
|
|
||||||
|
@ -19,19 +22,16 @@ HRectangle {
|
||||||
avatarToolTipImage.source = toolTipImageUrl
|
avatarToolTipImage.source = toolTipImageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
width: dimension
|
readonly property var params: Utils.thumbnailParametersFor(width, height)
|
||||||
height: hidden ? 1 : dimension
|
|
||||||
implicitWidth: dimension
|
|
||||||
implicitHeight: hidden ? 1 : dimension
|
|
||||||
|
|
||||||
opacity: hidden ? 0 : 1
|
color: imageUrl ? "transparent" :
|
||||||
|
name ? Utils.avatarColor(name) :
|
||||||
color: name ? Utils.avatarColor(name) : theme.avatar.background.unknown
|
theme.avatar.background.unknown
|
||||||
|
|
||||||
HLabel {
|
HLabel {
|
||||||
z: 1
|
z: 1
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: ! hidden && ! imageUrl
|
visible: ! imageUrl
|
||||||
|
|
||||||
text: name ? name.charAt(0) : "?"
|
text: name ? name.charAt(0) : "?"
|
||||||
color: theme.avatar.letter
|
color: theme.avatar.letter
|
||||||
|
@ -39,14 +39,13 @@ HRectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
HImage {
|
HImage {
|
||||||
z: 2
|
|
||||||
id: avatarImage
|
id: avatarImage
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: ! hidden && imageUrl
|
visible: imageUrl
|
||||||
fillMode: Image.PreserveAspectCrop
|
z: 2
|
||||||
|
sourceSize.width: params.width
|
||||||
sourceSize.width: dimension
|
sourceSize.height: params.height
|
||||||
sourceSize.height: dimension
|
fillMode: params.fillMode
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hoverHandler
|
id: hoverHandler
|
||||||
|
@ -54,16 +53,17 @@ HRectangle {
|
||||||
|
|
||||||
HToolTip {
|
HToolTip {
|
||||||
id: avatarToolTip
|
id: avatarToolTip
|
||||||
visible: hoverHandler.hovered
|
visible: toolTipImageUrl && hoverHandler.hovered
|
||||||
width: 128
|
width: 128
|
||||||
height: 128
|
height: 128
|
||||||
|
|
||||||
HImage {
|
HImage {
|
||||||
id: avatarToolTipImage
|
id: avatarToolTipImage
|
||||||
sourceSize.width: avatarToolTip.width
|
width: parent.width
|
||||||
sourceSize.height: avatarToolTip.height
|
height: parent.height
|
||||||
width: sourceSize.width
|
sourceSize.width: parent.width
|
||||||
height: sourceSize.height
|
sourceSize.height: parent.height
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,10 @@ Button {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: buttonBackground
|
id: buttonBackground
|
||||||
color: Qt.lighter(
|
color: Qt.lighter(
|
||||||
backgroundColor, checked ? (checkedLightens ? 1.3 : 0.7) : 1.0
|
backgroundColor,
|
||||||
|
! enabled ? 0.7 :
|
||||||
|
checked ? (checkedLightens ? 1.3 : 0.7) :
|
||||||
|
1.0
|
||||||
)
|
)
|
||||||
radius: circle ? height : 0
|
radius: circle ? height : 0
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// This file is part of harmonyqml, licensed under LGPLv3.
|
// This file is part of harmonyqml, licensed under LGPLv3.
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
|
14
src/qml/Base/HGridLayout.qml
Normal file
14
src/qml/Base/HGridLayout.qml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2019 miruka
|
||||||
|
// This file is part of harmonyqml, licensed under LGPLv3.
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: gridLayout
|
||||||
|
rowSpacing: 0
|
||||||
|
columnSpacing: 0
|
||||||
|
|
||||||
|
property int totalSpacing:
|
||||||
|
spacing * Math.max(0, (gridLayout.visibleChildren.length - 1))
|
||||||
|
}
|
23
src/qml/Base/HLabeledTextField.qml
Normal file
23
src/qml/Base/HLabeledTextField.qml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2019 miruka
|
||||||
|
// This file is part of harmonyqml, licensed under LGPLv3.
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
property alias label: fieldLabel
|
||||||
|
property alias field: textField
|
||||||
|
|
||||||
|
HLabel {
|
||||||
|
id: fieldLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
HTextField {
|
||||||
|
id: textField
|
||||||
|
bordered: true
|
||||||
|
radius: 2
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,25 +4,14 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
|
|
||||||
HLabel {
|
HLabel {
|
||||||
|
// https://blog.shantanu.io/2015/02/15/creating-working-hyperlinks-in-qtquick-text/
|
||||||
id: label
|
id: label
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
acceptedButtons: Qt.NoButton
|
||||||
propagateComposedEvents: true
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
|
||||||
onPositionChanged: function (mouse) {
|
|
||||||
mouse.accepted = false
|
|
||||||
cursorShape = label.linkAt(mouse.x, mouse.y) ?
|
|
||||||
Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: function(mouse) {
|
|
||||||
var link = label.linkAt(mouse.x, mouse.y)
|
|
||||||
mouse.accepted = Boolean(link)
|
|
||||||
if (link) { Qt.openUrlExternally(link) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,8 @@ HAvatar {
|
||||||
dname[0] == "#" && dname.length > 1 ? dname.substring(1) : dname
|
dname[0] == "#" && dname.length > 1 ? dname.substring(1) : dname
|
||||||
|
|
||||||
imageUrl:
|
imageUrl:
|
||||||
roomInfo.avatarUrl ?
|
roomInfo.avatarUrl ? ("image://python/" + roomInfo.avatarUrl) : null
|
||||||
("image://python/crop/" + roomInfo.avatarUrl) : null
|
|
||||||
|
|
||||||
toolTipImageUrl:
|
toolTipImageUrl:
|
||||||
roomInfo.avatarUrl ?
|
roomInfo.avatarUrl ? ("image://python/" + roomInfo.avatarUrl) : null
|
||||||
("image://python/scale/" + roomInfo.avatarUrl) : null
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
|
property bool bordered: false
|
||||||
property alias backgroundColor: textFieldBackground.color
|
property alias backgroundColor: textFieldBackground.color
|
||||||
|
property alias radius: textFieldBackground.radius
|
||||||
|
|
||||||
font.family: theme.fontFamily.sans
|
font.family: theme.fontFamily.sans
|
||||||
font.pixelSize: theme.fontSize.normal
|
font.pixelSize: theme.fontSize.normal
|
||||||
|
@ -14,6 +16,8 @@ TextField {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: textFieldBackground
|
id: textFieldBackground
|
||||||
color: theme.controls.textField.background
|
color: theme.controls.textField.background
|
||||||
|
border.color: theme.controls.textField.borderColor
|
||||||
|
border.width: bordered ? theme.controls.textField.borderWidth : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
HBaseButton {
|
HBaseButton {
|
||||||
property int horizontalMargin: 0
|
property int horizontalMargin: 0
|
||||||
|
@ -14,6 +15,7 @@ HBaseButton {
|
||||||
property var iconTransform: null
|
property var iconTransform: null
|
||||||
|
|
||||||
property int fontSize: theme.fontSize.normal
|
property int fontSize: theme.fontSize.normal
|
||||||
|
property bool centerText: Boolean(iconName)
|
||||||
|
|
||||||
property bool loading: false
|
property bool loading: false
|
||||||
|
|
||||||
|
@ -29,7 +31,7 @@ HBaseButton {
|
||||||
|
|
||||||
HRowLayout {
|
HRowLayout {
|
||||||
id: contentLayout
|
id: contentLayout
|
||||||
spacing: button.text && iconName ? 5 : 0
|
spacing: button.text && iconName ? 8 : 0
|
||||||
Component.onCompleted: contentWidth = implicitWidth
|
Component.onCompleted: contentWidth = implicitWidth
|
||||||
|
|
||||||
HIcon {
|
HIcon {
|
||||||
|
@ -41,15 +43,25 @@ HBaseButton {
|
||||||
Layout.bottomMargin: verticalMargin
|
Layout.bottomMargin: verticalMargin
|
||||||
Layout.leftMargin: horizontalMargin
|
Layout.leftMargin: horizontalMargin
|
||||||
Layout.rightMargin: horizontalMargin
|
Layout.rightMargin: horizontalMargin
|
||||||
|
|
||||||
|
// Colorize {
|
||||||
|
// anchors.fill: parent
|
||||||
|
// source: parent
|
||||||
|
// visible: ! button.enabled
|
||||||
|
// saturation: 0
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
HLabel {
|
HLabel {
|
||||||
text: button.text
|
text: button.text
|
||||||
font.pixelSize: fontSize
|
font.pixelSize: fontSize
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: button.centerText ?
|
||||||
|
Text.AlignHCenter : Text.AlignLeft
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: enabled ?
|
||||||
|
theme.colors.foreground : theme.colors.foregroundDim2
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,16 @@ import QtQuick 2.12
|
||||||
|
|
||||||
HAvatar {
|
HAvatar {
|
||||||
property string userId: ""
|
property string userId: ""
|
||||||
|
|
||||||
readonly property var userInfo: userId ? users.find(userId) : ({})
|
readonly property var userInfo: userId ? users.find(userId) : ({})
|
||||||
|
|
||||||
name:
|
name:
|
||||||
userInfo.displayName || userId.substring(1) // no leading @
|
userInfo.displayName || userId.substring(1) // no leading @
|
||||||
|
|
||||||
imageUrl:
|
imageUrl:
|
||||||
userInfo.avatarUrl ?
|
userInfo.avatarUrl ? ("image://python/" + userInfo.avatarUrl) : null
|
||||||
("image://python/crop/" + userInfo.avatarUrl) : null
|
|
||||||
|
|
||||||
toolTipImageUrl:
|
toolTipImageUrl:
|
||||||
userInfo.avatarUrl ?
|
userInfo.avatarUrl ? ("image://python/" + userInfo.avatarUrl) : null
|
||||||
("image://python/scale/" + userInfo.avatarUrl) : null
|
|
||||||
|
|
||||||
//HImage {
|
//HImage {
|
||||||
//id: status
|
//id: status
|
||||||
|
|
|
@ -22,7 +22,6 @@ HRectangle {
|
||||||
|
|
||||||
HUserAvatar {
|
HUserAvatar {
|
||||||
id: bannerAvatar
|
id: bannerAvatar
|
||||||
dimension: banner.Layout.preferredHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HIcon {
|
HIcon {
|
||||||
|
|
|
@ -26,7 +26,6 @@ HRectangle {
|
||||||
HRoomAvatar {
|
HRoomAvatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
roomId: chatPage.roomId
|
roomId: chatPage.roomId
|
||||||
dimension: roomHeader.height
|
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ HRectangle {
|
||||||
HUserAvatar {
|
HUserAvatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
userId: chatPage.userId
|
userId: chatPage.userId
|
||||||
dimension: sendBox.Layout.minimumHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HScrollableTextArea {
|
HScrollableTextArea {
|
||||||
|
|
|
@ -13,9 +13,10 @@ Row {
|
||||||
|
|
||||||
HUserAvatar {
|
HUserAvatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
hidden: combine
|
|
||||||
userId: model.senderId
|
userId: model.senderId
|
||||||
dimension: model.showNameLine ? 48 : 28
|
width: model.showNameLine ? 48 : 28
|
||||||
|
height: combine ? 1 : model.showNameLine ? 48 : 28
|
||||||
|
opacity: combine ? 0 : 1
|
||||||
visible: ! isOwn
|
visible: ! isOwn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ Row {
|
||||||
|
|
||||||
//width: nameLabel.implicitWidth
|
//width: nameLabel.implicitWidth
|
||||||
width: Math.min(
|
width: Math.min(
|
||||||
roomEventListView.width - avatar.width - messageContent.spacing,
|
eventList.width - avatar.width - messageContent.spacing,
|
||||||
theme.fontSize.normal * 0.5 * 75, // 600 with 16px font
|
theme.fontSize.normal * 0.5 * 75, // 600 with 16px font
|
||||||
Math.max(
|
Math.max(
|
||||||
nameLabel.visible ? nameLabel.implicitWidth : 0,
|
nameLabel.visible ? nameLabel.implicitWidth : 0,
|
||||||
|
|
|
@ -16,8 +16,8 @@ Column {
|
||||||
function getPreviousItem(nth) {
|
function getPreviousItem(nth) {
|
||||||
// Remember, index 0 = newest bottomest message
|
// Remember, index 0 = newest bottomest message
|
||||||
nth = nth || 1
|
nth = nth || 1
|
||||||
return roomEventListView.model.count - 1 > model.index + nth ?
|
return eventList.model.count - 1 > model.index + nth ?
|
||||||
roomEventListView.model.get(model.index + nth) : null
|
eventList.model.get(model.index + nth) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
property var previousItem: getPreviousItem()
|
property var previousItem: getPreviousItem()
|
||||||
|
@ -60,7 +60,7 @@ Column {
|
||||||
property int verticalPadding: 4
|
property int verticalPadding: 4
|
||||||
|
|
||||||
ListView.onAdd: {
|
ListView.onAdd: {
|
||||||
var nextDelegate = roomEventListView.contentItem.children[index]
|
var nextDelegate = eventList.contentItem.children[index]
|
||||||
if (nextDelegate) { nextDelegate.reloadPreviousItem() }
|
if (nextDelegate) { nextDelegate.reloadPreviousItem() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@ import SortFilterProxyModel 0.2
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
|
|
||||||
HRectangle {
|
HRectangle {
|
||||||
property alias listView: roomEventListView
|
property alias listView: eventList
|
||||||
|
|
||||||
property int space: 8
|
property int space: 8
|
||||||
|
|
||||||
color: theme.chat.roomEventList.background
|
color: theme.chat.eventList.background
|
||||||
|
|
||||||
HListView {
|
HListView {
|
||||||
id: roomEventListView
|
id: eventList
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
model: HListModel {
|
model: HListModel {
|
||||||
|
@ -48,12 +48,12 @@ HRectangle {
|
||||||
if (chatPage.category != "Invites" && canLoad && yPos <= 0.1) {
|
if (chatPage.category != "Invites" && canLoad && yPos <= 0.1) {
|
||||||
zz += 1
|
zz += 1
|
||||||
print(canLoad, zz)
|
print(canLoad, zz)
|
||||||
canLoad = false
|
eventList.canLoad = false
|
||||||
py.callClientCoro(
|
py.callClientCoro(
|
||||||
chatPage.userId,
|
chatPage.userId,
|
||||||
"load_past_events",
|
"load_past_events",
|
||||||
[chatPage.roomId],
|
[chatPage.roomId],
|
||||||
function(more_to_load) { canLoad = more_to_load }
|
function(more_to_load) { eventList.canLoad = more_to_load }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ HRectangle {
|
||||||
HNoticePage {
|
HNoticePage {
|
||||||
text: qsTr("Nothing to show here yet...")
|
text: qsTr("Nothing to show here yet...")
|
||||||
|
|
||||||
visible: roomEventListView.model.count < 1
|
visible: eventList.model.count < 1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,6 @@ import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
import "../../utils.js" as Utils
|
||||||
|
|
||||||
HRectangle {
|
HLabel {
|
||||||
HLabel {
|
text: "Client - TODO"
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "Client - TODO"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,6 @@ import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
import "../../utils.js" as Utils
|
||||||
|
|
||||||
HRectangle {
|
HLabel {
|
||||||
HLabel {
|
text: "Devices - TODO"
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "Devices - TODO"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,66 +7,75 @@ import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
import "../../utils.js" as Utils
|
||||||
|
|
||||||
HRectangle {
|
Page {
|
||||||
|
id: editAccount
|
||||||
|
padding: currentSpacing < 8 ? 0 : currentSpacing
|
||||||
|
Behavior on padding { HNumberAnimation {} }
|
||||||
|
|
||||||
|
property bool wide: width > 414 + padding * 2
|
||||||
|
property int thinMaxWidth: 240
|
||||||
|
property int normalSpacing: 8
|
||||||
|
property int currentSpacing:
|
||||||
|
Math.min(normalSpacing * width / 400, normalSpacing * 2)
|
||||||
|
|
||||||
property string userId: ""
|
property string userId: ""
|
||||||
readonly property var userInfo: users.find(userId)
|
readonly property var userInfo: users.find(userId)
|
||||||
|
|
||||||
HColumnLayout {
|
header: HRectangle {
|
||||||
anchors.fill: parent
|
width: parent.width
|
||||||
|
height: theme.bottomElementsHeight
|
||||||
|
color: theme.pageHeadersBackground
|
||||||
|
|
||||||
HRowLayout {
|
HRowLayout {
|
||||||
Layout.preferredHeight: theme.bottomElementsHeight
|
width: parent.width
|
||||||
|
|
||||||
HLabel {
|
HLabel {
|
||||||
text: qsTr("Edit %1").arg(
|
text: qsTr("Account settings for %1").arg(
|
||||||
Utils.coloredNameHtml(userInfo.displayName, userId)
|
Utils.coloredNameHtml(userInfo.displayName, userId)
|
||||||
)
|
)
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
font.pixelSize: theme.fontSize.big
|
font.pixelSize: theme.fontSize.big
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
// visible: width > 50
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.leftMargin: currentSpacing
|
||||||
Layout.maximumWidth: parent.width - tabBar.width
|
|
||||||
Layout.leftMargin: 8
|
|
||||||
Layout.rightMargin: Layout.leftMargin
|
Layout.rightMargin: Layout.leftMargin
|
||||||
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
TabBar {
|
|
||||||
id: tabBar
|
|
||||||
currentIndex: swipeView.currentIndex
|
|
||||||
spacing: 0
|
|
||||||
contentHeight: parent.height
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: qsTr("Profile")
|
|
||||||
width: implicitWidth * 1.25
|
|
||||||
}
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: qsTr("Devices")
|
|
||||||
width: implicitWidth * 1.25
|
|
||||||
}
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: qsTr("Harmony")
|
|
||||||
width: implicitWidth * 1.25
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SwipeView {
|
|
||||||
id: swipeView
|
|
||||||
clip: true
|
|
||||||
currentIndex: tabBar.currentIndex
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Profile {}
|
|
||||||
Devices {}
|
|
||||||
ClientSettings {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background: null
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
HRectangle {
|
||||||
|
color: theme.box.background
|
||||||
|
// radius: theme.box.radius
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
|
Layout.preferredWidth: wide ? parent.width : thinMaxWidth
|
||||||
|
Layout.maximumWidth: Math.min(parent.width, 640)
|
||||||
|
|
||||||
|
Layout.preferredHeight: childrenRect.height
|
||||||
|
Layout.maximumHeight: parent.height
|
||||||
|
|
||||||
|
Profile { width: parent.width }
|
||||||
|
}
|
||||||
|
|
||||||
|
// HRectangle {
|
||||||
|
// color: theme.box.background
|
||||||
|
// radius: theme.box.radius
|
||||||
|
// ClientSettings { width: parent.width }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// HRectangle {
|
||||||
|
// color: theme.box.background
|
||||||
|
// radius: theme.box.radius
|
||||||
|
// Devices { width: parent.width }
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,93 @@ import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
import "../../utils.js" as Utils
|
||||||
|
|
||||||
HRectangle {
|
HGridLayout {
|
||||||
HLabel {
|
function applyChanges() {
|
||||||
anchors.centerIn: parent
|
saveButton.loading = true
|
||||||
text: "profile"
|
|
||||||
|
py.callClientCoro(
|
||||||
|
userId, "set_displayname", [nameField.field.text],
|
||||||
|
() => { saveButton.loading = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
columns: 2
|
||||||
|
flow: wide ? GridLayout.LeftToRight : GridLayout.TopToBottom
|
||||||
|
rowSpacing: currentSpacing
|
||||||
|
|
||||||
|
Component.onCompleted: nameField.field.forceActiveFocus()
|
||||||
|
|
||||||
|
HUserAvatar {
|
||||||
|
id: avatar
|
||||||
|
userId: editAccount.userId
|
||||||
|
toolTipImageUrl: null
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: wide ? 0 : currentSpacing
|
||||||
|
|
||||||
|
Layout.preferredWidth: thinMaxWidth
|
||||||
|
Layout.preferredHeight: Layout.preferredWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
id: profileInfo
|
||||||
|
spacing: normalSpacing
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
spacing: normalSpacing
|
||||||
|
Layout.margins: currentSpacing
|
||||||
|
|
||||||
|
HLabel {
|
||||||
|
text: qsTr("User ID:<br>%1")
|
||||||
|
.arg(Utils.coloredNameHtml(userId, userId))
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HLabeledTextField {
|
||||||
|
id: nameField
|
||||||
|
label.text: qsTr("Display name:")
|
||||||
|
field.text: userInfo.displayName
|
||||||
|
field.onAccepted: applyChanges()
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 480
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HSpacer {}
|
||||||
|
|
||||||
|
HRowLayout {
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
|
HUIButton {
|
||||||
|
id: saveButton
|
||||||
|
iconName: "save"
|
||||||
|
text: qsTr("Save")
|
||||||
|
centerText: false
|
||||||
|
enabled: nameField.field.text != userInfo.displayName
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
|
onClicked: applyChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
HUIButton {
|
||||||
|
iconName: "cancel"
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
centerText: false
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
enabled: saveButton.enabled && ! saveButton.loading
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
nameField.field.text = userInfo.displayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,18 +12,18 @@ Column {
|
||||||
property var userInfo: users.find(model.userId)
|
property var userInfo: users.find(model.userId)
|
||||||
property bool expanded: true
|
property bool expanded: true
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
onTapped: pageStack.showPage(
|
|
||||||
"EditAccount/EditAccount", { "userId": model.userId }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
HHighlightRectangle {
|
HHighlightRectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
|
||||||
normalColor: theme.sidePane.account.background
|
normalColor: theme.sidePane.account.background
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
onTapped: pageStack.showPage(
|
||||||
|
"EditAccount/EditAccount", { "userId": model.userId }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
HRowLayout {
|
HRowLayout {
|
||||||
id: row
|
id: row
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
|
@ -32,6 +32,7 @@ QtObject {
|
||||||
property color background2: Qt.hsla(0, 0, 0.9, 0.7)
|
property color background2: Qt.hsla(0, 0, 0.9, 0.7)
|
||||||
property color foreground: "black"
|
property color foreground: "black"
|
||||||
property color foregroundDim: Qt.hsla(0, 0, 0.2, 1)
|
property color foregroundDim: Qt.hsla(0, 0, 0.2, 1)
|
||||||
|
property color foregroundDim2: Qt.hsla(0, 0, 0.3, 1)
|
||||||
property color foregroundError: Qt.hsla(0.95, 0.64, 0.32, 1)
|
property color foregroundError: Qt.hsla(0.95, 0.64, 0.32, 1)
|
||||||
property color textBorder: Qt.hsla(0, 0, 0, 0.07)
|
property color textBorder: Qt.hsla(0, 0, 0, 0.07)
|
||||||
}
|
}
|
||||||
|
@ -50,6 +51,8 @@ QtObject {
|
||||||
|
|
||||||
property QtObject textField: QtObject {
|
property QtObject textField: QtObject {
|
||||||
property color background: colors.background2
|
property color background: colors.background2
|
||||||
|
property color borderColor: "black"
|
||||||
|
property int borderWidth: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
property QtObject textArea: QtObject {
|
property QtObject textArea: QtObject {
|
||||||
|
@ -82,7 +85,7 @@ QtObject {
|
||||||
property color background: colors.background2
|
property color background: colors.background2
|
||||||
}
|
}
|
||||||
|
|
||||||
property QtObject roomEventList: QtObject {
|
property QtObject eventList: QtObject {
|
||||||
property color background: "transparent"
|
property color background: "transparent"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +123,8 @@ QtObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property color pageHeadersBackground: colors.background2
|
||||||
|
|
||||||
property QtObject box: QtObject {
|
property QtObject box: QtObject {
|
||||||
property color background: colors.background0
|
property color background: colors.background0
|
||||||
property int radius: theme.radius
|
property int radius: theme.radius
|
||||||
|
|
|
@ -47,7 +47,7 @@ function nameColor(name) {
|
||||||
|
|
||||||
function coloredNameHtml(name, alt_id) {
|
function coloredNameHtml(name, alt_id) {
|
||||||
// substring: remove leading @
|
// substring: remove leading @
|
||||||
return "<font color='" + nameColor(name || alt_id.substring(1)) + "'>" +
|
return "<font color='" + nameColor(alt_id.substring(1)) + "'>" +
|
||||||
escapeHtml(name || alt_id) +
|
escapeHtml(name || alt_id) +
|
||||||
"</font>"
|
"</font>"
|
||||||
}
|
}
|
||||||
|
@ -106,3 +106,22 @@ function filterMatches(filter, text) {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function thumbnailParametersFor(width, height) {
|
||||||
|
// https://matrix.org/docs/spec/client_server/latest#thumbnails
|
||||||
|
|
||||||
|
if (width > 640 || height > 480)
|
||||||
|
return {width: 800, height: 600, fillMode: Image.PreserveAspectFit}
|
||||||
|
|
||||||
|
if (width > 320 || height > 240)
|
||||||
|
return {width: 640, height: 480, fillMode: Image.PreserveAspectFit}
|
||||||
|
|
||||||
|
if (width > 96 || height > 96)
|
||||||
|
return {width: 320, height: 240, fillMode: Image.PreserveAspectFit}
|
||||||
|
|
||||||
|
if (width > 32 || height > 32)
|
||||||
|
return {width: 96, height: 96, fillMode: Image.PreserveAspectCrop}
|
||||||
|
|
||||||
|
return {width: 32, height: 32, fillMode: Image.PreserveAspectCrop}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user