first commit
This commit is contained in:
74
.gitignore
vendored
Normal file
74
.gitignore
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
3
backup.cpp
Normal file
3
backup.cpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#include "backup.h"
|
||||||
|
|
||||||
|
Backup::Backup() {}
|
||||||
10
backup.h
Normal file
10
backup.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef BACKUP_H
|
||||||
|
#define BACKUP_H
|
||||||
|
|
||||||
|
class Backup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Backup();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BACKUP_H
|
||||||
71
client.cpp
Normal file
71
client.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include "client.h"
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
Client::Client(QString url, QString username, QString password) {
|
||||||
|
this->url = url;
|
||||||
|
this->username = username;
|
||||||
|
this->password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::version(std::function<void (Information)> callback, std::function<void (QString)> errorCallback)
|
||||||
|
{
|
||||||
|
QNetworkAccessManager manager;
|
||||||
|
QUrl url(this->url);
|
||||||
|
url = url.resolved(QUrl("/api/v1/version/"));
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
QNetworkReply *reply = manager.get(request);
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [&]() {
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
QByteArray response = reply->readAll();
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(response);
|
||||||
|
Information info(doc.object());
|
||||||
|
callback(info);
|
||||||
|
} else {
|
||||||
|
errorCallback(reply->errorString());
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::hash(QString gameID, std::function<void(QString)> callback, std::function<void(QString)> errorCallback)
|
||||||
|
{
|
||||||
|
QNetworkAccessManager manager;
|
||||||
|
QUrl url(this->url);
|
||||||
|
url = url.resolved("/api/v1/games/"+gameID+"/metadata");
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
QNetworkReply *reply = manager.get(request);
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [&]() {
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
QByteArray response = reply->readAll();
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(response);
|
||||||
|
callback(doc.object()["md5"].toString());
|
||||||
|
} else {
|
||||||
|
errorCallback(reply->errorString());
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::metadata(QString gameID, std::function<void(Metadata)> callback, std::function<void(QString)> errorCallback)
|
||||||
|
{
|
||||||
|
QNetworkAccessManager manager;
|
||||||
|
QUrl url(this->url);
|
||||||
|
url = url.resolved("/api/v1/games/"+gameID+"/metadata");
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
QNetworkReply *reply = manager.get(request);
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [&]() {
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
QByteArray response = reply->readAll();
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(response);
|
||||||
|
Metadata m(doc.object());
|
||||||
|
callback(m);
|
||||||
|
} else {
|
||||||
|
errorCallback(reply->errorString());
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
});
|
||||||
|
}
|
||||||
35
client.h
Normal file
35
client.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef CLIENT_H
|
||||||
|
#define CLIENT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "information.h"
|
||||||
|
#include "metadata.h"
|
||||||
|
#include "backup.h"
|
||||||
|
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Client(QString url, QString username, QString password);
|
||||||
|
|
||||||
|
bool exists(QString gameID, std::function<void(bool)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void version(std::function<void(Information)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void hash(QString gameID, std::function<void(QString)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void metadata(QString gameID, std::function<void(Metadata)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void pushSave(QString archivePath, Metadata game, std::function<void(Metadata)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void pushBackup(QString archivePath, Metadata game, std::function<void(Metadata)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void listArchives(QString gameID, std::function<void(QVector<QString>)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void archiveInfo(QString gameID, QString backupID, std::function<void(Backup)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void pull(QString gameID, QString archivePath, std::function<void(Metadata)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void pullBackup(QString gameID, QString backupID, QString archivePath, std::function<void(Metadata)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
bool ping(std::function<void(Metadata)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
void all(std::function<void(QVector<Metadata>)> callback, std::function<void(QString)> errorCallback);
|
||||||
|
QString baseURL();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString url;
|
||||||
|
QString username;
|
||||||
|
QString password;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CLIENT_H
|
||||||
35
cloudsave-gui.pro
Normal file
35
cloudsave-gui.pro
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
QT += core gui network
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
CONFIG += c++17
|
||||||
|
|
||||||
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
backup.cpp \
|
||||||
|
client.cpp \
|
||||||
|
information.cpp \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp \
|
||||||
|
metadata.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
backup.h \
|
||||||
|
client.h \
|
||||||
|
information.h \
|
||||||
|
mainwindow.h \
|
||||||
|
metadata.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
RESOURCES += \
|
||||||
|
resources.qrc
|
||||||
18
information.cpp
Normal file
18
information.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "information.h"
|
||||||
|
|
||||||
|
Information::Information(QString version, QString apiVersion, QString goVersion, QString osName, QString osArchitecture) {
|
||||||
|
this->_version = version;
|
||||||
|
this->_apiVersion = apiVersion;
|
||||||
|
this->_goVersion = goVersion;
|
||||||
|
this->_osName = osName;
|
||||||
|
this->_osArchitecture = osArchitecture;
|
||||||
|
}
|
||||||
|
|
||||||
|
Information::Information(QJsonObject obj)
|
||||||
|
{
|
||||||
|
this->_version = obj["version"].toString();
|
||||||
|
this->_apiVersion = obj["api_version"].toString();
|
||||||
|
this->_goVersion = obj["go_version"].toString();
|
||||||
|
this->_osName = obj["os_name"].toString();
|
||||||
|
this->_osArchitecture = obj["os_architecture"].toString();
|
||||||
|
}
|
||||||
21
information.h
Normal file
21
information.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef INFORMATION_H
|
||||||
|
#define INFORMATION_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
class Information
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Information(QString version, QString apiVersion, QString goVersion, QString osName, QString osArchitecture);
|
||||||
|
Information(QJsonObject obj);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _version;
|
||||||
|
QString _apiVersion;
|
||||||
|
QString _goVersion;
|
||||||
|
QString _osName;
|
||||||
|
QString _osArchitecture;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INFORMATION_H
|
||||||
11
main.cpp
Normal file
11
main.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
||||||
14
mainwindow.cpp
Normal file
14
mainwindow.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::MainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
23
mainwindow.h
Normal file
23
mainwindow.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
};
|
||||||
|
#endif // MAINWINDOW_H
|
||||||
108
mainwindow.ui
Normal file
108
mainwindow.ui
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1002</width>
|
||||||
|
<height>594</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>1002</width>
|
||||||
|
<height>594</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>CloudSave</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tableView"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
<widget class="QToolBar" name="toolBar">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>90</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>toolBar</string>
|
||||||
|
</property>
|
||||||
|
<property name="movable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>70</width>
|
||||||
|
<height>37</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="floatable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="toolBarArea">
|
||||||
|
<enum>TopToolBarArea</enum>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="toolBarBreak">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<addaction name="actionAdd"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionScan"/>
|
||||||
|
<addaction name="actionSync"/>
|
||||||
|
</widget>
|
||||||
|
<action name="actionAdd">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="resources.qrc">
|
||||||
|
<normaloff>:/icon/resources/plus-solid-full.svg</normaloff>:/icon/resources/plus-solid-full.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionScan">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="resources.qrc">
|
||||||
|
<normaloff>:/icon/resources/magnifying-glass-solid-full.svg</normaloff>:/icon/resources/magnifying-glass-solid-full.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Scan</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSync">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="resources.qrc">
|
||||||
|
<normaloff>:/icon/resources/arrows-rotate-solid-full.svg</normaloff>:/icon/resources/arrows-rotate-solid-full.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Sync</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="resources.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
19
metadata.cpp
Normal file
19
metadata.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "metadata.h"
|
||||||
|
|
||||||
|
Metadata::Metadata(QString name) {
|
||||||
|
this->_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Metadata::Metadata(QJsonObject obj)
|
||||||
|
{
|
||||||
|
this->_id = obj["id"].toString();
|
||||||
|
this->_name = obj["name"].toString();
|
||||||
|
this->_version = obj["version"].toInt();
|
||||||
|
this->_date = QDateTime::fromString(obj["date"].toString());
|
||||||
|
this->_md5 = obj["md5"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Metadata::md5()
|
||||||
|
{
|
||||||
|
return this->_md5;
|
||||||
|
}
|
||||||
23
metadata.h
Normal file
23
metadata.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef METADATA_H
|
||||||
|
#define METADATA_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
class Metadata
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Metadata(QString name);
|
||||||
|
Metadata(QJsonObject obj);
|
||||||
|
|
||||||
|
QString md5();
|
||||||
|
private:
|
||||||
|
QString _id;
|
||||||
|
QString _name;
|
||||||
|
uint64_t _version;
|
||||||
|
QDateTime _date;
|
||||||
|
QString _md5;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // METADATA_H
|
||||||
7
resources.qrc
Normal file
7
resources.qrc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/icon">
|
||||||
|
<file>resources/plus-solid-full.svg</file>
|
||||||
|
<file>resources/arrows-rotate-solid-full.svg</file>
|
||||||
|
<file>resources/magnifying-glass-solid-full.svg</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
1
resources/arrows-rotate-solid-full.svg
Normal file
1
resources/arrows-rotate-solid-full.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M129.9 292.5C143.2 199.5 223.3 128 320 128C373 128 421 149.5 455.8 184.2C456 184.4 456.2 184.6 456.4 184.8L464 192L416.1 192C398.4 192 384.1 206.3 384.1 224C384.1 241.7 398.4 256 416.1 256L544.1 256C561.8 256 576.1 241.7 576.1 224L576.1 96C576.1 78.3 561.8 64 544.1 64C526.4 64 512.1 78.3 512.1 96L512.1 149.4L500.8 138.7C454.5 92.6 390.5 64 320 64C191 64 84.3 159.4 66.6 283.5C64.1 301 76.2 317.2 93.7 319.7C111.2 322.2 127.4 310 129.9 292.6zM573.4 356.5C575.9 339 563.7 322.8 546.3 320.3C528.9 317.8 512.6 330 510.1 347.4C496.8 440.4 416.7 511.9 320 511.9C267 511.9 219 490.4 184.2 455.7C184 455.5 183.8 455.3 183.6 455.1L176 447.9L223.9 447.9C241.6 447.9 255.9 433.6 255.9 415.9C255.9 398.2 241.6 383.9 223.9 383.9L96 384C87.5 384 79.3 387.4 73.3 393.5C67.3 399.6 63.9 407.7 64 416.3L65 543.3C65.1 561 79.6 575.2 97.3 575C115 574.8 129.2 560.4 129 542.7L128.6 491.2L139.3 501.3C185.6 547.4 249.5 576 320 576C449 576 555.7 480.6 573.4 356.5z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
resources/magnifying-glass-solid-full.svg
Normal file
1
resources/magnifying-glass-solid-full.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M480 272C480 317.9 465.1 360.3 440 394.7L566.6 521.4C579.1 533.9 579.1 554.2 566.6 566.7C554.1 579.2 533.8 579.2 521.3 566.7L394.7 440C360.3 465.1 317.9 480 272 480C157.1 480 64 386.9 64 272C64 157.1 157.1 64 272 64C386.9 64 480 157.1 480 272zM272 416C351.5 416 416 351.5 416 272C416 192.5 351.5 128 272 128C192.5 128 128 192.5 128 272C128 351.5 192.5 416 272 416z"/></svg>
|
||||||
|
After Width: | Height: | Size: 595 B |
1
resources/plus-solid-full.svg
Normal file
1
resources/plus-solid-full.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M352 128C352 110.3 337.7 96 320 96C302.3 96 288 110.3 288 128L288 288L128 288C110.3 288 96 302.3 96 320C96 337.7 110.3 352 128 352L288 352L288 512C288 529.7 302.3 544 320 544C337.7 544 352 529.7 352 512L352 352L512 352C529.7 352 544 337.7 544 320C544 302.3 529.7 288 512 288L352 288L352 128z"/></svg>
|
||||||
|
After Width: | Height: | Size: 522 B |
Reference in New Issue
Block a user