feat(Communities/Settings): Communities banner editing
Use the generic EditCroppedImagePanel for banner cropping - Depends on status-go IdentityImage banner extension fixes #5118
This commit is contained in:
parent
4b399d15e3
commit
9f633f0fcc
|
@ -378,6 +378,7 @@ proc editCommunity*(
|
|||
color: string,
|
||||
imageUrl: string,
|
||||
aX: int, aY: int, bX: int, bY: int,
|
||||
bannerJsonStr: string,
|
||||
historyArchiveSupportEnabled: bool,
|
||||
pinMessageAllMembersEnabled: bool) =
|
||||
self.communityService.editCommunity(
|
||||
|
@ -388,6 +389,7 @@ proc editCommunity*(
|
|||
color,
|
||||
imageUrl,
|
||||
aX, aY, bX, bY,
|
||||
bannerJsonStr,
|
||||
historyArchiveSupportEnabled,
|
||||
pinMessageAllMembersEnabled)
|
||||
|
||||
|
|
|
@ -243,7 +243,7 @@ method removeUserFromCommunity*(self: AccessInterface, pubKey: string) {.base.}
|
|||
method banUserFromCommunity*(self: AccessInterface, pubKey: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method editCommunity*(self: AccessInterface, name: string, description: string, access: int, color: string, imagePath: string, aX: int, aY: int, bX: int, bY: int, historyArchiveSupportEnabled: bool, pinMessageAllMembersEnabled: bool) {.base.} =
|
||||
method editCommunity*(self: AccessInterface, name: string, description: string, access: int, color: string, imagePath: string, aX: int, aY: int, bX: int, bY: int, bannerJsonData: string, historyArchiveSupportEnabled: bool, pinMessageAllMembersEnabled: bool) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method exportCommunity*(self: AccessInterface): string {.base.} =
|
||||
|
|
|
@ -727,9 +727,11 @@ method banUserFromCommunity*(self: Module, pubKey: string) =
|
|||
method editCommunity*(self: Module, name: string, description: string,
|
||||
access: int, color: string,
|
||||
imagePath: string,
|
||||
aX: int, aY: int, bX: int, bY: int, historyArchiveSupportEnabled: bool,
|
||||
aX: int, aY: int, bX: int, bY: int,
|
||||
bannerJsonStr: string,
|
||||
historyArchiveSupportEnabled: bool,
|
||||
pinMessageAllMembersEnabled: bool) =
|
||||
self.controller.editCommunity(name, description, access, color, imagePath, aX, aY, bX, bY, historyArchiveSupportEnabled, pinMessageAllMembersEnabled)
|
||||
self.controller.editCommunity(name, description, access, color, imagePath, aX, aY, bX, bY, bannerJsonStr, historyArchiveSupportEnabled, pinMessageAllMembersEnabled)
|
||||
|
||||
method exportCommunity*(self: Module): string =
|
||||
self.controller.exportCommunity()
|
||||
|
|
|
@ -244,8 +244,8 @@ QtObject:
|
|||
proc banUserFromCommunity*(self: View, pubKey: string) {.slot.} =
|
||||
self.delegate.banUserFromCommunity(pubKey)
|
||||
|
||||
proc editCommunity*(self: View, name: string, description: string, access: int, color: string, imagePath: string, aX: int, aY: int, bX: int, bY: int, historyArchiveSupportEnabled: bool, pinMessageAllMembersEnabled: bool) {.slot.} =
|
||||
self.delegate.editCommunity(name, description, access, color, imagePath, aX, aY, bX, bY, historyArchiveSupportEnabled, pinMessageAllMembersEnabled)
|
||||
proc editCommunity*(self: View, name: string, description: string, access: int, color: string, imagePath: string, aX: int, aY: int, bX: int, bY: int, bannerJsonData: string, historyArchiveSupportEnabled: bool, pinMessageAllMembersEnabled: bool) {.slot.} =
|
||||
self.delegate.editCommunity(name, description, access, color, imagePath, aX, aY, bX, bY, bannerJsonData, historyArchiveSupportEnabled, pinMessageAllMembersEnabled)
|
||||
|
||||
proc exportCommunity*(self: View): string {.slot.} =
|
||||
self.delegate.exportCommunity()
|
||||
|
|
|
@ -75,6 +75,7 @@ method getCommunityItem(self: Module, c: CommunityDto): SectionItem =
|
|||
c.admin,
|
||||
c.description,
|
||||
c.images.thumbnail,
|
||||
c.images.banner,
|
||||
icon = "",
|
||||
c.color,
|
||||
hasNotification = false,
|
||||
|
|
|
@ -197,6 +197,7 @@ proc createChannelGroupItem[T](self: Module[T], c: ChannelGroupDto): SectionItem
|
|||
c.admin,
|
||||
c.description,
|
||||
c.images.thumbnail,
|
||||
c.images.banner,
|
||||
icon = if (isCommunity): "" else: conf.CHAT_SECTION_ICON,
|
||||
c.color,
|
||||
hasNotification,
|
||||
|
|
|
@ -59,6 +59,12 @@ QtObject:
|
|||
QtProperty[string] image:
|
||||
read = getImage
|
||||
|
||||
proc getBannerImageData(self: ActiveSection): string {.slot.} =
|
||||
return self.item.bannerImageData
|
||||
|
||||
QtProperty[string] bannerImageData:
|
||||
read = getBannerImageData
|
||||
|
||||
proc getIcon(self: ActiveSection): string {.slot.} =
|
||||
return self.item.icon
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ type
|
|||
amISectionAdmin: bool
|
||||
description: string
|
||||
image: string
|
||||
bannerImageData: string
|
||||
icon: string
|
||||
color: string
|
||||
hasNotification: bool
|
||||
|
@ -44,6 +45,7 @@ proc initItem*(
|
|||
amISectionAdmin = false,
|
||||
description = "",
|
||||
image = "",
|
||||
bannerImageData = "",
|
||||
icon = "",
|
||||
color = "",
|
||||
hasNotification = false,
|
||||
|
@ -68,6 +70,7 @@ proc initItem*(
|
|||
result.amISectionAdmin = amISectionAdmin
|
||||
result.description = description
|
||||
result.image = image
|
||||
result.bannerImageData = bannerImageData
|
||||
result.icon = icon
|
||||
result.color = color
|
||||
result.hasNotification = hasNotification
|
||||
|
@ -99,6 +102,7 @@ proc `$`*(self: SectionItem): string =
|
|||
amISectionAdmin: {self.amISectionAdmin},
|
||||
description: {self.description},
|
||||
image: {self.image},
|
||||
bannerImageData: {self.bannerImageData},
|
||||
icon: {self.icon},
|
||||
color: {self.color},
|
||||
hasNotification: {self.hasNotification},
|
||||
|
@ -135,6 +139,9 @@ proc description*(self: SectionItem): string {.inline.} =
|
|||
proc image*(self: SectionItem): string {.inline.} =
|
||||
self.image
|
||||
|
||||
proc bannerImageData*(self: SectionItem): string {.inline.} =
|
||||
self.bannerImageData
|
||||
|
||||
proc icon*(self: SectionItem): string {.inline.} =
|
||||
self.icon
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ type
|
|||
AmISectionAdmin
|
||||
Description
|
||||
Image
|
||||
BannerImageData
|
||||
Icon
|
||||
Color
|
||||
HasNotification
|
||||
|
@ -72,6 +73,7 @@ QtObject:
|
|||
ModelRole.AmISectionAdmin.int: "amISectionAdmin",
|
||||
ModelRole.Description.int:"description",
|
||||
ModelRole.Image.int:"image",
|
||||
ModelRole.BannerImageData.int:"bannerImageData",
|
||||
ModelRole.Icon.int:"icon",
|
||||
ModelRole.Color.int:"color",
|
||||
ModelRole.HasNotification.int:"hasNotification",
|
||||
|
@ -114,6 +116,8 @@ QtObject:
|
|||
result = newQVariant(item.description)
|
||||
of ModelRole.Image:
|
||||
result = newQVariant(item.image)
|
||||
of ModelRole.BannerImageData:
|
||||
result = newQVariant(item.bannerImageData)
|
||||
of ModelRole.Icon:
|
||||
result = newQVariant(item.icon)
|
||||
of ModelRole.Color:
|
||||
|
@ -211,6 +215,7 @@ QtObject:
|
|||
ModelRole.Name.int,
|
||||
ModelRole.Description.int,
|
||||
ModelRole.Image.int,
|
||||
ModelRole.BannerImageData.int,
|
||||
ModelRole.Icon.int,
|
||||
ModelRole.Color.int,
|
||||
ModelRole.HasNotification.int,
|
||||
|
@ -312,6 +317,7 @@ QtObject:
|
|||
"amISectionAdmin": item.amISectionAdmin,
|
||||
"description": item.description,
|
||||
"image": item.image,
|
||||
"bannerImageData": item.bannerImageData,
|
||||
"icon": item.icon,
|
||||
"color": item.color,
|
||||
"hasNotification": item.hasNotification,
|
||||
|
|
|
@ -31,6 +31,7 @@ type
|
|||
Images* = object
|
||||
thumbnail*: string
|
||||
large*: string
|
||||
banner*: string
|
||||
|
||||
type ChatMember* = object
|
||||
id*: string
|
||||
|
@ -136,6 +137,10 @@ proc toImages*(jsonObj: JsonNode): Images =
|
|||
if(jsonObj.getProp("thumbnail", thumbnailObj)):
|
||||
discard thumbnailObj.getProp("uri", result.thumbnail)
|
||||
|
||||
var bannerObj: JsonNode
|
||||
if(jsonObj.getProp("banner", bannerObj)):
|
||||
discard bannerObj.getProp("uri", result.banner)
|
||||
|
||||
proc toCategory*(jsonObj: JsonNode): Category =
|
||||
result = Category()
|
||||
if (not jsonObj.getProp("category_id", result.id)):
|
||||
|
|
|
@ -572,6 +572,7 @@ QtObject:
|
|||
color: string,
|
||||
imageUrl: string,
|
||||
aX: int, aY: int, bX: int, bY: int,
|
||||
bannerJsonStr: string,
|
||||
historyArchiveSupportEnabled: bool,
|
||||
pinMessageAllMembersEnabled: bool) =
|
||||
try:
|
||||
|
@ -584,6 +585,7 @@ QtObject:
|
|||
color,
|
||||
image,
|
||||
aX, aY, bX, bY,
|
||||
bannerJsonStr,
|
||||
historyArchiveSupportEnabled,
|
||||
pinMessageAllMembersEnabled)
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ import json, strutils
|
|||
import core, utils
|
||||
import response_type
|
||||
|
||||
import interpret/cropped_image
|
||||
|
||||
export response_type
|
||||
|
||||
proc getJoinedComunities*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
|
@ -66,9 +68,11 @@ proc editCommunity*(
|
|||
aY: int,
|
||||
bX: int,
|
||||
bY: int,
|
||||
bannerJsonStr: string,
|
||||
historyArchiveSupportEnabled: bool,
|
||||
pinMessageAllMembersEnabled: bool
|
||||
): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let bannerImage = newCroppedImage(bannerJsonStr)
|
||||
result = callPrivateRPC("editCommunity".prefix, %*[{
|
||||
# TODO this will need to be renamed membership (small m)
|
||||
"CommunityID": communityId,
|
||||
|
@ -82,6 +86,7 @@ proc editCommunity*(
|
|||
"imageAy": aY,
|
||||
"imageBx": bX,
|
||||
"imageBy": bY,
|
||||
"banner": bannerImage,
|
||||
"historyArchiveSupportEnabled": historyArchiveSupportEnabled,
|
||||
"pinMessageAllMembersEnabled": pinMessageAllMembersEnabled
|
||||
}])
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import json
|
||||
|
||||
#[ Represent a part of an image
|
||||
image: path to the image
|
||||
x, y, width, height: crop rectangle in image coordinates
|
||||
]#
|
||||
type CroppedImage* = ref object
|
||||
imagePath*: string
|
||||
x*: int
|
||||
y*: int
|
||||
width*: int
|
||||
height*: int
|
||||
|
||||
proc newCroppedImage*(jsonStr: string): CroppedImage =
|
||||
let rootNode = parseJson(jsonStr)
|
||||
result = CroppedImage()
|
||||
result.imagePath = rootNode["imagePath"].getStr()
|
||||
let cropRect = rootNode["cropRect"]
|
||||
result.x = int(cropRect["x"].getFloat())
|
||||
result.y = int(cropRect["y"].getFloat())
|
||||
result.width = int(cropRect["width"].getFloat())
|
||||
result.height = int(cropRect["height"].getFloat())
|
|
@ -23,11 +23,15 @@ Flickable {
|
|||
|
||||
property alias name: nameInput.text
|
||||
property alias description: descriptionTextInput.text
|
||||
property alias image: addImageButton.selectedImage
|
||||
property alias logoImagePath: addImageButton.selectedImage
|
||||
property string logoImageData: ""
|
||||
readonly property alias imageAx: imageCropperModal.aX
|
||||
readonly property alias imageAy: imageCropperModal.aY
|
||||
readonly property alias imageBx: imageCropperModal.bX
|
||||
readonly property alias imageBy: imageCropperModal.bY
|
||||
property string bannerImageData: ""
|
||||
property alias bannerPath: bannerEditor.source
|
||||
property alias bannerCropRect: bannerEditor.cropRect
|
||||
property bool isCommunityHistoryArchiveSupportEnabled: false
|
||||
property alias historyArchiveSupportToggle: historyArchiveSupportToggle.checked
|
||||
|
||||
|
@ -91,12 +95,10 @@ Flickable {
|
|||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
StatusBaseText {
|
||||
//% "Thumbnail image"
|
||||
text: qsTrId("thumbnail-image")
|
||||
text: qsTr("Community logo")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
@ -121,11 +123,12 @@ Flickable {
|
|||
id: imageDialog
|
||||
title: qsTr("Please choose an image")
|
||||
folder: shortcuts.pictures
|
||||
nameFilters: [//% "Image files (*.jpg *.jpeg *.png)"
|
||||
qsTrId("image-files----jpg---jpeg---png-")]
|
||||
nameFilters: [qsTr("Image files (*.jpg *.jpeg *.png)")]
|
||||
onAccepted: {
|
||||
addImageButton.selectedImage = imageDialog.fileUrls[0]
|
||||
imageCropperModal.open()
|
||||
if(imageDialog.fileUrls.length > 0) {
|
||||
addImageButton.selectedImage = imageDialog.fileUrls[0]
|
||||
imageCropperModal.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,12 +138,14 @@ Flickable {
|
|||
width: parent.width
|
||||
height: parent.height
|
||||
radius: parent.width / 2
|
||||
visible: !!addImageButton.selectedImage
|
||||
visible: !!addImageButton.selectedImage || !!root.logoImageData
|
||||
|
||||
Image {
|
||||
id: imagePreview
|
||||
visible: !!addImageButton.selectedImage
|
||||
visible: !!addImageButton.selectedImage || !!root.logoImageData
|
||||
source: addImageButton.selectedImage
|
||||
? addImageButton.selectedImage
|
||||
: root.logoImageData
|
||||
fillMode: Image.PreserveAspectFit
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
@ -156,30 +161,10 @@ Flickable {
|
|||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: addImageCenter
|
||||
visible: !imagePreview.visible
|
||||
width: uploadText.width
|
||||
height: childrenRect.height
|
||||
NoImageUploadedPanel {
|
||||
anchors.centerIn: parent
|
||||
|
||||
SVGImage {
|
||||
id: imageImg
|
||||
source: Style.svg("images_icon")
|
||||
width: 20
|
||||
height: 18
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: uploadText
|
||||
//% "Upload"
|
||||
text: qsTrId("upload")
|
||||
anchors.top: imageImg.bottom
|
||||
anchors.topMargin: 5
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
visible: !imagePreview.visible
|
||||
}
|
||||
|
||||
StatusRoundButton {
|
||||
|
@ -206,6 +191,43 @@ Flickable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Banner
|
||||
//
|
||||
StatusBaseText {
|
||||
text: qsTr("Community banner")
|
||||
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
||||
EditCroppedImagePanel {
|
||||
id: bannerEditor
|
||||
|
||||
Layout.preferredWidth: 475
|
||||
Layout.preferredHeight: Layout.preferredWidth / aspectRatio
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
imageFileDialogTitle: qsTr("Choose an image for banner")
|
||||
title: qsTr("Community banner")
|
||||
acceptButtonText: qsTr("Make this my Community banner")
|
||||
|
||||
roundedImage: false
|
||||
aspectRatio: 375/184
|
||||
|
||||
dataImage: root.bannerImageData
|
||||
|
||||
NoImageUploadedPanel {
|
||||
anchors.centerIn: parent
|
||||
|
||||
visible: !bannerEditor.userSelectedImage && !root.bannerImageData
|
||||
showARHint: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
|
@ -10,23 +10,26 @@ import StatusQ.Components 0.1
|
|||
import "../../layouts"
|
||||
|
||||
/*! TODO: very confusing to be refactored
|
||||
The "API" properties are just for input purpose to and to track the inital state
|
||||
that will be used in evaluating the dirty flag. They should not be updated based
|
||||
on the user input. The final values are accessed through the \c item member of \c edit property
|
||||
|
||||
The "API" properties are just for input purposes and to track the initial state
|
||||
used in evaluating the dirty flag. They should not be updated based
|
||||
on user input. The final relevant values are accessed through the \c item member of \c edit property
|
||||
*/
|
||||
StackLayout {
|
||||
id: root
|
||||
|
||||
property string name
|
||||
property string description
|
||||
property string image
|
||||
property string logoImageData
|
||||
property string bannerImageData
|
||||
property rect bannerCropRect
|
||||
property color color
|
||||
property bool editable: false
|
||||
property bool owned: false
|
||||
property bool isCommunityHistoryArchiveSupportEnabled: false
|
||||
property bool historyArchiveSupportToggle: false
|
||||
|
||||
signal edited(Item item) // item containing edited fields (name, description, image, color)
|
||||
signal edited(Item item) // item containing edited fields (name, description, logoImagePath, bannerPath, bannerCropRect, color)
|
||||
|
||||
clip: true
|
||||
|
||||
|
@ -47,7 +50,7 @@ StackLayout {
|
|||
icon {
|
||||
width: 80
|
||||
height: 80
|
||||
isLetterIdenticon: !root.image
|
||||
isLetterIdenticon: !root.logoImageData
|
||||
color: root.color
|
||||
letterSize: width / 2.4
|
||||
}
|
||||
|
@ -55,7 +58,7 @@ StackLayout {
|
|||
image {
|
||||
width: 80
|
||||
height: 80
|
||||
source: root.image
|
||||
source: root.logoImageData
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,19 +135,24 @@ StackLayout {
|
|||
name: root.name
|
||||
description: root.description
|
||||
color: root.color
|
||||
image: root.image
|
||||
logoImageData: root.logoImageData
|
||||
bannerImageData: root.bannerImageData
|
||||
isCommunityHistoryArchiveSupportEnabled: root.isCommunityHistoryArchiveSupportEnabled
|
||||
historyArchiveSupportToggle: root.historyArchiveSupportToggle
|
||||
|
||||
Component.onCompleted: {
|
||||
editCommunityPage.dirty =
|
||||
Qt.binding(() => {
|
||||
return root.name != name ||
|
||||
root.description != description ||
|
||||
root.image != image ||
|
||||
root.color != color ||
|
||||
return root.name !== name ||
|
||||
root.description !== description ||
|
||||
logoImagePath.length > 0 ||
|
||||
root.color !== color ||
|
||||
bannerPath.length > 0 ||
|
||||
isValidRect(bannerCropRect) ||
|
||||
root.historyArchiveSupportToggle !== historyArchiveSupportToggle
|
||||
})
|
||||
|
||||
function isValidRect(r /*rect*/) { return r.width !== 0 && r.height !== 0 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,8 @@ StatusAppTwoPanelLayout {
|
|||
CommunityOverviewSettingsPanel {
|
||||
name: root.community.name
|
||||
description: root.community.description
|
||||
image: root.community.image
|
||||
logoImageData: root.community.image
|
||||
bannerImageData: root.community.bannerImageData
|
||||
color: root.community.color
|
||||
editable: root.community.amISectionAdmin
|
||||
isCommunityHistoryArchiveSupportEnabled: root.rootStore.isCommunityHistoryArchiveSupportEnabled
|
||||
|
@ -128,12 +129,13 @@ StatusAppTwoPanelLayout {
|
|||
Utils.filterXSS(item.description),
|
||||
root.community.access,
|
||||
item.color.toString().toUpperCase(),
|
||||
item.image === root.community.image ? "" : item.image,
|
||||
item.logoImagePath,
|
||||
item.imageAx,
|
||||
item.imageAy,
|
||||
item.imageBx,
|
||||
item.imageBy,
|
||||
root.rootStore.isCommunityHistoryArchiveSupportEnabled,
|
||||
JSON.stringify({imagePath: String(item.bannerPath).replace("file://", ""), cropRect: item.bannerCropRect}),
|
||||
item.historyArchiveSupportToggle,
|
||||
false /*TODO port the modal implementation*/
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 82550fca343f56fbfcb71cc416ebcd96bf5409c9
|
||||
Subproject commit 63e58ba0354448b67449053b4599b2974d398a11
|
Loading…
Reference in New Issue