diff --git a/.gitmodules b/.gitmodules
index 4d13bc0f27..c6dd287caa 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -52,3 +52,6 @@
[submodule "vendor/nim-metrics"]
path = vendor/nim-metrics
url = https://github.com/status-im/nim-metrics
+[submodule "vendor/QR-Code-generator"]
+ path = vendor/QR-Code-generator
+ url = https://github.com/status-im/QR-Code-generator
diff --git a/Makefile b/Makefile
index 861ac0bf06..e8354d3c52 100644
--- a/Makefile
+++ b/Makefile
@@ -150,9 +150,16 @@ $(STATUSGO): | deps
+ cd vendor/status-go && \
$(MAKE) statusgo-library $(HANDLE_OUTPUT)
-nim_status_client: | $(DOTHERSIDE) $(STATUSGO) deps
+QRCODEGEN := vendor/QR-Code-generator/c/libqrcodegen.a
+
+$(QRCODEGEN): | deps
+ echo -e $(BUILD_MSG) "QR-Code-generator"
+ + cd vendor/QR-Code-generator/c && \
+ $(MAKE)
+
+nim_status_client: | $(DOTHERSIDE) $(STATUSGO) $(QRCODEGEN) deps
echo -e $(BUILD_MSG) "$@" && \
- $(ENV_SCRIPT) nim c $(NIM_PARAMS) --passL:"$(STATUSGO)" --passL:"-lm" src/nim_status_client.nim
+ $(ENV_SCRIPT) nim c $(NIM_PARAMS) --passL:"$(STATUSGO)" --passL:"$(QRCODEGEN)" --passL:"-lm" src/nim_status_client.nim
_APPIMAGE_TOOL := appimagetool-x86_64.AppImage
APPIMAGE_TOOL := tmp/linux/tools/$(_APPIMAGE_TOOL)
diff --git a/src/app/profile/qrcode/qrcode.nim b/src/app/profile/qrcode/qrcode.nim
new file mode 100644
index 0000000000..21bd483b8f
--- /dev/null
+++ b/src/app/profile/qrcode/qrcode.nim
@@ -0,0 +1,21 @@
+import strformat
+import strutils
+import qrcodegen
+
+proc generateQRCodeSVG*(text: string, border: int = 0): string =
+ var qr0: array[0..qrcodegen_BUFFER_LEN_MAX, uint8]
+ var tempBuffer: array[0..qrcodegen_BUFFER_LEN_MAX, uint8]
+ let ok: bool = qrcodegen_encodeText(text, tempBuffer[0].addr, qr0[0].addr, qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
+ if not ok:
+ raise newException(Exception, "Error generating QR Code")
+ else:
+ var parts: seq[string] = @[]
+ let size = qrcodegen_getSize(qr0[0].addr);
+ for y in countup(0, size):
+ for x in countup(0, size):
+ if qrcodegen_getModule(qr0[0].addr, x.cint, y.cint):
+ parts.add(&"M{x + border},{y + border}h1v1h-1z")
+ let partsStr = parts.join(" ")
+ result = &""
+
+
diff --git a/src/app/profile/qrcode/qrcodegen.nim b/src/app/profile/qrcode/qrcodegen.nim
new file mode 100644
index 0000000000..72e6b88e5c
--- /dev/null
+++ b/src/app/profile/qrcode/qrcodegen.nim
@@ -0,0 +1,164 @@
+##
+## QR Code generator library (C)
+##
+## Copyright (c) Project Nayuki. (MIT License)
+## https://www.nayuki.io/page/qr-code-generator-library
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy of
+## this software and associated documentation files (the "Software"), to deal in
+## the Software without restriction, including without limitation the rights to
+## use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+## the Software, and to permit persons to whom the Software is furnished to do so,
+## subject to the following conditions:
+## - The above copyright notice and this permission notice shall be included in
+## all copies or substantial portions of the Software.
+## - The Software is provided "as is", without warranty of any kind, express or
+## implied, including but not limited to the warranties of merchantability,
+## fitness for a particular purpose and noninfringement. In no event shall the
+## authors or copyright holders be liable for any claim, damages or other
+## liability, whether in an action of contract, tort or otherwise, arising from,
+## out of or in connection with the Software or the use or other dealings in the
+## Software.
+##
+
+##
+## This library creates QR Code symbols, which is a type of two-dimension barcode.
+## Invented by Denso Wave and described in the ISO/IEC 18004 standard.
+## A QR Code structure is an immutable square grid of black and white cells.
+## The library provides functions to create a QR Code from text or binary data.
+## The library covers the QR Code Model 2 specification, supporting all versions (sizes)
+## from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
+##
+## Ways to create a QR Code object:
+## - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary().
+## - Low level: Custom-make the list of segments and call
+## qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced().
+## (Note that all ways require supplying the desired error correction level and various byte buffers.)
+##
+## ---- Enum and struct types----
+##
+## The error correction level in a QR Code symbol.
+##
+
+type
+ qrcodegen_Ecc* = enum ## Must be declared in ascending order of error protection
+ ## so that an internal qrcodegen function works properly
+ qrcodegen_Ecc_LOW = 0, ## The QR Code can tolerate about 7% erroneous codewords
+ qrcodegen_Ecc_MEDIUM, ## The QR Code can tolerate about 15% erroneous codewords
+ qrcodegen_Ecc_QUARTILE, ## The QR Code can tolerate about 25% erroneous codewords
+ qrcodegen_Ecc_HIGH ## The QR Code can tolerate about 30% erroneous codewords
+
+
+##
+## The mask pattern used in a QR Code symbol.
+##
+
+type
+ qrcodegen_Mask* = enum ## A special value to tell the QR Code encoder to
+ ## automatically select an appropriate mask pattern
+ qrcodegen_Mask_AUTO = -1, ## The eight actual mask patterns
+ qrcodegen_Mask_0 = 0, qrcodegen_Mask_1, qrcodegen_Mask_2, qrcodegen_Mask_3,
+ qrcodegen_Mask_4, qrcodegen_Mask_5, qrcodegen_Mask_6, qrcodegen_Mask_7
+
+
+##
+## Describes how a segment's data bits are interpreted.
+##
+
+type
+ qrcodegen_Mode* = enum
+ qrcodegen_Mode_NUMERIC = 0x00000001, qrcodegen_Mode_ALPHANUMERIC = 0x00000002,
+ qrcodegen_Mode_BYTE = 0x00000004, qrcodegen_Mode_ECI = 0x00000007,
+ qrcodegen_Mode_KANJI = 0x00000008
+
+
+##
+## A segment of character/binary/control data in a QR Code symbol.
+## The mid-level way to create a segment is to take the payload data
+## and call a factory function such as qrcodegen_makeNumeric().
+## The low-level way to create a segment is to custom-make the bit buffer
+## and initialize a qrcodegen_Segment struct with appropriate values.
+## Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
+## Any segment longer than this is meaningless for the purpose of generating QR Codes.
+## Moreover, the maximum allowed bit length is 32767 because
+## the largest QR Code (version 40) has 31329 modules.
+##
+
+type
+ qrcodegen_Segment* {.bycopy.} = object
+ mode*: qrcodegen_Mode ## The mode indicator of this segment.
+ ## The length of this segment's unencoded data. Measured in characters for
+ ## numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
+ ## Always zero or positive. Not the same as the data's bit length.
+ numChars*: cint ## The data bits of this segment, packed in bitwise big endian.
+ ## Can be null if the bit length is zero.
+ data*: ptr uint8 ## The number of valid data bits used in the buffer. Requires
+ ## 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8.
+ ## The character count (numChars) must agree with the mode and the bit buffer length.
+ bitLength*: cint
+
+
+## ---- Macro constants and functions ----
+
+const
+ qrcodegen_VERSION_MIN* = 1
+ qrcodegen_VERSION_MAX* = 40
+
+## Calculates the number of bytes needed to store any QR Code up to and including the given version number,
+## as a compile-time constant. For example, 'uint8 buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];'
+## can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16).
+## Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX.
+
+template qrcodegen_BUFFER_LEN_FOR_VERSION*(n: untyped): untyped =
+ ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) div 8 + 1)
+
+## The worst-case number of bytes needed to store one QR Code, up to and including
+## version 40. This value equals 3918, which is just under 4 kilobytes.
+## Use this more convenient value to avoid calculating tighter memory bounds for buffers.
+
+const
+ qrcodegen_BUFFER_LEN_MAX* = qrcodegen_BUFFER_LEN_FOR_VERSION(
+ qrcodegen_VERSION_MAX)
+
+## ---- Functions (high level) to generate QR Codes ----
+##
+## Encodes the given text string to a QR Code, returning true if encoding succeeded.
+## If the data is too long to fit in any version in the given range
+## at the given ECC level, then false is returned.
+## - The input text must be encoded in UTF-8 and contain no NULs.
+## - The variables ecl and mask must correspond to enum constant values.
+## - Requires 1 <= minVersion <= maxVersion <= 40.
+## - The arrays tempBuffer and qrcode must each have a length
+## of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion).
+## - After the function returns, tempBuffer contains no useful data.
+## - If successful, the resulting QR Code may use numeric,
+## alphanumeric, or byte mode to encode the text.
+## - In the most optimistic case, a QR Code at version 40 with low ECC
+## can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string
+## up to 4296 characters, or any digit string up to 7089 characters.
+## These numbers represent the hard upper limit of the QR Code standard.
+## - Please consult the QR Code specification for information on
+## data capacities per version, ECC level, and text encoding mode.
+##
+
+proc qrcodegen_encodeText*(text: cstring; tempBuffer: ptr uint8;
+ qrcode: ptr uint8; ecl: qrcodegen_Ecc; minVersion: cint;
+ maxVersion: cint; mask: qrcodegen_Mask; boostEcl: bool): bool {.importc: "qrcodegen_encodeText".}
+
+#proc qrcodegen_makeEci*(assignVal: clong; buf: ptr uint8): qrcodegen_Segment
+## ---- Functions to extract raw data from QR Codes ----
+##
+## Returns the side length of the given QR Code, assuming that encoding succeeded.
+## The result is in the range [21, 177]. Note that the length of the array buffer
+## is related to the side length - every 'uint8 qrcode[]' must have length at least
+## qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1).
+##
+
+proc qrcodegen_getSize*(qrcode: ptr uint8): cint {.importc: "qrcodegen_getSize".}
+##
+## Returns the color of the module (pixel) at the given coordinates, which is false
+## for white or true for black. The top left corner has the coordinates (x=0, y=0).
+## If the given coordinates are out of bounds, then false (white) is returned.
+##
+
+proc qrcodegen_getModule*(qrcode: ptr uint8; x: cint; y: cint): bool {.importc: "qrcodegen_getModule".}
\ No newline at end of file
diff --git a/src/app/profile/view.nim b/src/app/profile/view.nim
index 5e63aaeada..d3343ea334 100644
--- a/src/app/profile/view.nim
+++ b/src/app/profile/view.nim
@@ -6,6 +6,7 @@ import ../../status/contacts as status_contacts
import ../../status/accounts as status_accounts
import ../../status/status
import ../../status/chat/chat
+import qrcode/qrcode
QtObject:
type ProfileView* = ref object of QObject
@@ -93,3 +94,6 @@ QtObject:
proc isAdded*(self: ProfileView, id: string): bool {.slot.} =
if id == "": return false
self.status.contacts.isAdded(id)
+
+ proc qrCode*(self: ProfileView, text:string): string {.slot.} =
+ result = "data:image/svg+xml;utf8," & generateQRCodeSVG(text, 2)
diff --git a/ui/app/AppLayouts/Chat/components/ProfilePopup.qml b/ui/app/AppLayouts/Chat/components/ProfilePopup.qml
index fd9da52f10..1dfa3211d5 100644
--- a/ui/app/AppLayouts/Chat/components/ProfilePopup.qml
+++ b/ui/app/AppLayouts/Chat/components/ProfilePopup.qml
@@ -10,8 +10,10 @@ ModalPopup {
property var identicon: ""
property var userName: ""
property var fromAuthor: ""
+ property bool showQR: false
function openPopup(userNameParam, fromAuthorParam, identiconParam) {
+ this.showQR = false
this.userName = userNameParam
this.fromAuthor = fromAuthorParam
this.identicon = identiconParam
@@ -63,114 +65,138 @@ ModalPopup {
font.pixelSize: 14
color: Theme.darkGrey
}
+
- // TODO(pascal): implement qrcode view
- // Rectangle {
- // id: qrCodeButton
- // height: 32
- // width: 32
- // anchors.top: parent.top
- // anchors.topMargin: Theme.padding
- // anchors.right: parent.right
- // anchors.rightMargin: 32 + Theme.smallPadding
- // radius: 8
+ Rectangle {
+ id: qrCodeButton
+ height: 32
+ width: 32
+ anchors.top: parent.top
+ anchors.topMargin: Theme.padding
+ anchors.right: parent.right
+ anchors.rightMargin: 32 + Theme.smallPadding
+ radius: 8
- // Image {
- // source: "../../../../shared/img/qr-code-icon.svg"
- // anchors.horizontalCenter: parent.horizontalCenter
- // anchors.verticalCenter: parent.verticalCenter
- // }
-
- // MouseArea {
- // cursorShape: Qt.PointingHandCursor
- // anchors.fill: parent
- // hoverEnabled: true
- // onExited: {
- // qrCodeButton.color = Theme.white
- // }
- // onEntered:{
- // qrCodeButton.color = Theme.grey
- // }
- // }
- // }
+ Image {
+ source: "../../../img/qr-code-icon.svg"
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ MouseArea {
+ cursorShape: Qt.PointingHandCursor
+ anchors.fill: parent
+ hoverEnabled: true
+ onExited: {
+ qrCodeButton.color = Theme.white
+ }
+ onEntered:{
+ qrCodeButton.color = Theme.grey
+ }
+ onClicked: {
+ showQR = true
+ }
+ }
+ }
}
- StyledText {
- id: labelEnsUsername
- text: qsTr("ENS username")
- font.pixelSize: 13
- font.weight: Font.Medium
- color: Theme.darkGrey
- anchors.left: parent.left
- anchors.leftMargin: Theme.smallPadding
- anchors.top: parent.top
- anchors.topMargin: Theme.smallPadding
+ Item {
+ anchors.fill: parent
+ visible: showQR
+ Image {
+ asynchronous: true
+ fillMode: QtQuick.Image.PreserveAspectFit
+ source: profileModel.qrCode(fromAuthor)
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ height: 212
+ width: 212
+ mipmap: true
+ smooth: false
+ }
}
- StyledText {
- id: valueEnsName
- text: "@emily.stateofus.eth"
- font.pixelSize: 14
- anchors.left: parent.left
- anchors.leftMargin: Theme.smallPadding
- anchors.top: labelEnsUsername.bottom
- anchors.topMargin: Theme.smallPadding
- }
+ Item {
+ anchors.fill: parent
+ visible: !showQR
- StyledText {
- id: labelChatKey
- text: qsTr("Chat key")
- font.pixelSize: 13
- font.weight: Font.Medium
- color: Theme.darkGrey
- anchors.left: parent.left
- anchors.leftMargin: Theme.smallPadding
- anchors.top: valueEnsName.bottom
- anchors.topMargin: Theme.padding
- }
+ StyledText {
+ id: labelEnsUsername
+ text: qsTr("ENS username")
+ font.pixelSize: 13
+ font.weight: Font.Medium
+ color: Theme.darkGrey
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.smallPadding
+ anchors.top: parent.top
+ anchors.topMargin: Theme.smallPadding
+ }
- StyledText {
- id: valueChatKey
- text: fromAuthor
- width: 160
- elide: Text.ElideMiddle
- font.pixelSize: 14
- anchors.left: parent.left
- anchors.leftMargin: Theme.smallPadding
- anchors.top: labelChatKey.bottom
- anchors.topMargin: Theme.smallPadding
- }
+ StyledText {
+ id: valueEnsName
+ text: "@emily.stateofus.eth"
+ font.pixelSize: 14
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.smallPadding
+ anchors.top: labelEnsUsername.bottom
+ anchors.topMargin: Theme.smallPadding
+ }
- Separator {
- id: separator
- anchors.top: valueChatKey.bottom
- anchors.topMargin: Theme.padding
- anchors.left: parent.left
- anchors.leftMargin: -Theme.padding
- anchors.right: parent.right
- anchors.rightMargin: -Theme.padding
- }
+ StyledText {
+ id: labelChatKey
+ text: qsTr("Chat key")
+ font.pixelSize: 13
+ font.weight: Font.Medium
+ color: Theme.darkGrey
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.smallPadding
+ anchors.top: valueEnsName.bottom
+ anchors.topMargin: Theme.padding
+ }
- StyledText {
- id: labelShareURL
- text: qsTr("Share Profile URL")
- font.pixelSize: 13
- font.weight: Font.Medium
- color: Theme.darkGrey
- anchors.left: parent.left
- anchors.leftMargin: Theme.smallPadding
- anchors.top: separator.bottom
- anchors.topMargin: Theme.padding
- }
+ StyledText {
+ id: valueChatKey
+ text: fromAuthor
+ width: 160
+ elide: Text.ElideMiddle
+ font.pixelSize: 14
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.smallPadding
+ anchors.top: labelChatKey.bottom
+ anchors.topMargin: Theme.smallPadding
+ }
- StyledText {
- id: valueShareURL
- text: "https://join.status.im/u/" + fromAuthor.substr(0, 4) + "..." + fromAuthor.substr(fromAuthor.length - 5)
- font.pixelSize: 14
- anchors.left: parent.left
- anchors.leftMargin: Theme.smallPadding
- anchors.top: labelShareURL.bottom
- anchors.topMargin: Theme.smallPadding
+ Separator {
+ id: separator
+ anchors.top: valueChatKey.bottom
+ anchors.topMargin: Theme.padding
+ anchors.left: parent.left
+ anchors.leftMargin: -Theme.padding
+ anchors.right: parent.right
+ anchors.rightMargin: -Theme.padding
+ }
+
+ StyledText {
+ id: labelShareURL
+ text: qsTr("Share Profile URL")
+ font.pixelSize: 13
+ font.weight: Font.Medium
+ color: Theme.darkGrey
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.smallPadding
+ anchors.top: separator.bottom
+ anchors.topMargin: Theme.padding
+ }
+
+ StyledText {
+ id: valueShareURL
+ text: "https://join.status.im/u/" + fromAuthor.substr(0, 4) + "..." + fromAuthor.substr(fromAuthor.length - 5)
+ font.pixelSize: 14
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.smallPadding
+ anchors.top: labelShareURL.bottom
+ anchors.topMargin: Theme.smallPadding
+ }
}
// TODO(pascal): implement copy to clipboard component
diff --git a/ui/app/img/qr-code-icon.svg b/ui/app/img/qr-code-icon.svg
new file mode 100644
index 0000000000..3ed5db298d
--- /dev/null
+++ b/ui/app/img/qr-code-icon.svg
@@ -0,0 +1,11 @@
+
diff --git a/vendor/QR-Code-generator b/vendor/QR-Code-generator
new file mode 160000
index 0000000000..6d8cc8bee9
--- /dev/null
+++ b/vendor/QR-Code-generator
@@ -0,0 +1 @@
+Subproject commit 6d8cc8bee9ca0f14985ca3b8564f5e2f86a8faec