diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 94fd4bc..049b66c 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,3 +1,4 @@
github: [korapp]
ko_fi: korapp
-liberapay: korapp
\ No newline at end of file
+liberapay: korapp
+custom: ["revolut.me/korapp"]
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5134bbb..b6b378d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -14,7 +14,7 @@ jobs:
changelog: ${{ steps.tag_version.outputs.changelog }}
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Bump version and push tag
id: tag_version
@@ -30,7 +30,9 @@ jobs:
if: ${{ needs.bump.outputs.new_tag != null }}
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
+ with:
+ submodules: true
- name: Set env
run: |
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2e74cbc..1054471 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -6,11 +6,13 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install dependencies
- run: sudo apt install -y qtdeclarative5-dev-tools qml-module-qttest libxcb-xinerama0
+ run: |
+ sudo apt update
+ sudo apt install -y qtdeclarative5-dev-tools qml-module-qttest libxcb-xinerama0
- name: QmlTestRunner
run: |
export DISPLAY=:99
- sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
+ Xvfb :99 &
qmltestrunner
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..358c483
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "package/contents/lib/secrets"]
+ path = package/contents/lib/secrets
+ url = https://github.com/korapp/plasma-lib-secrets
diff --git a/README.md b/README.md
index 7b43009..70cbafc 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,8 @@
-# Innoreader Plasmoid
+# Inoreader Plasmoid
+
+[![plasma](https://img.shields.io/static/v1?message=KDE%20Store&color=54a3d8&logo=kde&logoColor=FFFFFF&label=)][kdestore]
+[![downloads](https://img.shields.io/github/downloads/korapp/plasma-inoreader/total)][releases]
+[![release](https://img.shields.io/github/v/release/korapp/plasma-inoreader)][releases]
Plasma applet for [Inoreader](https://innoreader.com).
@@ -27,7 +31,7 @@ The preferred and easiest way to install is to use Plasma Discover or KDE Get Ne
### From file
-Download the latest version of plasmoid from [KDE Store](https://store.kde.org/p/1829436/) or [release page](https://github.com/korapp/plasma-inoreader/releases)
+Download the latest version of plasmoid from [KDE Store][kdestore] or [release page][releases]
#### A) Plasma UI
@@ -46,7 +50,7 @@ plasmapkg2 -i plasma-inoreader-*.plasmoid
Clone repository and go to the project directory
```sh
-git clone https://github.com/korapp/plasma-inoreader.git
+git clone --recurse-submodules https://github.com/korapp/plasma-inoreader.git
cd plasma-inoreader
```
@@ -62,3 +66,7 @@ Say thank you with coffee ☕ if you'd like.
[![liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/korapp/donate)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/korapp)
+[](https://revolut.me/korapp)
+
+[kdestore]: https://store.kde.org/p/1829436/
+[releases]: https://github.com/korapp/plasma-inoreader/releases
\ No newline at end of file
diff --git a/package/contents/code/qByteArray.mjs b/package/contents/code/qByteArray.mjs
deleted file mode 100644
index 52850b9..0000000
--- a/package/contents/code/qByteArray.mjs
+++ /dev/null
@@ -1,100 +0,0 @@
-export function parseBytes(string) {
- if (!string) {
- return new Uint8Array()
- }
- return Uint8Array.from(string.match(/[0-9a-f]{2}/g).map(n => Number.parseInt(n, 16)))
-}
-
-export function bytesToString(bytes, offset = 0, length = bytes.length) {
- let result = ""
- for(let i = offset; i < length; i+= 2) {
- result += String.fromCharCode(bytes[i] << 8 | bytes[i+1])
- }
- return result
-}
-
-export function stringToBytes(str, bytes = new Uint8Array(str.length * 2), offset = 0) {
- for (let i = 0; i < str.length; i++) {
- const c = str.charCodeAt(i), b = 2 * i + offset
- bytes[b] = c >>> 8
- bytes[b+1] = c
- }
- return bytes
-}
-
-export function qBytesToMap(bytes) {
- const length = bytes.length
- const dv = new DataView(bytes.buffer)
- const output = {}
- let entryCount = dv.getUint32() * 2
- let key
- let pointer = 4
- while (entryCount--) {
- const counter = dv.getUint32(pointer)
- const start = pointer + 4
- const string = bytesToString(bytes, start, start + counter)
- pointer = start + counter
- if (entryCount % 2) {
- key = string
- } else {
- output[key] = string
- }
- }
- return output
-}
-
-export function mapToQBytes(map) {
- const object = stringifyValues(map)
- const entriesBytesCount = qMapSize(object)
- const bytes = new Uint8Array(entriesBytesCount)
- const dv = new DataView(bytes.buffer)
- let entryCount = 0
- let pointer = 4
- for(const k in object) {
- dv.setInt32(pointer, k.length * 2)
- pointer += 4
- stringToBytes(k, bytes, pointer)
- pointer += k.length * 2
- dv.setInt32(pointer, object[k].length * 2)
- pointer += 4
- stringToBytes(object[k], bytes, pointer)
- pointer += object[k].length * 2
- entryCount++
- }
- dv.setInt32(0, entryCount)
- return bytes
-}
-
-export function stringToQBytes(str) {
- const size = str.length * 2
- const bytes = new Uint8Array(size + 4)
- const dv = new DataView(bytes.buffer)
- dv.setUint32(0, size)
- stringToBytes(str, bytes, 4)
- return bytes
-}
-
-export function qBytesToString(bytes) {
- const byteRange = new Uint8Array(bytes.buffer, 4, bytes.length - 4)
- return bytesToString(byteRange)
-}
-
-export function parseEntryList(list) {
- return list.split(/\s\s+/).filter(Boolean)
-}
-
-function qMapSize(o) {
- let size = 4
- for(const key in o) {
- size += (key.length + o[key].length) * 2 + 8
- }
- return size
-}
-
-function stringifyValues(obj) {
- const n = {}
- for(const key in obj) {
- n[key] = JSON.stringify(obj[key])
- }
- return n
-}
\ No newline at end of file
diff --git a/package/contents/config/main.xml b/package/contents/config/main.xml
index 287623a..a2ba666 100644
--- a/package/contents/config/main.xml
+++ b/package/contents/config/main.xml
@@ -29,6 +29,9 @@
true
+
+ false
+
true
diff --git a/package/contents/lib/secrets b/package/contents/lib/secrets
new file mode 160000
index 0000000..9589dda
--- /dev/null
+++ b/package/contents/lib/secrets
@@ -0,0 +1 @@
+Subproject commit 9589dda9cb499c7c90c425fdd05f7211b50428e3
diff --git a/package/contents/ui/ConfigGeneral.qml b/package/contents/ui/ConfigGeneral.qml
index 080b374..f2e8360 100644
--- a/package/contents/ui/ConfigGeneral.qml
+++ b/package/contents/ui/ConfigGeneral.qml
@@ -3,6 +3,8 @@ import QtQuick.Controls 2.0
import org.kde.kirigami 2.3 as Kirigami
+import "../lib/secrets"
+
Kirigami.FormLayout {
property alias cfg_appId: appId.text
@@ -10,6 +12,9 @@ Kirigami.FormLayout {
property alias cfg_itemsDownloadLimit: itemsDownloadLimit.value
property alias cfg_autoRead: autoRead.checked
property alias cfg_fetchUnreadOnly: fetchUnreadOnly.checked
+ property alias cfg_readAndFetch: readAndFetch.checked
+
+ signal configurationChanged
Item {
Kirigami.FormData.isSection: true
@@ -27,23 +32,27 @@ Kirigami.FormLayout {
Secrets {
id: secrets
+ appId: "Inoreader"
onReady: getAppKey()
+
+ property string appKey
function getAppKey() {
- return get(appId.text).then(r => appKey.text = r)
+ return get(appId.text).then(r => this.appKey = r)
}
}
TextField {
id: appId
validator: IntValidator {}
- onEditingFinished: secrets.getAppKey(appId.text)
+ onEditingFinished: secrets.getAppKey()
Kirigami.FormData.label: i18n("App id")
}
TextField {
id: appKey
- onEditingFinished: secrets.set(appId.text, text)
+ text: secrets.appKey
+ onTextChanged: text !== secrets.appKey && configurationChanged()
Kirigami.FormData.label: i18n("App key")
}
@@ -70,9 +79,18 @@ Kirigami.FormLayout {
Kirigami.FormData.label: i18n("Fetch unread only")
}
+ CheckBox {
+ id: readAndFetch
+ Kirigami.FormData.label: i18n("Fetch articles after 'Read all'")
+ }
+
CheckBox {
id: autoRead
Kirigami.FormData.label: i18n("Automatically mark an article as read")
}
+ function saveConfig() {
+ secrets.set(appId.text, appKey.text)
+ }
+
}
\ No newline at end of file
diff --git a/package/contents/ui/InoreaderModel.qml b/package/contents/ui/InoreaderModel.qml
index 7e925cd..c268454 100644
--- a/package/contents/ui/InoreaderModel.qml
+++ b/package/contents/ui/InoreaderModel.qml
@@ -16,6 +16,8 @@ BaseObject {
readonly property int unreadCount: Math.max(_.unreadCount, 0)
readonly property string unreadCountFormatted: _.kNumber(unreadCount) + (_.unreadMaxReached ? "+" : "")
+ signal reloaded()
+
Connections {
target: dispatcher
@@ -25,6 +27,7 @@ BaseObject {
onSetArticleRead: _.setArticleRead(article, read)
onSetAllArticlesRead: _.setAllArticlesRead(read)
+ onSetAllArticlesReadAndFetch: _.callPending(() => _.setAllArticlesRead(read).then(_.loadData), 'fetchStream')
onSetArticleStarred: _.setArticleStarred(article, starred)
}
@@ -110,6 +113,7 @@ BaseObject {
if (!continuation) {
_.articles.clear()
+ reloaded()
}
_.articles.append(stream.items)
_.unreadNewCount = 0
diff --git a/package/contents/ui/Logic.qml b/package/contents/ui/Logic.qml
index 1f92489..f663741 100644
--- a/package/contents/ui/Logic.qml
+++ b/package/contents/ui/Logic.qml
@@ -10,6 +10,7 @@ QtObject {
signal fetchStreamContinuation()
signal setAllArticlesRead(bool read)
+ signal setAllArticlesReadAndFetch(bool read)
signal setArticleRead(var article, bool read)
signal setArticleStarred(var article, bool starred)
@@ -52,7 +53,13 @@ QtObject {
icon.name: "checkbox"
text: i18n("Read all")
onTriggered: setAllArticlesRead(true)
- }
+ }
+
+ readonly property Action readAllAndFetchAction: Action {
+ icon.name: "checkbox"
+ text: i18n("Read all and fetch")
+ onTriggered: setAllArticlesReadAndFetch(true)
+ }
readonly property Action starAction: Action {
readonly property var icons: ({ false: "non-starred-symbolic", true: "starred-symbolic" })
diff --git a/package/contents/ui/Secrets.qml b/package/contents/ui/Secrets.qml
deleted file mode 100644
index c8a8c64..0000000
--- a/package/contents/ui/Secrets.qml
+++ /dev/null
@@ -1,35 +0,0 @@
-import QtQuick 2.0
-
-import "components"
-import "../code/qByteArray.mjs" as Qbytes
-
-BaseObject {
- signal ready
-
- function set(key, value) {
- if (!key) {
- return
- }
- return wallet.writeEntry(wallet.folder, key, value)
- }
-
- function get(key) {
- return wallet.readEntry(wallet.folder, key).then(Qbytes.bytesToString)
- }
-
- function list() {
- return wallet.entryList(wallet.folder)
- }
-
- Wallet {
- id: wallet
- appId: "Inoreader"
- readonly property string folder: appId
-
- Component.onCompleted: localWallet()
- .then(open)
- .then(() => hasFolder(folder))
- .then(hasFolder => hasFolder || createFolder(folder))
- .then(ready)
- }
-}
\ No newline at end of file
diff --git a/package/contents/ui/StreamView.qml b/package/contents/ui/StreamView.qml
index bb55536..079099c 100644
--- a/package/contents/ui/StreamView.qml
+++ b/package/contents/ui/StreamView.qml
@@ -25,7 +25,7 @@ FocusScope {
text: i18np("1 new article", "%1 new articles", stream.unreadNewCount)
onClicked: logic.fetchStream()
flat: true
- width: parent.width
+ width: listView.width
}
}
@@ -34,7 +34,7 @@ FocusScope {
PlasmaComponents3.Button {
action: logic.fetchStreamContinuationAction
flat: true
- width: parent.width
+ width: listView.width
enabled: !stream.isPending("fetchStreamContinuation")
indicator: PlasmaComponents3.ProgressBar {
@@ -54,7 +54,7 @@ FocusScope {
Layout.fillWidth: true
}
ToolButton {
- action: logic.readAllAction
+ action: plasmoid.configuration.readAndFetch ? logic.readAllAndFetchAction : logic.readAllAction
}
ToolButton {
action: logic.reloadAction
@@ -64,34 +64,6 @@ FocusScope {
PlasmaComponents3.ScrollView {
anchors.fill: parent
focus: true
-
- /*ListView {
- id: listView
- currentIndex: -1
- clip: true
- focus: true
- model: stream.articles
- spacing: PlasmaCore.Units.smallSpacing
- boundsBehavior: Flickable.StopAtBounds
- highlight: PlasmaComponents.Highlight {}
- highlightMoveDuration: PlasmaCore.Units.shortDuration
- header: stream.unreadNewCount ? newArticlesButton : null
- footer: stream.hasContinuation ? fetchMoreButton : null
- delegate: PlasmaComponents.ListItem {
- id: wrapper
- width: ListView.view.width
- enabled: true
- opacity: model.read ? 0.6 : 1
- onContainsMouseChanged: listView.currentIndex = index
- onClicked: selected(model)
- Keys.onReturnPressed: clicked()
- content: Loader {
- source: `ItemDelegate${plasmoid.configuration.viewStyle}.qml`
- width: parent.width
- }
- property bool isCurrentItem: ListView.isCurrentItem
- }
- }*/
GridView {
readonly property var cellSizes: viewMinSizeMap[plasmoid.configuration.viewStyle]
@@ -128,6 +100,10 @@ FocusScope {
}
property bool isCurrentItem: GridView.isCurrentItem
}
+ Connections {
+ target: stream
+ onReloaded: Qt.callLater(listView.positionViewAtBeginning)
+ }
}
}
}
diff --git a/package/contents/ui/components/Exec.qml b/package/contents/ui/components/Exec.qml
deleted file mode 100644
index 95e17b2..0000000
--- a/package/contents/ui/components/Exec.qml
+++ /dev/null
@@ -1,30 +0,0 @@
-import QtQuick 2.0
-
-import org.kde.plasma.core 2.0 as PlasmaCore
-
-PlasmaCore.DataSource {
- engine: "executable"
-
- readonly property var callbacks: ({})
-
- onNewData: {
- const { stdout } = data
- if (callbacks[sourceName] !== undefined) {
- if (!data["exit code"]) {
- callbacks[sourceName].resolve(stdout.trim())
- } else {
- callbacks[sourceName].reject(stdout.trim())
- }
- delete callbacks[sourceName]
- }
-
- disconnectSource(sourceName)
- }
-
- function exec(cmd) {
- return new Promise((resolve, reject) => {
- callbacks[cmd] = { resolve, reject }
- connectSource(cmd)
- })
- }
-}
\ No newline at end of file
diff --git a/package/contents/ui/components/Wallet.qml b/package/contents/ui/components/Wallet.qml
deleted file mode 100644
index af5147b..0000000
--- a/package/contents/ui/components/Wallet.qml
+++ /dev/null
@@ -1,137 +0,0 @@
-import QtQuick 2.0
-
-import "../../code/qByteArray.mjs" as Qbytes
-
-Exec {
- property string appId
- property int handler
-
- readonly property string commandBase: "dbus-send --session --type=method_call --print-reply=literal --dest=org.kde.kwalletd5 /modules/kwalletd5 org.kde.KWallet."
-
- enum EntryType {
- Password = 1,
- Binary,
- Map
- }
-
- function setHandler(h) {
- handler = h
- }
-
- function call(command) {
- return exec(commandBase + command)
- .then(parseResponse)
- }
-
- function callWallet(command, ...args) {
- return call([command, f(handler), ...args, f(appId)].join(" "))
- }
-
- function formatEntryValue(value, type) {
- switch(type) {
- case Wallet.EntryType.Password: return Qbytes.stringToQBytes(value)
- case Wallet.EntryType.Binary: return Qbytes.stringToBytes(value)
- case Wallet.EntryType.Map: return Qbytes.mapToQBytes(value)
- }
- }
-
- function f(value, type) {
- if (!type) {
- type = {
- number: 'int32',
- object: 'array:byte'
- }[typeof value] || typeof value
- }
- return `${type}:"${value}"`
- }
-
- function parseResponse(str) {
- const t = str.trim()
- const typeEndIndex = t.indexOf(" ")
- const dataType = t.slice(0, typeEndIndex)
- const data = ~typeEndIndex ? t.slice(typeEndIndex + 1) : t
- if (dataType === 'array') {
- return data.slice(data.indexOf("[") + 1, data.lastIndexOf("]") - 1)
- }
- return data
- }
-
- function open(wallet) {
- if (handler) {
- return Promise.resolve(handler)
- }
- return call(`open ${f(wallet)} int64:0 ${f(appId)}`)
- .then(Number.parseInt)
- .then(h => (setHandler(h), h))
- }
-
- function close() {
- return callWallet('close', 'int64:0')
- .then(() => setHandler())
- }
-
- function localWallet() {
- return call('localWallet')
- }
-
- function hasFolder(folderName) {
- return callWallet('hasFolder', f(folderName)).then(JSON.parse)
- }
-
- function createFolder(folderName) {
- return callWallet('createFolder', f(folderName))
- }
-
- function removeFolder(folderName) {
- return callWallet('removeFolder', f(folderName))
- }
-
- function writeEntry(folderName, key, value, type = Wallet.EntryType.Binary) {
- return callWallet('writeEntry',
- f(folderName),
- f(key),
- f(formatEntryValue(value, type)),
- f(type))
- }
-
- function readEntry(folderName, key) {
- return callWallet('readEntry', f(folderName), f(key))
- .then(Qbytes.parseBytes)
- }
-
- function removeEntry(folderName, key) {
- return callWallet('removeEntry', f(folderName), f(key))
- }
-
- function entryList(folderName) {
- return callWallet('entryList', f(folderName))
- .then(Qbytes.parseEntryList)
- }
-
- function readMap(folderName, key) {
- return readEntry(f(folderName), f(key))
- .then(Qbytes.qBytesToMap)
- .catch(e => ({}))
- }
-
- function writeMap(folderName, key, value) {
- return writeEntry(
- f(folderName),
- f(key),
- f(formatEntryValue(value, Wallet.EntryType.Map)),
- f(Wallet.EntryType.Map))
- }
-
- function readPassword(folderName, key) {
- return readEntry(f(folderName), f(key))
- .then(Qbytes.bytesToString)
- }
-
- function writePassword(folderName, key, value) {
- return writeEntry(
- f(folderName),
- f(key),
- f(formatEntryValue(value, Wallet.EntryType.Password)),
- f(Wallet.EntryType.Password))
- }
-}
\ No newline at end of file
diff --git a/package/contents/ui/components/qmldir b/package/contents/ui/components/qmldir
index 79bb38c..e05cad7 100644
--- a/package/contents/ui/components/qmldir
+++ b/package/contents/ui/components/qmldir
@@ -1,8 +1,6 @@
Badge 1.0 Badge.qml
BaseObject 1.0 BaseObject.qml
-Exec 1.0 Exec.qml
OAuth 1.0 OAuth.qml
Paragraph 1.0 Paragraph.qml
ToolButton 1.0 ToolButton.qml
-Wallet 1.0 Wallet.qml
WebWindow 1.0 WebWindow.qml
\ No newline at end of file
diff --git a/package/contents/ui/main.qml b/package/contents/ui/main.qml
index 0902898..af87c25 100644
--- a/package/contents/ui/main.qml
+++ b/package/contents/ui/main.qml
@@ -4,6 +4,7 @@ import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import "components"
+import "../lib/secrets"
Item {
id: root
@@ -81,8 +82,9 @@ Item {
Secrets {
id: secrets
+ appId: "Inoreader"
property bool pending: true
- readonly property string tokenKey: (userId && appId) ? userId + "@" + appId : ""
+ readonly property string tokenKey: (userId && root.appId) ? userId + "@" + root.appId : ""
function stopPending() {
pending = false
@@ -90,8 +92,8 @@ Item {
function restore() {
const promises = []
- if (appId) {
- promises.push(get(appId).then(key => auth.clientSecret = key))
+ if (root.appId) {
+ promises.push(get(root.appId).then(key => auth.clientSecret = key))
}
if (tokenKey) {
promises.push(get(tokenKey).then(token => {
@@ -104,7 +106,7 @@ Item {
}
function init() {
- if (appId) {
+ if (root.appId) {
return restore()
}
const isUserEntry = e => e.includes("@")
diff --git a/test/tst_qbytearray.qml b/test/tst_qbytearray.qml
deleted file mode 100644
index 6496bd2..0000000
--- a/test/tst_qbytearray.qml
+++ /dev/null
@@ -1,57 +0,0 @@
-import QtQuick 2.0
-import QtTest 1.2
-
-import "../package/contents/code/qByteArray.mjs" as Q
-
-TestCase {
- name: "QByteTests"
-
- function test_parseBytes() {
- const string = "[00 00 00 06 00 31 00 32 00 33]"
- const result = new Uint8Array([0x0, 0x0, 0x0, 0x06, 0x0, 0x31, 0x0, 0x32, 0x0, 0x33])
- compare(Q.parseBytes(string), result)
- }
-
- function test_bytesToString() {
- const bytes = new Uint8Array([0x0, 0x74, 0x02, 0x58, 0x0, 0x73, 0x0, 0x74])
- const string = "tɘst"
- compare(Q.bytesToString(bytes), string)
- }
-
- function test_stringToBytes() {
- const string = "tɘst"
- const bytes = new Uint8Array([0x0, 0x74, 0x02, 0x58, 0x0, 0x73, 0x0, 0x74])
- compare(Q.stringToBytes(string), bytes)
- }
-
- function test_mapToQBytes() {
- const map = { a: 1, b: true }
- const bytes = new Uint8Array([0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x61, 0x0, 0x0, 0x0, 0x2, 0x0, 0x31, 0x0, 0x0, 0x0, 0x2, 0x0, 0x62, 0x0, 0x0, 0x0, 0x8, 0x0, 0x74, 0x0, 0x72, 0x0, 0x75, 0x0, 0x65])
- compare(Q.mapToQBytes(map), bytes)
- }
-
- function test_qBytesToMap() {
- const map = { a: "1", b: "true" }
- const bytes = new Uint8Array([0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x61, 0x0, 0x0, 0x0, 0x2, 0x0, 0x31, 0x0, 0x0, 0x0, 0x2, 0x0, 0x62, 0x0, 0x0, 0x0, 0x8, 0x0, 0x74, 0x0, 0x72, 0x0, 0x75, 0x0, 0x65])
- compare(Q.qBytesToMap(bytes), map)
- }
-
- function test_stringToQBytes() {
- const string = "tɘst"
- const bytes = new Uint8Array([0x0, 0x0, 0x0, 0x8, 0x0, 0x74, 0x02, 0x58, 0x0, 0x73, 0x0, 0x74])
- compare(Q.stringToQBytes(string), bytes)
- }
-
- function test_qBytesToString() {
- const string = "tɘst"
- const bytes = new Uint8Array([0x0, 0x0, 0x0, 0x8, 0x0, 0x74, 0x02, 0x58, 0x0, 0x73, 0x0, 0x74])
- compare(Q.qBytesToString(bytes), string)
- }
-
- function test_parseEntryList() {
- const listStr = ' 123456789 987654321@123456789'
- const entryList = ['123456789', '987654321@123456789']
- compare(Q.parseEntryList(listStr), entryList)
- }
-
-}
\ No newline at end of file