summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorꦌꦫꦶꦏ꧀ꦦꦿꦧꦮꦑꦩꦭ꧀ <erik@darapsa.co.id>2021-07-04 15:29:04 +0800
committerꦌꦫꦶꦏ꧀ꦦꦿꦧꦮꦑꦩꦭ꧀ <erik@darapsa.co.id>2021-07-04 15:29:04 +0800
commitf8dbe8e0f771949f83e91b5e37a690e571a047ee (patch)
tree50cb474a2ebe8e0e730d38186dad87912c0d5f4d
Separated client code
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules6
-rw-r--r--Catalog.qml28
-rw-r--r--README.md48
-rw-r--r--controller.cxx55
-rw-r--r--controller.hxx21
-rw-r--r--handler.c62
-rw-r--r--images/onboarding-illustration-1.pngbin0 -> 64816 bytes
m---------larva0
-rw-r--r--larva.qrc5
-rw-r--r--main.cxx12
-rw-r--r--main.qml46
-rw-r--r--namatoko.pro50
-rw-r--r--namatoko.qrc8
m---------qicpos0
-rw-r--r--qicpos.qrc5
-rw-r--r--qtquickcontrols2.conf7
-rw-r--r--registration.c34
18 files changed, 391 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e79efa9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*~
+*.pro.*
+*.swp
+build*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..b1eb6f3
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "larva"]
+ path = larva
+ url = git://darapsa.org/larva.git
+[submodule "qicpos"]
+ path = qicpos
+ url = git://darapsa.org/qicpos.git
diff --git a/Catalog.qml b/Catalog.qml
new file mode 100644
index 0000000..be144c5
--- /dev/null
+++ b/Catalog.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import "qicpos"
+
+Rectangle {
+ color: "#0a0a0a"
+ ListView {
+ anchors.fill: parent
+ objectName: "catalog"
+ model: catalog
+ spacing: 16
+ delegate: ProductForm {
+ width: 328
+ height: 132
+ color: "#2a2a2a"
+ anchors.horizontalCenter: parent.horizontalCenter
+ previewImage {
+ width: 64
+ height: 64
+ source: imageDir + "thumb/" + image
+ }
+ nameLabel.text: description
+ priceLabel.text: price
+ }
+
+ ScrollBar.horizontal: ScrollBar {}
+ }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a96f32c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,48 @@
+# Namatoko
+
+# Building
+
+## Getting
+
+```sh
+$ git clone interch@darapsa.co.id:/usr/local/git/namatoko.git
+$ git submodule init
+$ git submodule update
+$ cd namatoko
+$ mkdir build
+$ cd build
+```
+
+## Configuring for various target hosts (with optional debugging), compiling, and linking
+
+```sh
+$ /opt/Qt/5.15.2/wasm_32/bin/qmake ../namatoko.pro -spec wasm-emscripten CONFIG+='debug qml_debug' SAMPLEURL=https://darapsa.com IMAGE_DIR=/images
+$ emmake make
+```
+
+or
+
+```sh
+$ /opt/Qt/5.15.2/android/bin/qmake ../namatoko.pro -spec android-clang CONFIG+='debug qml_debug' ANDROID_NDK_PATH=/opt/android-sdk-update-manager/ndk/21.3.6528147 ANDROID_NDK_HOST=linux-x86_64 ANDROID_TARGET_ARCH=arm64-v8a QT_ANDROID_LIBDIR=/opt/Qt/5.15.2/android/lib API=21 CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt ANDROID_ABIS=arm64-v8a SAMPLEURL=https://darapsa.com IMAGE_DIR=/images
+$ make
+```
+
+or so on.
+
+## Producing bundle
+
+For Android:
+
+```sh
+$ make install INSTALL_ROOT=android-build
+$ export ANDROID_SDK_ROOT=/opt/android-sdk-update-manager
+$ /opt/Qt/5.15.2/android/bin/androiddeployqt --input android-namatoko-deployment-settings.json --output android-build --android-platform android-30
+```
+
+and if on FreeBSD:
+
+```sh
+$ cd android-build
+$ echo "android.aapt2FromMavenOverride = $ANDROID_SDK_ROOT/build-tools/28.0.3/aapt2" >> gradle.properties
+./gradlew assembleDebug
+```
diff --git a/controller.cxx b/controller.cxx
new file mode 100644
index 0000000..2e95c09
--- /dev/null
+++ b/controller.cxx
@@ -0,0 +1,55 @@
+#include <QtQml>
+#include <qicclient/admin.hxx>
+#include "controller.hxx"
+
+extern "C" {
+ void sign_up(char const*, char const*);
+ struct icclient_catalog* catalog_data(char const*);
+}
+
+Controller::Controller(QObject* parent) :
+ QObject{parent},
+ catalog{nullptr}
+{
+#ifdef __ANDROID__
+ QString cert{CA_BUNDLE};
+ QString path{QDir{QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)}.absolutePath()
+ % cert.remove(0, cert.lastIndexOf("/"))};
+ QFile{"assets:" % cert}.copy(path);
+#endif
+ interchange = new Client{SAMPLEURL, IMAGE_DIR
+#ifdef __ANDROID__
+ , path.toLatin1().constData()
+#endif
+ };
+ auto engine = static_cast<QQmlApplicationEngine*>(parent);
+ engine->load(QUrl{QStringLiteral("qrc:/main.qml")});
+ auto window = engine->rootObjects()[0];
+ window->setProperty("imageDir", SAMPLEURL"/images/");
+ connect(window, SIGNAL(signUp(QString)), this, SIGNAL(signUp(QString)));
+ connect(this, &Controller::signUp, [/*this,*/
+#ifdef __ANDROID__
+ &path
+#endif
+ ](QString const& brand) {
+ sign_up(brand.toLatin1().constData(),
+#ifdef __ANDROID__
+ path.toLatin1().constData()
+#else
+ nullptr
+#endif
+ );
+// interchange->catalog(brand);
+ });
+ connect(interchange, &Client::gotCatalog, [this,engine,window](QString const& response) {
+ catalog = new Catalog{catalog_data(response.toLatin1().constData())};
+ engine->rootContext()->setContextProperty("catalog", catalog);
+ QMetaObject::invokeMethod(window, "pushCatalog");
+ });
+}
+
+Controller::~Controller()
+{
+ if (catalog) delete catalog;
+ delete interchange;
+}
diff --git a/controller.hxx b/controller.hxx
new file mode 100644
index 0000000..b073a67
--- /dev/null
+++ b/controller.hxx
@@ -0,0 +1,21 @@
+#ifndef CONTROLLER_HXX
+#define CONTROLLER_HXX
+
+#include <qicclient.hxx>
+
+using namespace QICClient;
+
+class Controller : public QObject
+{
+ Q_OBJECT
+ public:
+ Controller(QObject* parent = nullptr);
+ ~Controller();
+ signals:
+ void signUp(QString const& name);
+ private:
+ Client* interchange;
+ Catalog* catalog;
+};
+
+#endif
diff --git a/handler.c b/handler.c
new file mode 100644
index 0000000..8e118f7
--- /dev/null
+++ b/handler.c
@@ -0,0 +1,62 @@
+#include <stdbool.h>
+#include <tidy.h>
+#include <tidybuffio.h>
+#include <icclient.h>
+
+static void recurse_catalog(TidyDoc doc, TidyNode tnod, struct icclient_catalog **catalog)
+{
+ for (TidyNode child = tidyGetChild(tnod); child; child = tidyGetNext(child)) {
+ ctmbstr name = tidyNodeGetName(child);
+ if (!name)
+ continue;
+ if (strcmp(name, "img")) {
+ recurse_catalog(doc, child, catalog);
+ continue;
+ }
+ static const char *prefix = IMAGE_DIR"/thumb/";
+ size_t prefix_len = strlen(prefix);
+ bool bail = false;
+ for (TidyAttr attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr))
+ if (!strcmp(tidyAttrName(attr), "src") && strncmp(tidyAttrValue(attr), prefix, prefix_len)) {
+ bail = true;
+ break;
+ }
+ if (bail)
+ continue;
+ struct icclient_product *product = malloc(sizeof(struct icclient_product));
+ memset(product, '\0', sizeof(struct icclient_product));
+ for (TidyAttr attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr)) {
+ name = tidyAttrName(attr);
+ ctmbstr value = tidyAttrValue(attr);
+ if (!strcmp(name, "src")) {
+ size_t len = strlen(value) - prefix_len;
+ product->image = malloc(len + 1);
+ strncpy(product->image, value + prefix_len, len + 1);
+ } else if (!strcmp(name, "alt")) {
+ product->description = malloc(strlen(value) + 1);
+ strcpy(product->description, value);
+ } else if (!strcmp(name, "title")) {
+ product->sku = malloc(strlen(value + 1));
+ strcpy(product->sku, value);
+ }
+ }
+ (*catalog)->length++;
+ *catalog = realloc(*catalog, sizeof(struct icclient_catalog)
+ + sizeof(struct icclient_product *[(*catalog)->length]));
+ (*catalog)->products[(*catalog)->length - 1] = product;
+ }
+}
+
+struct icclient_catalog *catalog_data(const char *response)
+{
+ TidyDoc tdoc = tidyCreate();
+ TidyBuffer output = {0};
+ tidyParseString(tdoc, response);
+ tidySaveBuffer(tdoc, &output);
+ struct icclient_catalog *catalog = malloc(sizeof(struct icclient_catalog));
+ catalog->length = 0;
+ recurse_catalog(tdoc, tidyGetRoot(tdoc), &catalog);
+ tidyBufFree(&output);
+ tidyRelease(tdoc);
+ return catalog;
+}
diff --git a/images/onboarding-illustration-1.png b/images/onboarding-illustration-1.png
new file mode 100644
index 0000000..c09f062
--- /dev/null
+++ b/images/onboarding-illustration-1.png
Binary files differ
diff --git a/larva b/larva
new file mode 160000
+Subproject 4f7f7f1a79ea43aeb2266fca27d27346a78125a
diff --git a/larva.qrc b/larva.qrc
new file mode 100644
index 0000000..61307a5
--- /dev/null
+++ b/larva.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>larva/features/EmailForm.ui.qml</file>
+ </qresource>
+</RCC>
diff --git a/main.cxx b/main.cxx
new file mode 100644
index 0000000..9de5dab
--- /dev/null
+++ b/main.cxx
@@ -0,0 +1,12 @@
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include "controller.hxx"
+
+int main(int argc, char* argv[])
+{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QGuiApplication app{argc, argv};
+ QQmlApplicationEngine engine;
+ Controller controller{&engine};
+ return app.exec();
+}
diff --git a/main.qml b/main.qml
new file mode 100644
index 0000000..3d40e75
--- /dev/null
+++ b/main.qml
@@ -0,0 +1,46 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import "larva/features"
+
+ApplicationWindow {
+ property string imageDir
+ signal signUp(string brand)
+ function pushCatalog() {
+ stack.push("Catalog.qml")
+ }
+ id: window
+ width: 360
+ height: 640
+ visible: true
+ StackView {
+ id: stack
+ anchors.fill: parent
+ initialItem : Column {
+ Image {
+ width: parent.width
+ fillMode: Image.PreserveAspectFit
+ source: "images/onboarding-illustration-1.png"
+ }
+ EmailForm {
+ anchors.bottom: parent.bottom
+ width: parent.width
+ backButton.visible: false
+ instructionLabel {
+ text: qsTr("Cukup masukkan nama toko Anda, et voilà!")
+ font.pointSize: 18
+ }
+ emailTextField {
+ placeholderText: "Coba: Hardware, Ladders, Measuring Tools, Safety Equipment, Hand Tools, Painting Supplies, atau Tool Storage"
+ placeholderTextColor: "#888888"
+ color: "#000000"
+ onTextChanged: if (!emailTextField.text || !loginButton.enabled)
+ loginButton.enabled = !loginButton.enabled
+ }
+ loginButton {
+ text: qsTr("Mulai berjualan!")
+ onClicked: signUp(emailTextField.text)
+ }
+ }
+ }
+ }
+}
diff --git a/namatoko.pro b/namatoko.pro
new file mode 100644
index 0000000..b2830b8
--- /dev/null
+++ b/namatoko.pro
@@ -0,0 +1,50 @@
+QT += quickcontrols2
+DEFINES += \
+ CA_BUNDLE=\\\"$$CA_BUNDLE\\\" \
+ SAMPLEURL=\\\"$$SAMPLEURL\\\" \
+ IMAGE_DIR=\\\"$$IMAGE_DIR\\\"
+debug: DEFINES += DEBUG
+HEADERS += controller.hxx
+SOURCES += \
+ main.cxx \
+ controller.cxx \
+ registration.c \
+ handler.c
+RESOURCES += \
+ namatoko.qrc \
+ qicpos.qrc \
+ larva.qrc \
+ larva/material-design-icons.qrc
+wasm {
+ QT += svg
+ QMAKE_CXXFLAGS += -std=c++11
+ LIBS += -ltidys
+}
+!wasm: LIBS += \
+ -ltidy \
+ -lcurl
+LIBS += \
+ -licclient
+!android: LIBS += -lqicclient
+android {
+ QT += svg
+ PREFIX = $$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$$ANDROID_NDK_HOST/sysroot/usr
+ TRIPLE = aarch64-linux-android
+ contains(ANDROID_TARGET_ARCH,armeabi-v7a): TRIPLE = arm-linux-androideabi
+ contains(ANDROID_TARGET_ARCH,x86): TRIPLE = i686-linux-android
+ contains(ANDROID_TARGET_ARCH,x86_64): TRIPLE = x86_64-linux-android
+ LIBS += \
+ -L$$PREFIX/lib/$$TRIPLE/$$API \
+ -L$$QT_ANDROID_LIBDIR \
+ -lqicclient_$$ANDROID_TARGET_ARCH
+ ANDROID_EXTRA_LIBS += \
+ $$PREFIX/lib/$$TRIPLE/$$API/libcrypto_1_1.so \
+ $$PREFIX/lib/$$TRIPLE/$$API/libssl_1_1.so \
+ $$PREFIX/lib/$$TRIPLE/$$API/libcurl.so \
+ $$PREFIX/lib/$$TRIPLE/$$API/libtidy.so \
+ $$PREFIX/lib/$$TRIPLE/$$API/libicclient.so \
+ $$QT_ANDROID_LIBDIR/libqicclient_$${ANDROID_TARGET_ARCH}.so
+ assets.path = /assets
+ assets.files = $$CA_BUNDLE
+ INSTALLS += assets
+}
diff --git a/namatoko.qrc b/namatoko.qrc
new file mode 100644
index 0000000..bcb5510
--- /dev/null
+++ b/namatoko.qrc
@@ -0,0 +1,8 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qtquickcontrols2.conf</file>
+ <file>main.qml</file>
+ <file>images/onboarding-illustration-1.png</file>
+ <file>Catalog.qml</file>
+ </qresource>
+</RCC>
diff --git a/qicpos b/qicpos
new file mode 160000
+Subproject fabfc8193acac84883dda0874abd777e888b112
diff --git a/qicpos.qrc b/qicpos.qrc
new file mode 100644
index 0000000..b534680
--- /dev/null
+++ b/qicpos.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qicpos/ProductForm.ui.qml</file>
+ </qresource>
+</RCC>
diff --git a/qtquickcontrols2.conf b/qtquickcontrols2.conf
new file mode 100644
index 0000000..0af35a2
--- /dev/null
+++ b/qtquickcontrols2.conf
@@ -0,0 +1,7 @@
+[Controls]
+Style=Material
+
+[Material]
+Theme=Light
+Primary=#000000
+Background=#ffffff
diff --git a/registration.c b/registration.c
new file mode 100644
index 0000000..695cf53
--- /dev/null
+++ b/registration.c
@@ -0,0 +1,34 @@
+#ifdef __EMSCRIPTEN__
+#include <string.h>
+#include <emscripten/fetch.h>
+#else
+#include <curl/curl.h>
+#endif
+
+void sign_up(const char *brand, const char *certificate)
+{
+#ifdef __EMSCRIPTEN__
+ emscripten_fetch_attr_t attr;
+ emscripten_fetch_attr_init(&attr);
+ attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
+ strcpy(attr.requestMethod, "POST");
+ attr.requestData = brand;
+ attr.requestDataSize = strlen(brand);
+ emscripten_fetch(&attr, "register");
+ (void)certificate;
+#else
+ curl_global_init(CURL_GLOBAL_SSL);
+ CURL *curl = curl_easy_init();
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ if (certificate)
+ curl_easy_setopt(curl, CURLOPT_CAINFO, certificate);
+#ifdef DEBUG
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+#endif
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, brand);
+ curl_easy_setopt(curl, CURLOPT_URL, SAMPLEURL"/register");
+ curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+#endif
+}