button icons and dynamic news tabs

This commit is contained in:
LubuWest 2024-04-05 19:28:47 +02:00
commit 8391b028f4
333 changed files with 2193 additions and 2040 deletions

53
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,53 @@
cmake_minimum_required(VERSION 3.16.0)
project(friendiqa VERSION 0.6 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
if(CMAKE_VERSION VERSION_LESS "3.7.0")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
endif()
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Quick QuickControls2 Sql DBus NetworkAuth Multimedia REQUIRED)
qt_standard_project_setup()
set(MOC_SOURCES common/uploadableimage.h
common/xhr.h
common/filesystem.h
common/updatenews.h
common/alarm.h
common/oauth.h
common/documenthandler.h)
set(SOURCES common/friendiqa.cpp
common/uploadableimage.cpp
common/xhr.cpp
common/filesystem.cpp
common/updatenews.cpp
common/alarmlinux.cpp
common/oauth.cpp
common/documenthandler.cpp)
include_directories(common)
qt_add_executable(friendiqa ${SOURCES} ${MOC_SOURCES} application.qrc)
target_link_libraries(friendiqa PRIVATE Qt6::Core)
target_link_libraries(friendiqa PRIVATE Qt6::Widgets)
target_link_libraries(friendiqa PRIVATE Qt6::Quick)
target_link_libraries(friendiqa PRIVATE Qt6::QuickControls2)
target_link_libraries(friendiqa PRIVATE Qt6::Sql)
target_link_libraries(friendiqa PRIVATE Qt6::DBus)
target_link_libraries(friendiqa PRIVATE Qt6::NetworkAuth)
target_link_libraries(friendiqa PRIVATE Qt6::Multimedia)
#target_link_libraries(friendiqa PRIVATE Qt6::Svg)
install(TARGETS friendiqa DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES assets/de.manic.Friendiqa.desktop DESTINATION share/applications)
install(FILES assets/de.manic.Friendiqa.svg DESTINATION share/icons/hicolor/scalable/apps)

141
src/application.qrc Normal file
View file

@ -0,0 +1,141 @@
<RCC>
<qresource prefix="/">
<file>qtquickcontrols2.conf</file>
<file>qml/friendiqa.qml</file>
<file>qml/newsqml/NewsStack.qml</file>
<file>qml/newsqml/NewsTab.qml</file>
<file>qml/newsqml/Newsitem.qml</file>
<file>qml/newsqml/MessageSend.qml</file>
<file>qml/newsqml/Conversation.qml</file>
<file>qml/newsqml/FriendicaActivities.qml</file>
<file>qml/newsqml/Hashtag.qml</file>
<file>qml/newsqml/NewsImage.qml</file>
<file>qml/newsqml/NewsVideo.qml</file>
<file>qml/newsqml/ContactPage.qml</file>
<file>qml/newsqml/NewsVideoLarge.qml</file>
<file>qml/newsqml/SmileyDialog.qml</file>
<file>qml/contactqml/FriendsTab.qml</file>
<file>qml/contactqml/GroupComponent.qml</file>
<file>qml/contactqml/ProfileComponent.qml</file>
<file>qml/contactqml/Contactlist.qml</file>
<file>qml/photoqml/PhotoComponent.qml</file>
<file>qml/photoqml/PhotogroupComponent.qml</file>
<file>qml/photoqml/PhotoTab.qml</file>
<file>qml/photoqml/ImageUploadDialog.qml</file>
<file>qml/genericqml/ImagePicker.qml</file>
<file>qml/genericqml/ImagePickerLinux.qml</file>
<file>qml/genericqml/IntentReceiver.qml</file>
<file>qml/genericqml/MButton.qml</file>
<file>qml/genericqml/LinuxSync.qml</file>
<file>qml/genericqml/BlueButton.qml</file>
<file>qml/genericqml/ContactComponent.qml</file>
<file>qml/genericqml/PermissionDialog.qml</file>
<file>qml/calendarqml/CalendarTab.qml</file>
<file>qml/calendarqml/CalendarDay.qml</file>
<file>qml/calendarqml/EventList.qml</file>
<file>qml/configqml/AccountPage.qml</file>
<file>qml/configqml/SyncConfig.qml</file>
<file>qml/configqml/SyncComponent.qml</file>
<file>qml/configqml/InfoBox.qml</file>
<file>qml/configqml/ConfigPage.qml</file>
<file>qml/configqml/OSSettingsAndroid.qml</file>
<file>qml/configqml/OSSettingsLinux.qml</file>
<file>js/image.js</file>
<file>js/photoworker.js</file>
<file>js/service.js</file>
<file>js/news.js</file>
<file>js/newsworker.js</file>
<file>js/helper.js</file>
<file>js/smiley.js</file>
<file>translations/friendiqa-it.ts</file>
<file>translations/friendiqa-it.qm</file>
<file>translations/friendiqa-de.qm</file>
<file>translations/friendiqa-de.ts</file>
<file>translations/friendiqa-es.qm</file>
<file>translations/friendiqa-es.ts</file>
<file>assets/defaultcontact.jpg</file>
<file>assets/folder-blue.png</file>
<file>common/filesystem.cpp</file>
<file>common/filesystem.h</file>
<file>common/friendiqa.cpp</file>
<file>common/uploadableimage.cpp</file>
<file>common/uploadableimage.h</file>
<file>common/xhr.cpp</file>
<file>common/xhr.h</file>
<file>qml/newsqml/MoreComments.qml</file>
<file>qml/newsqml/NewsPhotolist.qml</file>
<file>qml/genericqml/DrawerAccountComponent.qml</file>
<file>qml/configqml/LeftDrawerScrollview.qml</file>
<file>qml/genericqml/LeftDrawerLinux.qml</file>
<file>qml/genericqml/LeftDrawerAndroid.qml</file>
<file>qml/genericqml/DrawerAccountComponentContacts.qml</file>
<file>qml/contactqml/ProfileTab.qml</file>
<file>qml/contactqml/FriendsListTab.qml</file>
<file>qml/contactqml/GroupsListTab.qml</file>
<file>qml/calendarqml/EventListItem.qml</file>
<file>translations/friendiqa-hu.qm</file>
<file>translations/friendiqa-hu.ts</file>
<file>assets/Friendiqa.png</file>
<file>assets/Friendica_monochrome.png</file>
<file>qml/configqml/ConfigAppearancePage.qml</file>
<file>qml/configqml/ConfigStartPage.qml</file>
<file>qml/contactqml/ContactsSearchPage.qml</file>
<file>assets/Friendiqa.ico</file>
<file>qml/calendarqml/EventCreate.qml</file>
<file>qml/newsqml/BlockUser.qml</file>
<file>qml/newsqml/ReportUser.qml</file>
<file>qml/newsqml/MessageImageUploadDialog.qml</file>
<file>qml/configqml/AcceptRules.qml</file>
<file>translations/friendiqa-nl.qm</file>
<file>translations/friendiqa-nl.ts</file>
<file>qml/newsqml/NewsTabbutton.qml</file>
<file>qml/genericqml/RootStack.qml</file>
<file>assets/icons/bars.svg</file>
<file>assets/icons/bell.svg</file>
<file>assets/icons/calendar.svg</file>
<file>assets/icons/caret-down.svg</file>
<file>assets/icons/check.svg</file>
<file>assets/icons/envelope.svg</file>
<file>assets/icons/exchange.svg</file>
<file>assets/icons/globe.svg</file>
<file>assets/icons/home.svg</file>
<file>assets/icons/list.svg</file>
<file>assets/icons/pencil.svg</file>
<file>assets/icons/picture-o.svg</file>
<file>assets/icons/refresh.svg</file>
<file>assets/icons/search.svg</file>
<file>assets/icons/star.svg</file>
<file>assets/icons/times-circle.svg</file>
<file>assets/icons/trash.svg</file>
<file>assets/icons/users.svg</file>
<file>assets/icons/comments.svg</file>
<file>assets/icons/history.svg</file>
<file>assets/icons/sign-out.svg</file>
<file>assets/icons/address-card.svg</file>
<file>assets/icons/star-o.svg</file>
<file>assets/icons/cogs.svg</file>
<file>assets/icons/paper-plane-o.svg</file>
<file>assets/icons/font.svg</file>
<file>assets/icons/hashtag.svg</file>
<file>assets/icons/code.svg</file>
<file>assets/icons/italic.svg</file>
<file>assets/icons/bold.svg</file>
<file>assets/icons/smile-o.svg</file>
<file>assets/icons/frown-o.svg</file>
<file>assets/icons/chevron-down.svg</file>
<file>assets/icons/chevron-up.svg</file>
<file>assets/icons/user-plus.svg</file>
<file>assets/icons/filter.svg</file>
<file>assets/icons/plus.svg</file>
<file>assets/icons/cloud-upload.svg</file>
<file>assets/icons/cloud-download.svg</file>
<file>assets/icons/repeat.svg</file>
<file>assets/icons/times.svg</file>
<file>assets/icons/play.svg</file>
<file>assets/icons/angle-right.svg</file>
<file>assets/icons/angle-left.svg</file>
<file>assets/icons/floppy-o.svg</file>
<file>assets/icons/unlock.svg</file>
<file>assets/icons/lock.svg</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

BIN
src/assets/Friendiqa.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

BIN
src/assets/Friendiqa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

View file

@ -0,0 +1,10 @@
[Desktop Entry]
Version=1.0
Type=Application
Exec=friendiqa %u
Icon=de.manic.Friendiqa.svg
Terminal=false
Name=Friendiqa
GenericName=Social Media
Comment= App for social network Friendica
Categories=Network

View file

@ -0,0 +1,36 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="76.8pt" height="76.8pt">
<defs>
<linearGradient id="gradient0" gradientTransform="matrix(1 0 0 0.545455 0 0)" gradientUnits="objectBoundingBox" x1="0.45865580612" y1="0.00000000000" x2="0.55207618367" y2="1.94989254167" spreadMethod="pad">
<stop stop-color="#ffffff" offset="0.00000000000" stop-opacity="0.74375524529"/>
<stop stop-color="#ffffff" offset="1.00000000000" stop-opacity="0.00000000000"/>
</linearGradient>
<filter id="filter0" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox" x="-0.02987755100" y="-0.12200000000" width="1.05975510000" height="1.24400000000">
<feGaussianBlur result="" in="" x="0.00000000000" y="0.00000000000" width="1.00000000000" height="1.00000000000" stdDeviation="0.0155612, 0.0635417"/>
</filter>
<linearGradient id="gradient1" gradientTransform="matrix(1 0 0 0.5 0 -24)" gradientUnits="objectBoundingBox" x1="0.54184075000" y1="4.00000000000" x2="0.44653682292" y2="1.74324879167" spreadMethod="pad">
<stop stop-color="#000000" offset="0.00000000000" stop-opacity="0.50000762951"/>
<stop stop-color="#818080" offset="1.00000000000" stop-opacity="0.00000000000"/>
</linearGradient>
<filter id="filter1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox" x="-0.03000000000" y="-0.12000000000" width="1.06000000000" height="1.24000000000">
<feGaussianBlur result="" in="" x="0.00000000000" y="0.00000000000" width="1.00000000000" height="1.00000000000" stdDeviation="0.015625, 0.0625"/>
</filter>
</defs>
<g id="layer1">
<path id="rect2993" transform="" fill="#ffc019" d="M16 0C7.0091 0.0430825 0 7.05218 0 16C0 16 0 57.4991 0 80C0 89.1201 7.0091 96 16 96L32 96L32 70L64 70L63.916 46.0684L32 46.2363L32 26L64 26L64 0C64 0 24 0 16 0Z"/>
<path id="rect2993-6" transform="translate(32, 0)" fill="#f6f7fc" d="M48 96C56.9909 96 64.0862 89.034 64 80C64 80 64 38.5009 64 16C64.0777 6.98361 56.9326 0 48 0L32 0L32 26L0 26L0 48L32 48L32 70L0 70L0 96C0 96 32 96 48 96Z"/>
</g><g id="g3997">
<path id="path3999" transform="translate(32, 0)" fill="none" stroke="#000000" stroke-width="4.00000000000" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4.00000000000" d="M32 0L32 26L0 26L0 48M32 48L32 70L0 70L0 96"/>
<rect id="rect4001" transform="" fill="none" stroke="#000000" stroke-width="4.00000000000" stroke-linecap="round" stroke-linejoin="round" width="76.80000000000pt" height="76.80000000000pt" rx="12.80000000000pt" ry="12.80000000000pt"/>
</g><g id="layer3">
<path id="path3926" transform="translate(32, 0)" fill="none" stroke="#000000" stroke-width="4.00000000000" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4.00000000000" d="M32 0L32 26L0 26L0 48L32 48L32 70L0 70L0 96"/>
<rect id="rect3928" transform="" fill="none" stroke="#000000" stroke-width="4.00000000000" stroke-linecap="round" stroke-linejoin="round" width="76.80000000000pt" height="76.80000000000pt" rx="12.80000000000pt" ry="12.80000000000pt"/>
</g><g id="layer2">
<rect id="rect3823" transform="translate(0, -2.48032)" fill="#000000" fill-opacity="0.00000000000" width="76.80000000000pt" height="38.43495200000pt" rx="12.17173120000pt" ry="12.17251520000pt"/>
<rect id="rect3823-8" transform="matrix(1 0 0 -1 1.55431e-14 96)" fill="#000000" fill-opacity="0.00000000000" width="76.80000000000pt" height="38.29376800000pt" rx="12.17173120000pt" ry="12.12780160000pt"/>
<rect id="rect4003" transform="matrix(1.02961 0 0 1.19638 -2.90192 -4.71321)" fill="url(#gradient0)" filter="url(#filter0)" width="78.40000000000pt" height="19.20000000000pt" rx="12.17173120000pt" ry="6.63955384000pt"/>
<rect id="rect4013" transform="matrix(0.976833 0 0 0.919746 1.16496 74.3199)" fill="url(#gradient1)" filter="url(#filter1)" opacity="0.56746030000" width="76.80000000000pt" height="19.20000000000pt" rx="11.20668480000pt" ry="9.60000000000pt"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>de.manic.friendiqa</id>
<name>Friendiqa</name>
<summary>Qt client for the social network Friendica</summary>
<metadata_license>FSFAP</metadata_license>
<project_license>GPL-3.0-or-later</project_license>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
</supports>
<description>
<p>
Qt based client for the Friendica Social Network. Tabs for news (incl. Direct Messages), friends, photos and events.
</p>
</description>
<launchable type="desktop-id">de.manic.Friendiqa.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>https://friendiqa.ma-nic.de/ScreenshotsDesktop/Screenshot_Desktop_Timeline_small.png</image>
</screenshot>
<screenshot>
<image>https://friendiqa.ma-nic.de/Screenshots/PhotoTab.jpg</image>
</screenshot>
<screenshot>
<image>https://friendiqa.ma-nic.de/Screenshots/EventsTab.jpg</image>
</screenshot>
</screenshots>
</component>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/assets/folder-blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,707 @@
<!DOCTYPE html>
<html lang="de-DE" class="theme-forgejo-auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Friendiqa/friendica-tray-black.svg an master - Friendiqa - Friendica</title>
<link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiRnJpZW5kaWNhIiwic2hvcnRfbmFtZSI6IkZyaWVuZGljYSIsInN0YXJ0X3VybCI6Imh0dHBzOi8vZ2l0LmZyaWVuZGkuY2EvIiwiaWNvbnMiOlt7InNyYyI6Imh0dHBzOi8vZ2l0LmZyaWVuZGkuY2EvYXNzZXRzL2ltZy9sb2dvLnBuZyIsInR5cGUiOiJpbWFnZS9wbmciLCJzaXplcyI6IjUxMng1MTIifSx7InNyYyI6Imh0dHBzOi8vZ2l0LmZyaWVuZGkuY2EvYXNzZXRzL2ltZy9sb2dvLnN2ZyIsInR5cGUiOiJpbWFnZS9zdmcreG1sIiwic2l6ZXMiOiI1MTJ4NTEyIn1dfQ==">
<meta name="theme-color" content="#6cc644">
<meta name="default-theme" content="forgejo-auto">
<meta name="author" content="MorsMortium">
<meta name="description" content="Friendiqa - Qt/QML App for Friendiqa ">
<meta name="keywords" content="git,forge,forgejo">
<meta name="referrer" content="no-referrer">
<link rel="alternate" type="application/atom+xml" title="" href="/MorsMortium/Friendiqa.atom">
<link rel="alternate" type="application/rss+xml" title="" href="/MorsMortium/Friendiqa.rss">
<link rel="icon" href="/assets/img/favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="/assets/img/favicon.png" type="image/png">
<link rel="stylesheet" href="/assets/css/index.css?v=1.19.0~2">
<script>
window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
window.config = {
appUrl: 'https:\/\/git.friendi.ca\/',
appSubUrl: '',
assetVersionEncoded: encodeURIComponent('1.19.0~2'),
assetUrlPrefix: '\/assets',
runModeIsProd: true ,
customEmojis: {"codeberg":":codeberg:","forgejo":":forgejo:","git":":git:","gitea":":gitea:","github":":github:","gitlab":":gitlab:","gogs":":gogs:"},
useServiceWorker: false ,
csrfToken: 'j3AMY83WEMWZdgwpxwqbuqbFxoE6MTY4NzcwMzA1ODE4MzkyMDQyOQ',
pageData: {},
requireTribute: null ,
notificationSettings: {"EventSourceUpdateTime":10000,"MaxTimeout":60000,"MinTimeout":10000,"TimeoutStep":10000},
enableTimeTracking: true ,
mermaidMaxSourceCharacters: 5000 ,
i18n: {
copy_success: 'Kopiert!',
copy_error: 'Kopieren fehlgeschlagen',
error_occurred: 'Ein Fehler ist aufgetreten',
network_error: 'Netzwerkfehler',
},
};
window.config.pageData = window.config.pageData || {};
</script>
<script src="/assets/js/webcomponents.js?v=1.19.0~2"></script>
<noscript>
<style>
.dropdown:hover > .menu { display: block; }
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
</style>
</noscript>
<meta property="og:title" content="Friendiqa">
<meta property="og:url" content="https://git.friendi.ca/MorsMortium/Friendiqa">
<meta property="og:description" content="Qt/QML App for Friendiqa ">
<meta property="og:type" content="object">
<meta property="og:image" content="https://git.friendi.ca/avatars/082870e8cbbfe310f1eb7f6c3e2253f1">
<meta property="og:site_name" content="Friendica">
<link rel="stylesheet" href="/assets/css/theme-forgejo-auto.css?v=1.19.0~2">
</head>
<body>
<div class="full height">
<noscript>Diese Webseite funktioniert besser mit JavaScript.</noscript>
<div class="ui top secondary stackable main menu following bar light no-vertical-tabs">
<nav class="ui container" id="navbar" aria-label="Navigation Bar">
<div class="item brand gt-sb">
<a href="/" aria-label="Startseite">
<img width="30" height="30" src="/assets/img/logo.svg" alt="Logo" aria-hidden="true">
</a>
<div class="gt-df gt-ac">
<button class="ui icon button mobile-only" id="navbar-expand-toggle">
<svg viewBox="0 0 16 16" class="svg octicon-three-bars" width="16" height="16" aria-hidden="true"><path d="M1 2.75A.75.75 0 0 1 1.75 2h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 2.75Zm0 5A.75.75 0 0 1 1.75 7h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 7.75ZM1.75 12h12.5a.75.75 0 0 1 0 1.5H1.75a.75.75 0 0 1 0-1.5Z"/></svg>
</button>
</div>
</div>
<a class="item " href="/explore/repos">Erkunden</a>
<a class="item" target="_blank" rel="noopener noreferrer" href="https://forgejo.org/docs/latest/">Hilfe</a>
<div class="right stackable menu">
<a class="item" href="/user/sign_up">
<svg viewBox="0 0 16 16" class="svg octicon-person" width="16" height="16" aria-hidden="true"><path d="M10.561 8.073a6.005 6.005 0 0 1 3.432 5.142.75.75 0 1 1-1.498.07 4.5 4.5 0 0 0-8.99 0 .75.75 0 0 1-1.498-.07 6.004 6.004 0 0 1 3.431-5.142 3.999 3.999 0 1 1 5.123 0ZM10.5 5a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z"/></svg> Registrieren
</a>
<a class="item" rel="nofollow" href="/user/login?redirect_to=%2fMorsMortium%2fFriendiqa%2fsrc%2fbranch%2fmaster%2fsource-linux%2fimages%2ffriendica-tray-black.svg">
<svg viewBox="0 0 16 16" class="svg octicon-sign-in" width="16" height="16" aria-hidden="true"><path d="M2 2.75C2 1.784 2.784 1 3.75 1h2.5a.75.75 0 0 1 0 1.5h-2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h2.5a.75.75 0 0 1 0 1.5h-2.5A1.75 1.75 0 0 1 2 13.25Zm6.56 4.5h5.69a.75.75 0 0 1 0 1.5H8.56l1.97 1.97a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L6.22 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734Z"/></svg> Anmelden
</a>
</div>
</nav>
</div>
<div role="main" aria-label="Friendiqa/friendica-tray-black.svg an master" class="page-content repository file list ">
<div class="header-wrapper">
<div class="ui container">
<div class="repo-header">
<div class="repo-title-wrap gt-df gt-fc">
<div class="repo-title" role="heading" aria-level="1">
<div class="repo-icon gt-mr-3">
<svg viewBox="0 0 16 16" class="svg octicon-repo-forked" width="32" height="32" aria-hidden="true"><path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/></svg>
</div>
<a href="/MorsMortium">MorsMortium</a>
<div class="gt-mx-2">/</div>
<a href="/MorsMortium/Friendiqa">Friendiqa</a>
<a href="/MorsMortium/Friendiqa.rss"><i class="ui grey icon tooltip gt-ml-3" data-content="RSS Feed" data-position="top center"><svg viewBox="0 0 16 16" class="svg octicon-rss" width="18" height="18" aria-hidden="true"><path d="M2.002 2.725a.75.75 0 0 1 .797-.699C8.79 2.42 13.58 7.21 13.974 13.201a.75.75 0 0 1-1.497.098 10.502 10.502 0 0 0-9.776-9.776.747.747 0 0 1-.7-.798ZM2.84 7.05h-.002a7.002 7.002 0 0 1 6.113 6.111.75.75 0 0 1-1.49.178 5.503 5.503 0 0 0-4.8-4.8.75.75 0 0 1 .179-1.489ZM2 13a1 1 0 1 1 2 0 1 1 0 0 1-2 0Z"/></svg></i></a>
<div class="labels gt-df gt-ac gt-fw">
</div>
</div>
<div class="fork-flag">geforkt von <a href="/lubuwest/Friendiqa">lubuwest/Friendiqa</a></div>
</div>
<div class="repo-buttons">
<form method="post" action="/MorsMortium/Friendiqa/action/watch?redirect_to=%2fMorsMortium%2fFriendiqa%2fsrc%2fbranch%2fmaster%2fsource-linux%2fimages%2ffriendica-tray-black.svg">
<input type="hidden" name="_csrf" value="j3AMY83WEMWZdgwpxwqbuqbFxoE6MTY4NzcwMzA1ODE4MzkyMDQyOQ">
<div class="ui labeled button tooltip" data-content="Melde dich an, um dieses Repository zu beobachten." data-position="top center">
<button type="submit" class="ui compact small basic button" disabled>
<svg viewBox="0 0 16 16" class="svg octicon-eye" width="16" height="16" aria-hidden="true"><path d="M8 2c1.981 0 3.671.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.45.678-1.367 1.932-2.637 3.023C11.67 13.008 9.981 14 8 14c-1.981 0-3.671-.992-4.933-2.078C1.797 10.83.88 9.576.43 8.898a1.62 1.62 0 0 1 0-1.798c.45-.677 1.367-1.931 2.637-3.022C4.33 2.992 6.019 2 8 2ZM1.679 7.932a.12.12 0 0 0 0 .136c.411.622 1.241 1.75 2.366 2.717C5.176 11.758 6.527 12.5 8 12.5c1.473 0 2.825-.742 3.955-1.715 1.124-.967 1.954-2.096 2.366-2.717a.12.12 0 0 0 0-.136c-.412-.621-1.242-1.75-2.366-2.717C10.824 4.242 9.473 3.5 8 3.5c-1.473 0-2.825.742-3.955 1.715-1.124.967-1.954 2.096-2.366 2.717ZM8 10a2 2 0 1 1-.001-3.999A2 2 0 0 1 8 10Z"/></svg>Beobachten
</button>
<a class="ui basic label" href="/MorsMortium/Friendiqa/watchers">
1
</a>
</div>
</form>
<form method="post" action="/MorsMortium/Friendiqa/action/star?redirect_to=%2fMorsMortium%2fFriendiqa%2fsrc%2fbranch%2fmaster%2fsource-linux%2fimages%2ffriendica-tray-black.svg">
<input type="hidden" name="_csrf" value="j3AMY83WEMWZdgwpxwqbuqbFxoE6MTY4NzcwMzA1ODE4MzkyMDQyOQ">
<div class="ui labeled button tooltip" data-content="Bitte melde dich an, um dieses Repository zu favorisieren." data-position="top center">
<button type="submit" class="ui compact small basic button" disabled>
<svg viewBox="0 0 16 16" class="svg octicon-star" width="16" height="16" aria-hidden="true"><path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"/></svg>Favorisieren
</button>
<a class="ui basic label" href="/MorsMortium/Friendiqa/stars">
0
</a>
</div>
</form>
<div class="ui labeled button
tooltip disabled
"
data-content="Bitte melde dich an, um dieses Repository zu forken."
data-position="top center">
<a class="ui compact small basic button"
>
<svg viewBox="0 0 16 16" class="svg octicon-repo-forked" width="16" height="16" aria-hidden="true"><path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/></svg>Fork
</a>
<div class="ui small modal" id="fork-repo-modal">
<svg viewBox="0 0 16 16" class="close inside svg octicon-x" width="16" height="16" aria-hidden="true"><path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"/></svg>
<div class="header">
Du hast bereits einen Fork von Friendiqa erstellt
</div>
<div class="content gt-tl">
<div class="ui list">
</div>
</div>
</div>
<a class="ui basic label" href="/MorsMortium/Friendiqa/forks">
0
</a>
</div>
</div>
</div>
</div>
<div class="ui tabs container">
<div class="ui tabular stackable menu navbar">
<a class="active item" href="/MorsMortium/Friendiqa">
<svg viewBox="0 0 16 16" class="svg octicon-code" width="16" height="16" aria-hidden="true"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"/></svg> Code
</a>
<a class="item" href="/MorsMortium/Friendiqa/issues">
<svg viewBox="0 0 16 16" class="svg octicon-issue-opened" width="16" height="16" aria-hidden="true"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"/><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"/></svg> Issues
</a>
<a class="item" href="/MorsMortium/Friendiqa/pulls">
<svg viewBox="0 0 16 16" class="svg octicon-git-pull-request" width="16" height="16" aria-hidden="true"><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"/></svg> Pull-Requests
</a>
<a href="/MorsMortium/Friendiqa/packages" class="item">
<svg viewBox="0 0 16 16" class="svg octicon-package" width="16" height="16" aria-hidden="true"><path d="m8.878.392 5.25 3.045c.54.314.872.89.872 1.514v6.098a1.75 1.75 0 0 1-.872 1.514l-5.25 3.045a1.75 1.75 0 0 1-1.756 0l-5.25-3.045A1.75 1.75 0 0 1 1 11.049V4.951c0-.624.332-1.201.872-1.514L7.122.392a1.75 1.75 0 0 1 1.756 0ZM7.875 1.69l-4.63 2.685L8 7.133l4.755-2.758-4.63-2.685a.248.248 0 0 0-.25 0ZM2.5 5.677v5.372c0 .09.047.171.125.216l4.625 2.683V8.432Zm6.25 8.271 4.625-2.683a.25.25 0 0 0 .125-.216V5.677L8.75 8.432Z"/></svg> Pakete
</a>
<a href="/MorsMortium/Friendiqa/projects" class="item">
<svg viewBox="0 0 16 16" class="svg octicon-project" width="16" height="16" aria-hidden="true"><path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 1.75v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25ZM11.75 3a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-1.5 0v-7.5a.75.75 0 0 1 .75-.75Zm-8.25.75a.75.75 0 0 1 1.5 0v5.5a.75.75 0 0 1-1.5 0ZM8 3a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 3Z"/></svg> Projekte
</a>
<a class="item" href="/MorsMortium/Friendiqa/releases">
<svg viewBox="0 0 16 16" class="svg octicon-tag" width="16" height="16" aria-hidden="true"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"/></svg> Releases
</a>
<a class="item" href="/MorsMortium/Friendiqa/wiki" >
<svg viewBox="0 0 16 16" class="svg octicon-book" width="16" height="16" aria-hidden="true"><path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"/></svg> Wiki
</a>
<a class="item" href="/MorsMortium/Friendiqa/activity">
<svg viewBox="0 0 16 16" class="svg octicon-pulse" width="16" height="16" aria-hidden="true"><path d="M6 2c.306 0 .582.187.696.471L10 10.731l1.304-3.26A.751.751 0 0 1 12 7h3.25a.75.75 0 0 1 0 1.5h-2.742l-1.812 4.528a.751.751 0 0 1-1.392 0L6 4.77 4.696 8.03A.75.75 0 0 1 4 8.5H.75a.75.75 0 0 1 0-1.5h2.742l1.812-4.529A.751.751 0 0 1 6 2Z"/></svg> Aktivität
</a>
</div>
</div>
<div class="ui tabs divider"></div>
</div>
<div class="ui container ">
<div class="gt-hidden" id="validate_prompt">
<span id="count_prompt">Du kannst nicht mehr als 25 Themen auswählen</span>
<span id="format_prompt">Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.</span>
</div>
<div class="repo-button-row gt-df gt-ac gt-sb gt-fw">
<div class="gt-df gt-ac gt-fw gt-gap-y-3">
<div class="fitted item choose reference">
<div class="ui floating filter dropdown custom"
data-branch-form=""
data-can-create-branch="false"
data-no-results="Keine Ergebnisse verfügbar."
data-set-action="" data-submit-form=""
data-view-type="branch"
data-ref-name="master"
data-branch-url-prefix="/MorsMortium/Friendiqa/src/branch/"
data-branch-url-suffix="/source-linux/images/friendica-tray-black.svg"
data-tag-url-prefix="/MorsMortium/Friendiqa/src/tag/"
data-tag-url-suffix="/source-linux/images/friendica-tray-black.svg">
<button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
<span class="text gt-df gt-ac gt-mr-2">
<span :class="{visible: isViewTag}" v-if="isViewTag" v-cloak><svg viewBox="0 0 16 16" class="svg octicon-tag" width="16" height="16" aria-hidden="true"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"/></svg></span>
<span :class="{visible: isViewBranch}" v-if="isViewBranch" ><svg viewBox="0 0 16 16" class="svg octicon-git-branch" width="16" height="16" aria-hidden="true"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg></span>
<span :class="{visible: isViewTree}" v-if="isViewTree" v-cloak><svg viewBox="0 0 16 16" class="svg octicon-git-branch" width="16" height="16" aria-hidden="true"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg></span>
<strong ref="dropdownRefName" class="gt-ml-3">master</strong>
</span>
<svg viewBox="0 0 16 16" class="dropdown icon svg octicon-triangle-down" width="14" height="14" aria-hidden="true"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"/></svg>
</button>
<div class="data gt-hidden" data-mode="branches">
<div class="item branch selected" data-url="master">master</div>
<div class="item tag " data-url="v0.001">v0.001</div>
<div class="item tag " data-url="v0.002">v0.002</div>
<div class="item tag " data-url="v0.004">v0.004</div>
<div class="item tag " data-url="v0.1">v0.1</div>
<div class="item tag " data-url="v0.1.2">v0.1.2</div>
<div class="item tag " data-url="v0.2">v0.2</div>
<div class="item tag " data-url="v0.2.1">v0.2.1</div>
<div class="item tag " data-url="v0.2.2">v0.2.2</div>
<div class="item tag " data-url="v0.3.1">v0.3.1</div>
<div class="item tag " data-url="v0.3.2">v0.3.2</div>
<div class="item tag " data-url="v0.3.3">v0.3.3</div>
<div class="item tag " data-url="v0.3.4">v0.3.4</div>
<div class="item tag " data-url="v0.5">v0.5</div>
<div class="item tag " data-url="v0.5.1">v0.5.1</div>
<div class="item tag " data-url="v0.5.2">v0.5.2</div>
<div class="item tag " data-url="v0.5.3">v0.5.3</div>
<div class="item tag " data-url="v0.5.4">v0.5.4</div>
<div class="item tag " data-url="v0.5.4.1">v0.5.4.1</div>
<div class="item tag " data-url="v0.6">v0.6</div>
<div class="item tag " data-url="v0.6.1">v0.6.1</div>
<div class="item tag " data-url="v0.6.2">v0.6.2</div>
<div class="item tag " data-url="v0.6.3">v0.6.3</div>
<div class="item tag " data-url="v0.6.5">v0.6.5</div>
<div class="item tag " data-url="v0.6.6">v0.6.6</div>
</div>
<div class="menu transition" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak>
<div class="ui icon search input">
<i class="icon gt-df gt-ac gt-jc gt-m-0"><svg viewBox="0 0 16 16" class="svg octicon-filter" width="16" height="16" aria-hidden="true"><path d="M.75 3h14.5a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1 0-1.5ZM3 7.75A.75.75 0 0 1 3.75 7h8.5a.75.75 0 0 1 0 1.5h-8.5A.75.75 0 0 1 3 7.75Zm3 4a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"/></svg></i>
<input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" placeholder="Branch oder Tag filtern...">
</div>
<div class="header branch-tag-choice">
<div class="ui grid">
<div class="two column row">
<a class="reference column" href="#" @click="createTag = false; mode = 'branches'; focusSearchField()">
<span class="text" :class="{black: mode == 'branches'}">
<svg viewBox="0 0 16 16" class="gt-mr-2 svg octicon-git-branch" width="16" height="16" aria-hidden="true"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg>Branches
</span>
</a>
<a class="reference column" href="#" @click="createTag = true; mode = 'tags'; focusSearchField()">
<span class="text" :class="{black: mode == 'tags'}">
<svg viewBox="0 0 16 16" class="gt-mr-2 svg octicon-tag" width="16" height="16" aria-hidden="true"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"/></svg>Tags
</span>
</a>
</div>
</div>
</div>
<div class="scrolling menu" ref="scrollContainer">
<div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active == index}" @click="selectItem(item)" :ref="'listItem' + index">${ item.name }</div>
<div class="item" v-if="showCreateNewBranch" :class="{active: active == filteredItems.length}" :ref="'listItem' + filteredItems.length">
<a href="#" @click="createNewBranch()">
<div v-show="createTag">
<i class="reference tags icon"></i>
Tag <strong>${ searchTerm }</strong> erstellen
</div>
<div v-show="!createTag">
<svg viewBox="0 0 16 16" class="svg octicon-git-branch" width="16" height="16" aria-hidden="true"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg>
Erstelle Branch <strong>${ searchTerm }</strong>
</div>
<div class="text small">
von „master“
</div>
</a>
<form ref="newBranchForm" action="/MorsMortium/Friendiqa/branches/_new/branch/master" method="post">
<input type="hidden" name="_csrf" value="j3AMY83WEMWZdgwpxwqbuqbFxoE6MTY4NzcwMzA1ODE4MzkyMDQyOQ">
<input type="hidden" name="new_branch_name" v-model="searchTerm">
<input type="hidden" name="create_tag" v-model="createTag">
<input type="hidden" name="current_path" value="source-linux/images/friendica-tray-black.svg">
</form>
</div>
</div>
<div class="message" v-if="showNoResults">${ noResults }</div>
</div>
</div>
</div>
<span class="ui breadcrumb repo-path gt-ml-2"><a class="section" href="/MorsMortium/Friendiqa/src/branch/master" title="Friendiqa">Friendiqa</a><span class="divider">/</span><span class="section"><a href="/MorsMortium/Friendiqa/src/branch/master/source-linux" title="source-linux">source-linux</a></span><span class="divider">/</span><span class="section"><a href="/MorsMortium/Friendiqa/src/branch/master/source-linux/images" title="images">images</a></span><span class="divider">/</span><span class="active section" title="friendica-tray-black.svg">friendica-tray-black.svg</span></span>
</div>
<div class="gt-df gt-ac">
</div>
</div>
<div class="tab-size-8 non-diff-file-content">
<h4 class="file-header ui top attached header gt-df gt-ac gt-sb gt-fw">
<div class="file-header-left gt-df gt-ac gt-py-3 gt-pr-4">
<div class="file-info text grey normal gt-mono">
<div class="file-info-entry">
42 Zeilen
</div>
<div class="file-info-entry">
1.7 KiB
</div>
<div class="file-info-entry">
XML
</div>
</div>
</div>
<div class="file-header-right file-actions gt-df gt-ac gt-fw">
<div class="ui compact icon buttons two-toggle-buttons">
<a href="/MorsMortium/Friendiqa/src/branch/master/source-linux/images/friendica-tray-black.svg?display=source" class="ui mini basic button tooltip " data-content="Quelltext anzeigen" data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-code" width="15" height="15" aria-hidden="true"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"/></svg></a>
<a href="/MorsMortium/Friendiqa/src/branch/master/source-linux/images/friendica-tray-black.svg" class="ui mini basic button tooltip active" data-content="Ansicht rendern" data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-file" width="15" height="15" aria-hidden="true"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"/></svg></a>
</div>
<div class="ui buttons gt-mr-2">
<a class="ui mini basic button" href="/MorsMortium/Friendiqa/raw/branch/master/source-linux/images/friendica-tray-black.svg">Originalformat</a>
<a class="ui mini basic button" href="/MorsMortium/Friendiqa/src/commit/8dc907e345eaaebbe8b81b9b098385454e3e8f45/source-linux/images/friendica-tray-black.svg">Permalink</a>
<a class="ui mini basic button" href="/MorsMortium/Friendiqa/blame/branch/master/source-linux/images/friendica-tray-black.svg">Blame</a>
<a class="ui mini basic button" href="/MorsMortium/Friendiqa/commits/branch/master/source-linux/images/friendica-tray-black.svg">Verlauf</a>
</div>
<a download href="/MorsMortium/Friendiqa/raw/branch/master/source-linux/images/friendica-tray-black.svg"><span class="btn-octicon tooltip" data-content="Datei herunterladen" data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-download" width="16" height="16" aria-hidden="true"><path d="M7.47 10.78 3.72 7.03a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l2.47 2.47V1.75a.75.75 0 0 1 1.5 0v6.69l2.47-2.47a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-3.75 3.75a.75.75 0 0 1-1.06 0ZM3.75 13h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Z"/></svg></span></a>
<a id="copy-content" class="btn-octicon tooltip" data-link="/MorsMortium/Friendiqa/raw/branch/master/source-linux/images/friendica-tray-black.svg" data-content="Copy content"><svg viewBox="0 0 16 16" class="svg octicon-copy" width="14" height="14" aria-hidden="true"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></a>
<span class="btn-octicon tooltip disabled" data-content="Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen." data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-pencil" width="16" height="16" aria-hidden="true"><path d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 0 1-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61Zm.176 4.823L9.75 4.81l-6.286 6.287a.253.253 0 0 0-.064.108l-.558 1.953 1.953-.558a.253.253 0 0 0 .108-.064Zm1.238-3.763a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Z"/></svg></span>
<span class="btn-octicon tooltip disabled" data-content="Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen." data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-trash" width="16" height="16" aria-hidden="true"><path d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"/></svg></span>
</div>
</h4>
<div class="ui attached table unstackable segment">
<div class="file-view">
<div class="view-raw ui center">
<img src="/MorsMortium/Friendiqa/raw/branch/master/source-linux/images/friendica-tray-black.svg">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer role="group" aria-label="Footer">
<div class="ui container">
<div class="ui left" role="contentinfo" aria-label="About Software">
<a target="_blank" rel="noopener noreferrer" href="https://forgejo.org">Powered by Forgejo</a>
Version:
1.19.0&#43;2
Seite: <strong>37ms</strong>
Template: <strong>1ms</strong>
</div>
<div class="ui right links" role="group" aria-label="Links">
<div class="ui language bottom floating slide up dropdown link item">
<svg viewBox="0 0 16 16" class="svg octicon-globe" width="16" height="16" aria-hidden="true"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM5.78 8.75a9.64 9.64 0 0 0 1.363 4.177c.255.426.542.832.857 1.215.245-.296.551-.705.857-1.215A9.64 9.64 0 0 0 10.22 8.75Zm4.44-1.5a9.64 9.64 0 0 0-1.363-4.177c-.307-.51-.612-.919-.857-1.215a9.927 9.927 0 0 0-.857 1.215A9.64 9.64 0 0 0 5.78 7.25Zm-5.944 1.5H1.543a6.507 6.507 0 0 0 4.666 5.5c-.123-.181-.24-.365-.352-.552-.715-1.192-1.437-2.874-1.581-4.948Zm-2.733-1.5h2.733c.144-2.074.866-3.756 1.58-4.948.12-.197.237-.381.353-.552a6.507 6.507 0 0 0-4.666 5.5Zm10.181 1.5c-.144 2.074-.866 3.756-1.58 4.948-.12.197-.237.381-.353.552a6.507 6.507 0 0 0 4.666-5.5Zm2.733-1.5a6.507 6.507 0 0 0-4.666-5.5c.123.181.24.365.353.552.714 1.192 1.436 2.874 1.58 4.948Z"/></svg>
<span>Deutsch</span>
<div class="menu language-menu">
<a lang="id-ID" data-url="/?lang=id-ID" class="item ">Bahasa Indonesia</a>
<a lang="de-DE" data-url="/?lang=de-DE" class="item active selected">Deutsch</a>
<a lang="en-US" data-url="/?lang=en-US" class="item ">English</a>
<a lang="es-ES" data-url="/?lang=es-ES" class="item ">Español</a>
<a lang="fr-FR" data-url="/?lang=fr-FR" class="item ">Français</a>
<a lang="it-IT" data-url="/?lang=it-IT" class="item ">Italiano</a>
<a lang="lv-LV" data-url="/?lang=lv-LV" class="item ">Latviešu</a>
<a lang="hu-HU" data-url="/?lang=hu-HU" class="item ">Magyar nyelv</a>
<a lang="nl-NL" data-url="/?lang=nl-NL" class="item ">Nederlands</a>
<a lang="pl-PL" data-url="/?lang=pl-PL" class="item ">Polski</a>
<a lang="pt-PT" data-url="/?lang=pt-PT" class="item ">Português de Portugal</a>
<a lang="pt-BR" data-url="/?lang=pt-BR" class="item ">Português do Brasil</a>
<a lang="fi-FI" data-url="/?lang=fi-FI" class="item ">Suomi</a>
<a lang="sv-SE" data-url="/?lang=sv-SE" class="item ">Svenska</a>
<a lang="tr-TR" data-url="/?lang=tr-TR" class="item ">Türkçe</a>
<a lang="cs-CZ" data-url="/?lang=cs-CZ" class="item ">Čeština</a>
<a lang="el-GR" data-url="/?lang=el-GR" class="item ">Ελληνικά</a>
<a lang="bg-BG" data-url="/?lang=bg-BG" class="item ">Български</a>
<a lang="ru-RU" data-url="/?lang=ru-RU" class="item ">Русский</a>
<a lang="uk-UA" data-url="/?lang=uk-UA" class="item ">Українська</a>
<a lang="fa-IR" data-url="/?lang=fa-IR" class="item ">فارسی</a>
<a lang="ml-IN" data-url="/?lang=ml-IN" class="item ">മലയാളം</a>
<a lang="ja-JP" data-url="/?lang=ja-JP" class="item ">日本語</a>
<a lang="zh-CN" data-url="/?lang=zh-CN" class="item ">简体中文</a>
<a lang="zh-TW" data-url="/?lang=zh-TW" class="item ">繁體中文(台灣)</a>
<a lang="zh-HK" data-url="/?lang=zh-HK" class="item ">繁體中文(香港)</a>
<a lang="ko-KR" data-url="/?lang=ko-KR" class="item ">한국어</a>
</div>
</div>
<a href="/assets/js/licenses.txt">Lizenzen</a>
<a href="/api/swagger">API</a>
</div>
</div>
</footer>
<script src="/assets/js/index.js?v=1.19.0~2" onerror="alert('Failed to load asset files from ' + this.src + '. Please make sure the asset files can be accessed.')"></script>
</body>
</html>

View file

@ -0,0 +1,707 @@
<!DOCTYPE html>
<html lang="de-DE" class="theme-forgejo-auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Friendiqa/friendica-tray-white.svg an master - Friendiqa - Friendica</title>
<link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiRnJpZW5kaWNhIiwic2hvcnRfbmFtZSI6IkZyaWVuZGljYSIsInN0YXJ0X3VybCI6Imh0dHBzOi8vZ2l0LmZyaWVuZGkuY2EvIiwiaWNvbnMiOlt7InNyYyI6Imh0dHBzOi8vZ2l0LmZyaWVuZGkuY2EvYXNzZXRzL2ltZy9sb2dvLnBuZyIsInR5cGUiOiJpbWFnZS9wbmciLCJzaXplcyI6IjUxMng1MTIifSx7InNyYyI6Imh0dHBzOi8vZ2l0LmZyaWVuZGkuY2EvYXNzZXRzL2ltZy9sb2dvLnN2ZyIsInR5cGUiOiJpbWFnZS9zdmcreG1sIiwic2l6ZXMiOiI1MTJ4NTEyIn1dfQ==">
<meta name="theme-color" content="#6cc644">
<meta name="default-theme" content="forgejo-auto">
<meta name="author" content="MorsMortium">
<meta name="description" content="Friendiqa - Qt/QML App for Friendiqa ">
<meta name="keywords" content="git,forge,forgejo">
<meta name="referrer" content="no-referrer">
<link rel="alternate" type="application/atom+xml" title="" href="/MorsMortium/Friendiqa.atom">
<link rel="alternate" type="application/rss+xml" title="" href="/MorsMortium/Friendiqa.rss">
<link rel="icon" href="/assets/img/favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="/assets/img/favicon.png" type="image/png">
<link rel="stylesheet" href="/assets/css/index.css?v=1.19.0~2">
<script>
window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
window.config = {
appUrl: 'https:\/\/git.friendi.ca\/',
appSubUrl: '',
assetVersionEncoded: encodeURIComponent('1.19.0~2'),
assetUrlPrefix: '\/assets',
runModeIsProd: true ,
customEmojis: {"codeberg":":codeberg:","forgejo":":forgejo:","git":":git:","gitea":":gitea:","github":":github:","gitlab":":gitlab:","gogs":":gogs:"},
useServiceWorker: false ,
csrfToken: 'hRtY4yQKJpvShSAHOERpMVsB_hI6MTY4NzcwMzY4NDU0MTUwMzIyMw',
pageData: {},
requireTribute: null ,
notificationSettings: {"EventSourceUpdateTime":10000,"MaxTimeout":60000,"MinTimeout":10000,"TimeoutStep":10000},
enableTimeTracking: true ,
mermaidMaxSourceCharacters: 5000 ,
i18n: {
copy_success: 'Kopiert!',
copy_error: 'Kopieren fehlgeschlagen',
error_occurred: 'Ein Fehler ist aufgetreten',
network_error: 'Netzwerkfehler',
},
};
window.config.pageData = window.config.pageData || {};
</script>
<script src="/assets/js/webcomponents.js?v=1.19.0~2"></script>
<noscript>
<style>
.dropdown:hover > .menu { display: block; }
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
</style>
</noscript>
<meta property="og:title" content="Friendiqa">
<meta property="og:url" content="https://git.friendi.ca/MorsMortium/Friendiqa">
<meta property="og:description" content="Qt/QML App for Friendiqa ">
<meta property="og:type" content="object">
<meta property="og:image" content="https://git.friendi.ca/avatars/082870e8cbbfe310f1eb7f6c3e2253f1">
<meta property="og:site_name" content="Friendica">
<link rel="stylesheet" href="/assets/css/theme-forgejo-auto.css?v=1.19.0~2">
</head>
<body>
<div class="full height">
<noscript>Diese Webseite funktioniert besser mit JavaScript.</noscript>
<div class="ui top secondary stackable main menu following bar light no-vertical-tabs">
<nav class="ui container" id="navbar" aria-label="Navigation Bar">
<div class="item brand gt-sb">
<a href="/" aria-label="Startseite">
<img width="30" height="30" src="/assets/img/logo.svg" alt="Logo" aria-hidden="true">
</a>
<div class="gt-df gt-ac">
<button class="ui icon button mobile-only" id="navbar-expand-toggle">
<svg viewBox="0 0 16 16" class="svg octicon-three-bars" width="16" height="16" aria-hidden="true"><path d="M1 2.75A.75.75 0 0 1 1.75 2h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 2.75Zm0 5A.75.75 0 0 1 1.75 7h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 7.75ZM1.75 12h12.5a.75.75 0 0 1 0 1.5H1.75a.75.75 0 0 1 0-1.5Z"/></svg>
</button>
</div>
</div>
<a class="item " href="/explore/repos">Erkunden</a>
<a class="item" target="_blank" rel="noopener noreferrer" href="https://forgejo.org/docs/latest/">Hilfe</a>
<div class="right stackable menu">
<a class="item" href="/user/sign_up">
<svg viewBox="0 0 16 16" class="svg octicon-person" width="16" height="16" aria-hidden="true"><path d="M10.561 8.073a6.005 6.005 0 0 1 3.432 5.142.75.75 0 1 1-1.498.07 4.5 4.5 0 0 0-8.99 0 .75.75 0 0 1-1.498-.07 6.004 6.004 0 0 1 3.431-5.142 3.999 3.999 0 1 1 5.123 0ZM10.5 5a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z"/></svg> Registrieren
</a>
<a class="item" rel="nofollow" href="/user/login?redirect_to=%2fMorsMortium%2fFriendiqa%2fsrc%2fbranch%2fmaster%2fsource-linux%2fimages%2ffriendica-tray-white.svg">
<svg viewBox="0 0 16 16" class="svg octicon-sign-in" width="16" height="16" aria-hidden="true"><path d="M2 2.75C2 1.784 2.784 1 3.75 1h2.5a.75.75 0 0 1 0 1.5h-2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h2.5a.75.75 0 0 1 0 1.5h-2.5A1.75 1.75 0 0 1 2 13.25Zm6.56 4.5h5.69a.75.75 0 0 1 0 1.5H8.56l1.97 1.97a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L6.22 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734Z"/></svg> Anmelden
</a>
</div>
</nav>
</div>
<div role="main" aria-label="Friendiqa/friendica-tray-white.svg an master" class="page-content repository file list ">
<div class="header-wrapper">
<div class="ui container">
<div class="repo-header">
<div class="repo-title-wrap gt-df gt-fc">
<div class="repo-title" role="heading" aria-level="1">
<div class="repo-icon gt-mr-3">
<svg viewBox="0 0 16 16" class="svg octicon-repo-forked" width="32" height="32" aria-hidden="true"><path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/></svg>
</div>
<a href="/MorsMortium">MorsMortium</a>
<div class="gt-mx-2">/</div>
<a href="/MorsMortium/Friendiqa">Friendiqa</a>
<a href="/MorsMortium/Friendiqa.rss"><i class="ui grey icon tooltip gt-ml-3" data-content="RSS Feed" data-position="top center"><svg viewBox="0 0 16 16" class="svg octicon-rss" width="18" height="18" aria-hidden="true"><path d="M2.002 2.725a.75.75 0 0 1 .797-.699C8.79 2.42 13.58 7.21 13.974 13.201a.75.75 0 0 1-1.497.098 10.502 10.502 0 0 0-9.776-9.776.747.747 0 0 1-.7-.798ZM2.84 7.05h-.002a7.002 7.002 0 0 1 6.113 6.111.75.75 0 0 1-1.49.178 5.503 5.503 0 0 0-4.8-4.8.75.75 0 0 1 .179-1.489ZM2 13a1 1 0 1 1 2 0 1 1 0 0 1-2 0Z"/></svg></i></a>
<div class="labels gt-df gt-ac gt-fw">
</div>
</div>
<div class="fork-flag">geforkt von <a href="/lubuwest/Friendiqa">lubuwest/Friendiqa</a></div>
</div>
<div class="repo-buttons">
<form method="post" action="/MorsMortium/Friendiqa/action/watch?redirect_to=%2fMorsMortium%2fFriendiqa%2fsrc%2fbranch%2fmaster%2fsource-linux%2fimages%2ffriendica-tray-white.svg">
<input type="hidden" name="_csrf" value="hRtY4yQKJpvShSAHOERpMVsB_hI6MTY4NzcwMzY4NDU0MTUwMzIyMw">
<div class="ui labeled button tooltip" data-content="Melde dich an, um dieses Repository zu beobachten." data-position="top center">
<button type="submit" class="ui compact small basic button" disabled>
<svg viewBox="0 0 16 16" class="svg octicon-eye" width="16" height="16" aria-hidden="true"><path d="M8 2c1.981 0 3.671.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.45.678-1.367 1.932-2.637 3.023C11.67 13.008 9.981 14 8 14c-1.981 0-3.671-.992-4.933-2.078C1.797 10.83.88 9.576.43 8.898a1.62 1.62 0 0 1 0-1.798c.45-.677 1.367-1.931 2.637-3.022C4.33 2.992 6.019 2 8 2ZM1.679 7.932a.12.12 0 0 0 0 .136c.411.622 1.241 1.75 2.366 2.717C5.176 11.758 6.527 12.5 8 12.5c1.473 0 2.825-.742 3.955-1.715 1.124-.967 1.954-2.096 2.366-2.717a.12.12 0 0 0 0-.136c-.412-.621-1.242-1.75-2.366-2.717C10.824 4.242 9.473 3.5 8 3.5c-1.473 0-2.825.742-3.955 1.715-1.124.967-1.954 2.096-2.366 2.717ZM8 10a2 2 0 1 1-.001-3.999A2 2 0 0 1 8 10Z"/></svg>Beobachten
</button>
<a class="ui basic label" href="/MorsMortium/Friendiqa/watchers">
1
</a>
</div>
</form>
<form method="post" action="/MorsMortium/Friendiqa/action/star?redirect_to=%2fMorsMortium%2fFriendiqa%2fsrc%2fbranch%2fmaster%2fsource-linux%2fimages%2ffriendica-tray-white.svg">
<input type="hidden" name="_csrf" value="hRtY4yQKJpvShSAHOERpMVsB_hI6MTY4NzcwMzY4NDU0MTUwMzIyMw">
<div class="ui labeled button tooltip" data-content="Bitte melde dich an, um dieses Repository zu favorisieren." data-position="top center">
<button type="submit" class="ui compact small basic button" disabled>
<svg viewBox="0 0 16 16" class="svg octicon-star" width="16" height="16" aria-hidden="true"><path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"/></svg>Favorisieren
</button>
<a class="ui basic label" href="/MorsMortium/Friendiqa/stars">
0
</a>
</div>
</form>
<div class="ui labeled button
tooltip disabled
"
data-content="Bitte melde dich an, um dieses Repository zu forken."
data-position="top center">
<a class="ui compact small basic button"
>
<svg viewBox="0 0 16 16" class="svg octicon-repo-forked" width="16" height="16" aria-hidden="true"><path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/></svg>Fork
</a>
<div class="ui small modal" id="fork-repo-modal">
<svg viewBox="0 0 16 16" class="close inside svg octicon-x" width="16" height="16" aria-hidden="true"><path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"/></svg>
<div class="header">
Du hast bereits einen Fork von Friendiqa erstellt
</div>
<div class="content gt-tl">
<div class="ui list">
</div>
</div>
</div>
<a class="ui basic label" href="/MorsMortium/Friendiqa/forks">
0
</a>
</div>
</div>
</div>
</div>
<div class="ui tabs container">
<div class="ui tabular stackable menu navbar">
<a class="active item" href="/MorsMortium/Friendiqa">
<svg viewBox="0 0 16 16" class="svg octicon-code" width="16" height="16" aria-hidden="true"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"/></svg> Code
</a>
<a class="item" href="/MorsMortium/Friendiqa/issues">
<svg viewBox="0 0 16 16" class="svg octicon-issue-opened" width="16" height="16" aria-hidden="true"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"/><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"/></svg> Issues
</a>
<a class="item" href="/MorsMortium/Friendiqa/pulls">
<svg viewBox="0 0 16 16" class="svg octicon-git-pull-request" width="16" height="16" aria-hidden="true"><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"/></svg> Pull-Requests
</a>
<a href="/MorsMortium/Friendiqa/packages" class="item">
<svg viewBox="0 0 16 16" class="svg octicon-package" width="16" height="16" aria-hidden="true"><path d="m8.878.392 5.25 3.045c.54.314.872.89.872 1.514v6.098a1.75 1.75 0 0 1-.872 1.514l-5.25 3.045a1.75 1.75 0 0 1-1.756 0l-5.25-3.045A1.75 1.75 0 0 1 1 11.049V4.951c0-.624.332-1.201.872-1.514L7.122.392a1.75 1.75 0 0 1 1.756 0ZM7.875 1.69l-4.63 2.685L8 7.133l4.755-2.758-4.63-2.685a.248.248 0 0 0-.25 0ZM2.5 5.677v5.372c0 .09.047.171.125.216l4.625 2.683V8.432Zm6.25 8.271 4.625-2.683a.25.25 0 0 0 .125-.216V5.677L8.75 8.432Z"/></svg> Pakete
</a>
<a href="/MorsMortium/Friendiqa/projects" class="item">
<svg viewBox="0 0 16 16" class="svg octicon-project" width="16" height="16" aria-hidden="true"><path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 1.75v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25ZM11.75 3a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-1.5 0v-7.5a.75.75 0 0 1 .75-.75Zm-8.25.75a.75.75 0 0 1 1.5 0v5.5a.75.75 0 0 1-1.5 0ZM8 3a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 3Z"/></svg> Projekte
</a>
<a class="item" href="/MorsMortium/Friendiqa/releases">
<svg viewBox="0 0 16 16" class="svg octicon-tag" width="16" height="16" aria-hidden="true"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"/></svg> Releases
</a>
<a class="item" href="/MorsMortium/Friendiqa/wiki" >
<svg viewBox="0 0 16 16" class="svg octicon-book" width="16" height="16" aria-hidden="true"><path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"/></svg> Wiki
</a>
<a class="item" href="/MorsMortium/Friendiqa/activity">
<svg viewBox="0 0 16 16" class="svg octicon-pulse" width="16" height="16" aria-hidden="true"><path d="M6 2c.306 0 .582.187.696.471L10 10.731l1.304-3.26A.751.751 0 0 1 12 7h3.25a.75.75 0 0 1 0 1.5h-2.742l-1.812 4.528a.751.751 0 0 1-1.392 0L6 4.77 4.696 8.03A.75.75 0 0 1 4 8.5H.75a.75.75 0 0 1 0-1.5h2.742l1.812-4.529A.751.751 0 0 1 6 2Z"/></svg> Aktivität
</a>
</div>
</div>
<div class="ui tabs divider"></div>
</div>
<div class="ui container ">
<div class="gt-hidden" id="validate_prompt">
<span id="count_prompt">Du kannst nicht mehr als 25 Themen auswählen</span>
<span id="format_prompt">Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.</span>
</div>
<div class="repo-button-row gt-df gt-ac gt-sb gt-fw">
<div class="gt-df gt-ac gt-fw gt-gap-y-3">
<div class="fitted item choose reference">
<div class="ui floating filter dropdown custom"
data-branch-form=""
data-can-create-branch="false"
data-no-results="Keine Ergebnisse verfügbar."
data-set-action="" data-submit-form=""
data-view-type="branch"
data-ref-name="master"
data-branch-url-prefix="/MorsMortium/Friendiqa/src/branch/"
data-branch-url-suffix="/source-linux/images/friendica-tray-white.svg"
data-tag-url-prefix="/MorsMortium/Friendiqa/src/tag/"
data-tag-url-suffix="/source-linux/images/friendica-tray-white.svg">
<button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
<span class="text gt-df gt-ac gt-mr-2">
<span :class="{visible: isViewTag}" v-if="isViewTag" v-cloak><svg viewBox="0 0 16 16" class="svg octicon-tag" width="16" height="16" aria-hidden="true"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"/></svg></span>
<span :class="{visible: isViewBranch}" v-if="isViewBranch" ><svg viewBox="0 0 16 16" class="svg octicon-git-branch" width="16" height="16" aria-hidden="true"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg></span>
<span :class="{visible: isViewTree}" v-if="isViewTree" v-cloak><svg viewBox="0 0 16 16" class="svg octicon-git-branch" width="16" height="16" aria-hidden="true"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg></span>
<strong ref="dropdownRefName" class="gt-ml-3">master</strong>
</span>
<svg viewBox="0 0 16 16" class="dropdown icon svg octicon-triangle-down" width="14" height="14" aria-hidden="true"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"/></svg>
</button>
<div class="data gt-hidden" data-mode="branches">
<div class="item branch selected" data-url="master">master</div>
<div class="item tag " data-url="v0.001">v0.001</div>
<div class="item tag " data-url="v0.002">v0.002</div>
<div class="item tag " data-url="v0.004">v0.004</div>
<div class="item tag " data-url="v0.1">v0.1</div>
<div class="item tag " data-url="v0.1.2">v0.1.2</div>
<div class="item tag " data-url="v0.2">v0.2</div>
<div class="item tag " data-url="v0.2.1">v0.2.1</div>
<div class="item tag " data-url="v0.2.2">v0.2.2</div>
<div class="item tag " data-url="v0.3.1">v0.3.1</div>
<div class="item tag " data-url="v0.3.2">v0.3.2</div>
<div class="item tag " data-url="v0.3.3">v0.3.3</div>
<div class="item tag " data-url="v0.3.4">v0.3.4</div>
<div class="item tag " data-url="v0.5">v0.5</div>
<div class="item tag " data-url="v0.5.1">v0.5.1</div>
<div class="item tag " data-url="v0.5.2">v0.5.2</div>
<div class="item tag " data-url="v0.5.3">v0.5.3</div>
<div class="item tag " data-url="v0.5.4">v0.5.4</div>
<div class="item tag " data-url="v0.5.4.1">v0.5.4.1</div>
<div class="item tag " data-url="v0.6">v0.6</div>
<div class="item tag " data-url="v0.6.1">v0.6.1</div>
<div class="item tag " data-url="v0.6.2">v0.6.2</div>
<div class="item tag " data-url="v0.6.3">v0.6.3</div>
<div class="item tag " data-url="v0.6.5">v0.6.5</div>
<div class="item tag " data-url="v0.6.6">v0.6.6</div>
</div>
<div class="menu transition" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak>
<div class="ui icon search input">
<i class="icon gt-df gt-ac gt-jc gt-m-0"><svg viewBox="0 0 16 16" class="svg octicon-filter" width="16" height="16" aria-hidden="true"><path d="M.75 3h14.5a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1 0-1.5ZM3 7.75A.75.75 0 0 1 3.75 7h8.5a.75.75 0 0 1 0 1.5h-8.5A.75.75 0 0 1 3 7.75Zm3 4a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"/></svg></i>
<input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" placeholder="Branch oder Tag filtern...">
</div>
<div class="header branch-tag-choice">
<div class="ui grid">
<div class="two column row">
<a class="reference column" href="#" @click="createTag = false; mode = 'branches'; focusSearchField()">
<span class="text" :class="{black: mode == 'branches'}">
<svg viewBox="0 0 16 16" class="gt-mr-2 svg octicon-git-branch" width="16" height="16" aria-hidden="true"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg>Branches
</span>
</a>
<a class="reference column" href="#" @click="createTag = true; mode = 'tags'; focusSearchField()">
<span class="text" :class="{black: mode == 'tags'}">
<svg viewBox="0 0 16 16" class="gt-mr-2 svg octicon-tag" width="16" height="16" aria-hidden="true"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"/></svg>Tags
</span>
</a>
</div>
</div>
</div>
<div class="scrolling menu" ref="scrollContainer">
<div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active == index}" @click="selectItem(item)" :ref="'listItem' + index">${ item.name }</div>
<div class="item" v-if="showCreateNewBranch" :class="{active: active == filteredItems.length}" :ref="'listItem' + filteredItems.length">
<a href="#" @click="createNewBranch()">
<div v-show="createTag">
<i class="reference tags icon"></i>
Tag <strong>${ searchTerm }</strong> erstellen
</div>
<div v-show="!createTag">
<svg viewBox="0 0 16 16" class="svg octicon-git-branch" width="16" height="16" aria-hidden="true"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg>
Erstelle Branch <strong>${ searchTerm }</strong>
</div>
<div class="text small">
von „master“
</div>
</a>
<form ref="newBranchForm" action="/MorsMortium/Friendiqa/branches/_new/branch/master" method="post">
<input type="hidden" name="_csrf" value="hRtY4yQKJpvShSAHOERpMVsB_hI6MTY4NzcwMzY4NDU0MTUwMzIyMw">
<input type="hidden" name="new_branch_name" v-model="searchTerm">
<input type="hidden" name="create_tag" v-model="createTag">
<input type="hidden" name="current_path" value="source-linux/images/friendica-tray-white.svg">
</form>
</div>
</div>
<div class="message" v-if="showNoResults">${ noResults }</div>
</div>
</div>
</div>
<span class="ui breadcrumb repo-path gt-ml-2"><a class="section" href="/MorsMortium/Friendiqa/src/branch/master" title="Friendiqa">Friendiqa</a><span class="divider">/</span><span class="section"><a href="/MorsMortium/Friendiqa/src/branch/master/source-linux" title="source-linux">source-linux</a></span><span class="divider">/</span><span class="section"><a href="/MorsMortium/Friendiqa/src/branch/master/source-linux/images" title="images">images</a></span><span class="divider">/</span><span class="active section" title="friendica-tray-white.svg">friendica-tray-white.svg</span></span>
</div>
<div class="gt-df gt-ac">
</div>
</div>
<div class="tab-size-8 non-diff-file-content">
<h4 class="file-header ui top attached header gt-df gt-ac gt-sb gt-fw">
<div class="file-header-left gt-df gt-ac gt-py-3 gt-pr-4">
<div class="file-info text grey normal gt-mono">
<div class="file-info-entry">
42 Zeilen
</div>
<div class="file-info-entry">
1.7 KiB
</div>
<div class="file-info-entry">
XML
</div>
</div>
</div>
<div class="file-header-right file-actions gt-df gt-ac gt-fw">
<div class="ui compact icon buttons two-toggle-buttons">
<a href="/MorsMortium/Friendiqa/src/branch/master/source-linux/images/friendica-tray-white.svg?display=source" class="ui mini basic button tooltip " data-content="Quelltext anzeigen" data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-code" width="15" height="15" aria-hidden="true"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"/></svg></a>
<a href="/MorsMortium/Friendiqa/src/branch/master/source-linux/images/friendica-tray-white.svg" class="ui mini basic button tooltip active" data-content="Ansicht rendern" data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-file" width="15" height="15" aria-hidden="true"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"/></svg></a>
</div>
<div class="ui buttons gt-mr-2">
<a class="ui mini basic button" href="/MorsMortium/Friendiqa/raw/branch/master/source-linux/images/friendica-tray-white.svg">Originalformat</a>
<a class="ui mini basic button" href="/MorsMortium/Friendiqa/src/commit/8dc907e345eaaebbe8b81b9b098385454e3e8f45/source-linux/images/friendica-tray-white.svg">Permalink</a>
<a class="ui mini basic button" href="/MorsMortium/Friendiqa/blame/branch/master/source-linux/images/friendica-tray-white.svg">Blame</a>
<a class="ui mini basic button" href="/MorsMortium/Friendiqa/commits/branch/master/source-linux/images/friendica-tray-white.svg">Verlauf</a>
</div>
<a download href="/MorsMortium/Friendiqa/raw/branch/master/source-linux/images/friendica-tray-white.svg"><span class="btn-octicon tooltip" data-content="Datei herunterladen" data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-download" width="16" height="16" aria-hidden="true"><path d="M7.47 10.78 3.72 7.03a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l2.47 2.47V1.75a.75.75 0 0 1 1.5 0v6.69l2.47-2.47a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-3.75 3.75a.75.75 0 0 1-1.06 0ZM3.75 13h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Z"/></svg></span></a>
<a id="copy-content" class="btn-octicon tooltip" data-link="/MorsMortium/Friendiqa/raw/branch/master/source-linux/images/friendica-tray-white.svg" data-content="Copy content"><svg viewBox="0 0 16 16" class="svg octicon-copy" width="14" height="14" aria-hidden="true"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></a>
<span class="btn-octicon tooltip disabled" data-content="Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen." data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-pencil" width="16" height="16" aria-hidden="true"><path d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 0 1-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61Zm.176 4.823L9.75 4.81l-6.286 6.287a.253.253 0 0 0-.064.108l-.558 1.953 1.953-.558a.253.253 0 0 0 .108-.064Zm1.238-3.763a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Z"/></svg></span>
<span class="btn-octicon tooltip disabled" data-content="Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen." data-position="bottom center"><svg viewBox="0 0 16 16" class="svg octicon-trash" width="16" height="16" aria-hidden="true"><path d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"/></svg></span>
</div>
</h4>
<div class="ui attached table unstackable segment">
<div class="file-view">
<div class="view-raw ui center">
<img src="/MorsMortium/Friendiqa/raw/branch/master/source-linux/images/friendica-tray-white.svg">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer role="group" aria-label="Footer">
<div class="ui container">
<div class="ui left" role="contentinfo" aria-label="About Software">
<a target="_blank" rel="noopener noreferrer" href="https://forgejo.org">Powered by Forgejo</a>
Version:
1.19.0&#43;2
Seite: <strong>42ms</strong>
Template: <strong>1ms</strong>
</div>
<div class="ui right links" role="group" aria-label="Links">
<div class="ui language bottom floating slide up dropdown link item">
<svg viewBox="0 0 16 16" class="svg octicon-globe" width="16" height="16" aria-hidden="true"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM5.78 8.75a9.64 9.64 0 0 0 1.363 4.177c.255.426.542.832.857 1.215.245-.296.551-.705.857-1.215A9.64 9.64 0 0 0 10.22 8.75Zm4.44-1.5a9.64 9.64 0 0 0-1.363-4.177c-.307-.51-.612-.919-.857-1.215a9.927 9.927 0 0 0-.857 1.215A9.64 9.64 0 0 0 5.78 7.25Zm-5.944 1.5H1.543a6.507 6.507 0 0 0 4.666 5.5c-.123-.181-.24-.365-.352-.552-.715-1.192-1.437-2.874-1.581-4.948Zm-2.733-1.5h2.733c.144-2.074.866-3.756 1.58-4.948.12-.197.237-.381.353-.552a6.507 6.507 0 0 0-4.666 5.5Zm10.181 1.5c-.144 2.074-.866 3.756-1.58 4.948-.12.197-.237.381-.353.552a6.507 6.507 0 0 0 4.666-5.5Zm2.733-1.5a6.507 6.507 0 0 0-4.666-5.5c.123.181.24.365.353.552.714 1.192 1.436 2.874 1.58 4.948Z"/></svg>
<span>Deutsch</span>
<div class="menu language-menu">
<a lang="id-ID" data-url="/?lang=id-ID" class="item ">Bahasa Indonesia</a>
<a lang="de-DE" data-url="/?lang=de-DE" class="item active selected">Deutsch</a>
<a lang="en-US" data-url="/?lang=en-US" class="item ">English</a>
<a lang="es-ES" data-url="/?lang=es-ES" class="item ">Español</a>
<a lang="fr-FR" data-url="/?lang=fr-FR" class="item ">Français</a>
<a lang="it-IT" data-url="/?lang=it-IT" class="item ">Italiano</a>
<a lang="lv-LV" data-url="/?lang=lv-LV" class="item ">Latviešu</a>
<a lang="hu-HU" data-url="/?lang=hu-HU" class="item ">Magyar nyelv</a>
<a lang="nl-NL" data-url="/?lang=nl-NL" class="item ">Nederlands</a>
<a lang="pl-PL" data-url="/?lang=pl-PL" class="item ">Polski</a>
<a lang="pt-PT" data-url="/?lang=pt-PT" class="item ">Português de Portugal</a>
<a lang="pt-BR" data-url="/?lang=pt-BR" class="item ">Português do Brasil</a>
<a lang="fi-FI" data-url="/?lang=fi-FI" class="item ">Suomi</a>
<a lang="sv-SE" data-url="/?lang=sv-SE" class="item ">Svenska</a>
<a lang="tr-TR" data-url="/?lang=tr-TR" class="item ">Türkçe</a>
<a lang="cs-CZ" data-url="/?lang=cs-CZ" class="item ">Čeština</a>
<a lang="el-GR" data-url="/?lang=el-GR" class="item ">Ελληνικά</a>
<a lang="bg-BG" data-url="/?lang=bg-BG" class="item ">Български</a>
<a lang="ru-RU" data-url="/?lang=ru-RU" class="item ">Русский</a>
<a lang="uk-UA" data-url="/?lang=uk-UA" class="item ">Українська</a>
<a lang="fa-IR" data-url="/?lang=fa-IR" class="item ">فارسی</a>
<a lang="ml-IN" data-url="/?lang=ml-IN" class="item ">മലയാളം</a>
<a lang="ja-JP" data-url="/?lang=ja-JP" class="item ">日本語</a>
<a lang="zh-CN" data-url="/?lang=zh-CN" class="item ">简体中文</a>
<a lang="zh-TW" data-url="/?lang=zh-TW" class="item ">繁體中文(台灣)</a>
<a lang="zh-HK" data-url="/?lang=zh-HK" class="item ">繁體中文(香港)</a>
<a lang="ko-KR" data-url="/?lang=ko-KR" class="item ">한국어</a>
</div>
</div>
<a href="/assets/js/licenses.txt">Lizenzen</a>
<a href="/api/swagger">API</a>
</div>
</div>
</footer>
<script src="/assets/js/index.js?v=1.19.0~2" onerror="alert('Failed to load asset files from ' + this.src + '. Please make sure the asset files can be accessed.')"></script>
</body>
</html>

View file

@ -0,0 +1,7 @@
[Unit]
Description=Run friendiqa background sync
[Service]
Type=oneshot
ExecStart=/usr/bin/friendiqa -service

View file

@ -0,0 +1,10 @@
[Unit]
Description=Run background sync for Friendiqa periodically
[Timer]
Unit=friendiqa-sync.service
OnBootSec=10min
OnUnitActiveSec=15min
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,3 @@
<svg width="1936" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1024 1131c0-155-38-327-196-327-49 28-115 76-188 76s-139-48-188-76c-158 0-196 172-196 327 0 87 57 149 128 149h512c71 0 128-62 128-149zM867 611c0-125-102-227-227-227S413 486 413 611c0 126 102 227 227 227s227-101 227-227zm925 509v-64c0-18-14-32-32-32h-576c-18 0-32 14-32 32v64c0 18 14 32 32 32h576c18 0 32-14 32-32zm0-260v-56c0-20-16-36-36-36h-568c-20 0-36 16-36 36v56c0 20 16 36 36 36h568c20 0 36-16 36-36zm0-252v-64c0-18-14-32-32-32h-576c-18 0-32 14-32 32v64c0 18 14 32 32 32h576c18 0 32-14 32-32zm256-320v1216c0 88-72 160-160 160h-352v-96c0-18-14-32-32-32h-64c-18 0-32 14-32 32v96H640v-96c0-18-14-32-32-32h-64c-18 0-32 14-32 32v96H160c-88 0-160-72-160-160V288c0-88 72-160 160-160h1728c88 0 160 72 160 160z"/>
</svg>

After

Width:  |  Height:  |  Size: 798 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M627 544c0 8-4 17-10 23L224 960l393 393c6 6 10 15 10 23s-4 17-10 23l-50 50c-6 6-15 10-23 10s-17-4-23-10L55 983c-6-6-10-15-10-23s4-17 10-23l466-466c6-6 15-10 23-10s17 4 23 10l50 50c6 6 10 14 10 23z"/>
</svg>

After

Width:  |  Height:  |  Size: 288 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M595 960c0 8-4 17-10 23l-466 466c-6 6-15 10-23 10s-17-4-23-10l-50-50c-6-6-10-14-10-23 0-8 4-17 10-23l393-393L23 567c-6-6-10-15-10-23s4-17 10-23l50-50c6-6 15-10 23-10s17 4 23 10l466 466c6 6 10 15 10 23z"/>
</svg>

After

Width:  |  Height:  |  Size: 293 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1536 1344v128c0 35-29 64-64 64H64c-35 0-64-29-64-64v-128c0-35 29-64 64-64h1408c35 0 64 29 64 64zm0-512v128c0 35-29 64-64 64H64c-35 0-64-29-64-64V832c0-35 29-64 64-64h1408c35 0 64 29 64 64zm0-512v128c0 35-29 64-64 64H64c-35 0-64-29-64-64V320c0-35 29-64 64-64h1408c35 0 64 29 64 64z"/>
</svg>

After

Width:  |  Height:  |  Size: 373 B

View file

@ -0,0 +1,3 @@
<svg width="1836" height="1836" xmlns="http://www.w3.org/2000/svg">
<path d="M912 1696c0-9-7-16-16-16-79 0-144-65-144-144 0-9-7-16-16-16s-16 7-16 16c0 97 79 176 176 176 9 0 16-7 16-16zm-666-288h1300c-179-202-266-476-266-832 0-129-122-320-384-320S512 447 512 576c0 356-87 630-266 832zm1482 0c0 70-58 128-128 128h-448c0 141-115 256-256 256s-256-115-256-256H192c-70 0-128-58-128-128 148-125 320-349 320-832 0-192 159-402 424-441-5-12-8-25-8-39 0-53 43-96 96-96s96 43 96 96c0 14-3 27-8 39 265 39 424 249 424 441 0 483 172 707 320 832z"/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M555 1521c44 19 92 32 140 32 228 0 376-91 376-335 0-62-8-127-41-180-93-150-227-158-388-158-30 0-73 0-101 10 0 106-1 212-1 317 0 69-9 256 15 314zm-14-746c36 6 73 7 109 7 206 0 353-58 353-289 0-195-173-262-340-262-44 0-87 6-130 13 0 101 8 202 8 303 0 53-1 106-1 159 0 23 0 46 1 69zM0 1664l2-94c64-16 129-17 191-43 35-59 30-163 30-230 0-22 2-978-22-1025-15-29-162-36-195-40l-4-83c238-4 476-21 713-21 45 0 91 1 136 1 226 0 475 108 475 368 0 179-136 246-277 310 190 43 359 172 359 382 0 344-313 458-606 458-88 0-176-6-264-6-179 0-360 16-538 23z"/>
</svg>

After

Width:  |  Height:  |  Size: 631 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M128 1664h288v-288H128v288zm352 0h320v-288H480v288zm-352-352h288V992H128v320zm352 0h320V992H480v320zM128 928h288V640H128v288zm736 736h320v-288H864v288zM480 928h320V640H480v288zm768 736h288v-288h-288v288zm-384-352h320V992H864v320zM512 448V160c0-17-15-32-32-32h-64c-17 0-32 15-32 32v288c0 17 15 32 32 32h64c17 0 32-15 32-32zm736 864h288V992h-288v320zM864 928h320V640H864v288zm384 0h288V640h-288v288zm32-480V160c0-17-15-32-32-32h-64c-17 0-32 15-32 32v288c0 17 15 32 32 32h64c17 0 32-15 32-32zm384-64v1280c0 70-58 128-128 128H128c-70 0-128-58-128-128V384c0-70 58-128 128-128h128v-96C256 72 328 0 416 0h64c88 0 160 72 160 160v96h384v-96c0-88 72-160 160-160h64c88 0 160 72 160 160v96h128c70 0 128 58 128 128z"/>
</svg>

After

Width:  |  Height:  |  Size: 794 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M1024 704c0 17-7 33-19 45l-448 448c-12 12-28 19-45 19s-33-7-45-19L19 749C7 737 0 721 0 704c0-35 29-64 64-64h896c35 0 64 29 64 64z"/>
</svg>

After

Width:  |  Height:  |  Size: 221 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M1671 566c0 25-10 50-28 68l-724 724-136 136c-18 18-43 28-68 28s-50-10-68-28l-136-136-362-362c-18-18-28-43-28-68s10-50 28-68l136-136c18-18 43-28 68-28s50 10 68 28l294 295 656-657c18-18 43-28 68-28s50 10 68 28l136 136c18 18 28 43 28 68z"/>
</svg>

After

Width:  |  Height:  |  Size: 326 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1228.8pt" height="1228.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(73.6288, 380.209)" fill="#000000" d="M1372.87 231.202L733.304 854.755C711.755 875.792 677.277 875.792 655.729 854.755L16.1616 231.202C-5.38719 210.165 -5.38719 175.663 16.1616 154.626L159.245 15.7781C180.794 -5.25938 215.272 -5.25938 236.821 15.7781L694.516 462.615L1152.21 15.7781C1173.76 -5.25938 1208.24 -5.25938 1229.79 15.7781L1372.87 154.626C1394.42 175.663 1394.42 210.165 1372.87 231.202Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 827 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1228.8pt" height="1228.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(68.5623, 351.122)" fill="#000000" d="M1377.88 685.152L1234.27 818.035C1212.65 838.169 1178.04 838.169 1156.41 818.035L697.05 390.394L237.685 818.035C216.057 838.169 181.454 838.169 159.826 818.035L16.2205 685.152C-5.40684 665.018 -5.40684 631.999 16.2205 611.865L658.12 15.1003C679.748 -5.03344 714.352 -5.03344 735.979 15.1003L1377.88 611.865C1399.51 631.999 1399.51 665.018 1377.88 685.152Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 823 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1400pt" height="1308.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(15.496, 130.214)" fill="#000000" d="M1151.13 714.652C1151.13 698.572 1138.54 686.066 1122.35 686.066L920.907 686.066L920.907 371.619C920.907 356.433 907.417 343.033 892.128 343.033L719.458 343.033C704.17 343.033 690.68 356.433 690.68 371.619L690.68 686.066L489.232 686.066C473.044 686.066 460.453 699.466 460.453 714.652C460.453 721.798 463.151 729.838 468.547 735.198L785.109 1049.65C790.505 1055 797.699 1057.68 805.793 1057.68C812.988 1057.68 821.082 1055 826.478 1049.65L1142.14 736.091C1147.54 729.838 1151.13 722.692 1151.13 714.652ZM1726.7 914.754C1726.7 1104.14 1572.02 1257.79 1381.36 1257.79L402.897 1257.79C180.764 1257.79 0 1078.23 0 857.582C0 702.146 90.8316 561.002 232.025 495.79C231.126 482.39 230.227 469.884 230.227 457.377C230.227 204.569 436.172 0 690.68 0C877.739 0 1045.91 112.558 1116.96 284.074C1158.33 248.342 1211.39 228.689 1266.25 228.689C1393.05 228.689 1496.47 331.42 1496.47 457.377C1496.47 501.15 1483.88 544.029 1459.6 580.655C1616.08 617.281 1726.7 755.744 1726.7 914.754Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1400pt" height="1228.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(43.6305, 112.193)" fill="#000000" d="M1115 685.617C1115 678.164 1112.39 669.78 1107.16 664.191L800.537 336.287C795.31 330.698 788.342 327.904 780.502 327.904C773.533 327.904 765.693 330.698 760.467 336.287L454.712 663.26C449.485 669.78 446.001 677.233 446.001 685.617C446.001 702.384 458.196 715.426 473.876 715.426L669.002 715.426L669.002 1043.33C669.002 1059.17 682.068 1073.14 696.877 1073.14L864.127 1073.14C878.936 1073.14 892.002 1059.17 892.002 1043.33L892.002 715.426L1087.13 715.426C1102.81 715.426 1115 701.453 1115 685.617ZM1672.5 953.901C1672.5 1151.39 1522.68 1311.61 1338 1311.61L390.251 1311.61C175.09 1311.61 0 1124.37 0 894.283C0 732.194 87.9807 585.01 224.743 517.007C223.872 503.034 223.001 489.992 223.001 476.951C223.001 213.324 422.481 0 669.002 0C850.189 0 1013.08 117.375 1081.9 296.231C1121.97 258.969 1173.37 238.475 1226.5 238.475C1349.33 238.475 1449.5 345.603 1449.5 476.951C1449.5 522.596 1437.31 567.31 1413.79 605.504C1565.36 643.697 1672.5 788.086 1672.5 953.901Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,3 @@
<svg width="1900" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M617 1399l-50 50c-13 13-33 13-46 0L55 983c-13-13-13-33 0-46l466-466c13-13 33-13 46 0l50 50c13 13 13 33 0 46L224 960l393 393c13 13 13 33 0 46zm591-1067L835 1623c-5 17-23 27-39 22l-62-17c-17-5-27-23-22-40l373-1291c5-17 23-27 39-22l62 17c17 5 27 23 22 40zm657 651l-466 466c-13 13-33 13-46 0l-50-50c-13-13-13-33 0-46l393-393-393-393c-13-13-13-33 0-46l50-50c13-13 33-13 46 0l466 466c13 13 13 33 0 46z"/>
</svg>

After

Width:  |  Height:  |  Size: 487 B

View file

@ -0,0 +1,3 @@
<svg width="1836" height="1836" xmlns="http://www.w3.org/2000/svg">
<path d="M896 896c0-141-115-256-256-256S384 755 384 896s115 256 256 256 256-115 256-256zm768 512c0-70-58-128-128-128s-128 58-128 128c0 71 58 128 128 128 71 0 128-58 128-128zm0-1024c0-70-58-128-128-128s-128 58-128 128c0 71 58 128 128 128 71 0 128-58 128-128zm-384 421v185c0 13-10 28-23 30l-155 24c-8 26-19 51-32 76 28 40 58 77 90 115 4 6 7 12 7 20 0 7-2 14-7 19-20 27-132 149-161 149-8 0-15-3-21-7l-115-90c-25 13-50 23-77 31-5 51-10 106-23 155-4 14-16 24-30 24H547c-14 0-28-11-30-25l-23-153c-26-8-51-19-75-31l-118 89c-5 5-13 7-20 7-8 0-15-3-21-8-26-24-144-131-144-160 0-7 3-13 7-19 29-38 59-75 88-114-14-27-26-54-35-82l-152-24c-14-2-24-15-24-29V802c0-13 10-28 23-30l155-24c8-26 19-51 32-76-28-40-58-77-90-115-4-6-7-13-7-20s2-14 7-20c20-27 132-148 161-148 8 0 15 3 21 7l115 90c25-13 50-23 77-32 5-50 10-105 23-154 4-14 16-24 30-24h186c14 0 28 11 30 25l23 153c26 8 51 19 75 31l118-89c6-5 13-7 20-7 8 0 15 3 21 8 26 24 144 132 144 160 0 7-3 13-7 19-29 39-59 75-87 114 13 27 25 54 34 82l152 23c14 3 24 16 24 30zm640 533v140c0 15-129 29-149 31-8 19-18 36-30 52 9 20 51 120 51 138 0 3-1 5-4 7-12 7-119 71-124 71-13 0-88-100-98-115-10 1-20 2-30 2s-20-1-30-2c-10 15-85 115-98 115-5 0-112-64-124-71-3-2-4-5-4-7 0-17 42-118 51-138-12-16-22-33-30-52-20-2-149-16-149-31v-140c0-15 129-29 149-31 8-18 18-36 30-52-9-20-51-121-51-138 0-2 1-5 4-7 12-6 119-70 124-70 13 0 88 99 98 114 10-1 20-2 30-2s20 1 30 2c28-39 58-78 92-112l6-2c5 0 112 63 124 70 3 2 4 5 4 7 0 18-42 118-51 138 12 16 22 34 30 52 20 2 149 16 149 31zm0-1024v140c0 15-129 29-149 31-8 19-18 36-30 52 9 20 51 120 51 138 0 3-1 5-4 7-12 7-119 71-124 71-13 0-88-100-98-115-10 1-20 2-30 2s-20-1-30-2c-10 15-85 115-98 115-5 0-112-64-124-71-3-2-4-5-4-7 0-17 42-118 51-138-12-16-22-33-30-52-20-2-149-16-149-31V314c0-15 129-29 149-31 8-18 18-36 30-52-9-20-51-121-51-138 0-2 1-5 4-7 12-6 119-70 124-70 13 0 88 99 98 114 10-1 20-2 30-2s20 1 30 2c28-39 58-78 92-112l6-2c5 0 112 63 124 70 3 2 4 5 4 7 0 18-42 118-51 138 12 16 22 34 30 52 20 2 149 16 149 31z"/>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,3 @@
<svg width="1836" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1408 768c0 283-315 512-704 512-61 0-120-6-176-16-83 59-177 102-278 128-27 7-56 12-86 16h-3c-15 0-29-12-32-29-4-19 9-31 20-44 39-44 83-83 117-166C104 1075 0 930 0 768c0-283 315-512 704-512s704 229 704 512zm384 256c0 163-104 307-266 401 34 83 78 122 117 166 11 13 24 25 20 44-4 18-19 31-35 29-30-4-59-9-86-16-101-26-195-69-278-128-56 10-115 16-176 16-181 0-347-50-472-132 29 2 59 4 88 4 215 0 418-62 573-174 167-122 259-287 259-466 0-52-8-103-23-152 169 93 279 241 279 408z"/>
</svg>

After

Width:  |  Height:  |  Size: 564 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M384 1248v192c0 53-43 96-96 96H96c-53 0-96-43-96-96v-192c0-53 43-96 96-96h192c53 0 96 43 96 96zm0-512v192c0 53-43 96-96 96H96c-53 0-96-43-96-96V736c0-53 43-96 96-96h192c53 0 96 43 96 96zm0-512v192c0 53-43 96-96 96H96c-53 0-96-43-96-96V224c0-53 43-96 96-96h192c53 0 96 43 96 96z"/>
</svg>

After

Width:  |  Height:  |  Size: 369 B

View file

@ -0,0 +1,3 @@
<svg width="1836" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1792 710v794c0 88-72 160-160 160H160c-88 0-160-72-160-160V710c30 33 64 62 101 87 166 113 334 226 497 345 84 62 188 138 297 138h2c109 0 213-76 297-138 163-118 331-232 498-345 36-25 70-54 100-87zm0-294c0 112-83 213-171 274-156 108-313 216-468 325-65 45-175 137-256 137h-2c-81 0-191-92-256-137-155-109-312-217-467-325C101 642 0 529 0 438c0-98 53-182 160-182h1472c87 0 160 72 160 160z"/>
</svg>

After

Width:  |  Height:  |  Size: 473 B

View file

@ -0,0 +1,3 @@
<svg width="1836" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1792 1184v192c0 17-15 32-32 32H384v192c0 17-14 32-32 32-9 0-17-4-24-10L9 1302c-6-6-9-14-9-22 0-9 3-17 9-23l320-320c6-6 15-9 23-9 17 0 32 14 32 32v192h1376c17 0 32 14 32 32zm0-544c0 8-3 17-9 23l-320 320c-6 6-15 9-23 9-17 0-32-15-32-32V768H32c-17 0-32-15-32-32V544c0-17 15-32 32-32h1376V320c0-18 14-32 32-32 9 0 17 4 24 10l319 319c6 6 9 15 9 23z"/>
</svg>

After

Width:  |  Height:  |  Size: 436 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M1403 295c10 24 5 52-14 70L896 858v742c0 26-16 49-39 59-8 3-17 5-25 5-17 0-33-6-45-19l-256-256c-12-12-19-28-19-45V858L19 365C0 347-5 319 5 295c10-23 33-39 59-39h1280c26 0 49 16 59 39z"/>
</svg>

After

Width:  |  Height:  |  Size: 275 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1228.8pt" height="1228.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(54.0188, 130.078)" fill="#000000" d="M354.238 1137.74L1062.72 1137.74L1062.72 827.445L354.238 827.445ZM1180.79 1137.74L1298.87 1137.74L1298.87 413.723C1298.87 398.37 1283.19 365.239 1271.2 354.735L1011.98 127.672C999.063 116.359 963.086 103.431 944.636 103.431L944.636 439.58C944.636 482.407 904.968 517.153 856.076 517.153L324.719 517.153C275.826 517.153 236.159 482.407 236.159 439.58L236.159 103.431L118.079 103.431L118.079 1137.74L236.159 1137.74L236.159 801.588C236.159 758.761 275.826 724.015 324.719 724.015L1092.24 724.015C1141.13 724.015 1180.79 758.761 1180.79 801.588ZM826.556 387.865L826.556 129.288C826.556 115.551 812.719 103.431 797.036 103.431L619.917 103.431C604.235 103.431 590.397 115.551 590.397 129.288L590.397 387.865C590.397 401.602 604.235 413.723 619.917 413.723L797.036 413.723C812.719 413.723 826.556 401.602 826.556 387.865ZM1416.95 413.723L1416.95 1163.59C1416.95 1206.42 1377.29 1241.17 1328.39 1241.17L88.5596 1241.17C39.6673 1241.17 0 1206.42 0 1163.59L0 77.573C0 34.7462 39.6673 0 88.5596 0L944.636 0C993.528 0 1060.87 24.2416 1095.93 54.9475L1354.22 281.202C1389.28 311.908 1416.95 370.896 1416.95 413.723Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M725 559l-170 450c99 1 198 4 297 4 19 0 38-1 57-2-52-152-113-307-184-452zM0 1664l2-79c94-29 196-9 238-117l237-616 280-724h128c4 7 8 14 11 21l205 480c75 177 144 356 220 532 45 104 80 211 130 313 7 16 21 46 35 57 33 26 125 32 172 50 3 19 6 38 6 57 0 9-1 17-1 26-127 0-254-16-381-16-131 0-262 11-393 15 0-26 1-52 4-78l131-28c27-6 80-13 80-50 0-36-129-333-145-374l-450-2c-26 58-127 320-127 358 0 77 147 80 204 88 1 19 1 38 1 58 0 9-1 18-2 27-116 0-233-20-349-20-14 0-34 6-48 8-63 11-125 14-188 14z"/>
</svg>

After

Width:  |  Height:  |  Size: 585 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1134 1229c11 34-8 69-41 80-34 11-70-8-81-42-33-107-132-179-244-179s-211 72-244 179c-11 34-47 53-80 42-34-11-53-46-42-80 50-161 197-269 366-269s316 108 366 269zM640 640c0 71-57 128-128 128s-128-57-128-128 57-128 128-128 128 57 128 128zm512 0c0 71-57 128-128 128s-128-57-128-128 57-128 128-128 128 57 128 128zm256 256c0-353-287-640-640-640S128 543 128 896s287 640 640 640 640-287 640-640zm128 0c0 424-344 768-768 768S0 1320 0 896s344-768 768-768 768 344 768 768z"/>
</svg>

After

Width:  |  Height:  |  Size: 553 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1428.8pt" height="1436.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(53.4464, 144.147)" fill="#000000" d="M719.097 0C321.951 -2.56978e-14 4.86364e-14 310.892 0 694.396C4.86364e-14 1077.9 321.951 1388.79 719.097 1388.79C1116.24 1388.79 1438.19 1077.9 1438.19 694.396C1438.19 310.892 1116.24 2.56978e-14 719.097 0ZM690.117 197.437C698.904 197.336 706.499 198.886 711.633 203.844C726.57 218.268 711.633 236.481 711.633 252.8C711.633 269.114 722.807 281.583 711.633 301.75C700.46 321.917 660.936 334.398 635.587 350.721C610.238 367.044 583.397 405.813 559.539 399.697C535.681 393.583 542.631 367.055 534.177 350.732C525.722 334.408 508.813 320.014 508.813 301.766C508.813 283.518 525.721 269.128 534.175 252.805C542.629 236.486 540.716 215.076 559.536 203.845C578.356 192.617 610.236 203.845 635.584 203.845C652.221 203.845 673.34 197.632 690.116 197.437ZM459.292 203.991C461.277 203.968 463.414 204.322 465.053 205.651C468.8 208.687 458.122 219.961 458.122 227.976C458.122 235.986 462.031 242.899 458.122 252.016C454.213 261.137 440.965 268.604 432.388 276.903C423.81 285.197 292.171 378.286 352.782 378.286C413.394 378.286 440.441 334.422 457.335 350.745C474.229 367.065 508.02 365.174 508.018 399.701C508.018 434.228 422.198 490.157 388.53 522.667C380.113 530.8 396.508 612.598 383.259 612.598C369.931 612.598 339.789 564.466 330.041 572.824C311.35 585.91 318.571 578.286 306.013 594.787C293.649 623.615 284.167 637.917 284.167 654.344C284.167 670.77 280.036 699.038 285.846 706.328C291.66 713.619 290.156 707.456 291.788 707.847C293.421 708.337 386.72 712.983 412.826 719.28C438.931 725.576 483.079 728.696 507.754 742.179C532.429 755.663 541.827 775.07 558.865 791.516C575.901 807.963 606.564 820.628 609.975 840.85C613.388 861.075 593.599 872.557 585.411 888.415C577.222 904.268 569.78 923.955 560.845 935.975C551.911 947.99 543.507 952.814 534.837 961.235C526.166 969.651 521.848 962.899 508.828 986.494C495.808 1010.06 490.465 1075.11 483.5 1108.73C478.125 1134.67 476.926 1193.56 468.001 1219.11C434.605 1204.29 402.693 1186.53 372.666 1166.06C353.284 1136.54 340.092 1102.06 331.36 1059.87C324.388 1026.22 321.782 966.013 306.005 937.503C290.227 908.993 272.286 904.855 255.428 888.537C238.57 872.213 208.247 859.938 204.852 839.57C201.455 819.203 221.762 807.022 230.215 790.743C238.67 774.468 265.554 744.793 261.179 718.659C256.804 692.523 204.7 692.781 179.262 668.213C166.581 655.966 144.3 645.916 123.045 635.346C141.305 465.398 236.409 311.93 382.872 216.065C412.727 211.266 453.206 204.766 453.764 204.671"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M991 1024l64-256H801l-64 256h254zm768-504l-56 224c-4 14-16 24-31 24h-327l-64 256h311c10 0 19 5 25 12 6 8 9 18 6 28l-56 224c-3 14-16 24-31 24h-327l-81 328c-4 14-17 24-31 24H873c-10 0-20-5-26-12-6-8-8-18-6-28l78-312H665l-81 328c-4 14-17 24-31 24H328c-9 0-19-5-25-12-6-8-8-18-6-28l78-312H64c-10 0-19-5-25-12-6-8-8-18-6-28l56-224c4-14 16-24 31-24h327l64-256H200c-10 0-19-5-25-12-6-8-9-18-6-28l56-224c3-14 16-24 31-24h327l81-328c4-14 17-24 32-24h224c9 0 19 5 25 12 6 8 8 18 6 28l-78 312h254l81-328c4-14 17-24 32-24h224c9 0 19 5 25 12 6 8 8 18 6 28l-78 312h311c10 0 19 5 25 12 6 8 8 18 6 28z"/>
</svg>

After

Width:  |  Height:  |  Size: 677 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1536 896c0 423-345 768-768 768-229 0-445-101-591-277-10-13-9-32 2-43l137-138c7-6 16-9 25-9 9 1 18 5 23 12 98 127 245 199 404 199 282 0 512-230 512-512s-230-512-512-512c-131 0-255 50-348 137l137 138c19 18 24 46 14 69-10 24-33 40-59 40H64c-35 0-64-29-64-64V256c0-26 16-49 40-59 23-10 51-5 69 14l130 129c141-133 332-212 529-212 423 0 768 345 768 768zM896 608v448c0 18-14 32-32 32H544c-18 0-32-14-32-32v-64c0-18 14-32 32-32h224V608c0-18 14-32 32-32h64c18 0 32 14 32 32z"/>
</svg>

After

Width:  |  Height:  |  Size: 558 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M1408 992v480c0 35-29 64-64 64H960v-384H704v384H320c-35 0-64-29-64-64V992c0-2 1-4 1-6l575-474 575 474c1 2 1 4 1 6zm223-69l-62 74c-5 6-13 10-21 11h-3c-8 0-15-2-21-7L832 424l-692 577c-7 5-15 8-24 7-8-1-16-5-21-11l-62-74c-11-13-9-34 4-45l719-599c42-35 110-35 152 0l244 204V288c0-18 14-32 32-32h192c18 0 32 14 32 32v408l219 182c13 11 15 32 4 45z"/>
</svg>

After

Width:  |  Height:  |  Size: 433 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M0 1662l17-85c64-20 133-28 193-59 23-29 34-66 41-101 13-68 231-1049 228-1129v-25c-55-30-122-22-182-32l19-103c129 6 260 16 390 16 106 0 212-10 318-16-4 30-11 60-19 89-69 24-142 35-210 62-22 54-27 113-37 170-48 259-112 518-165 775-10 48-59 247-55 289l1 18c61 14 123 21 185 31-2 33-8 66-16 99-22 0-43 3-65 3-57 0-116-19-173-20-69-1-138-2-206-2-89 0-176 15-264 20z"/>
</svg>

After

Width:  |  Height:  |  Size: 452 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M256 1312v192c0 17-15 32-32 32H32c-17 0-32-15-32-32v-192c0-17 15-32 32-32h192c17 0 32 15 32 32zm0-384v192c0 17-15 32-32 32H32c-17 0-32-15-32-32V928c0-17 15-32 32-32h192c17 0 32 15 32 32zm0-384v192c0 17-15 32-32 32H32c-17 0-32-15-32-32V544c0-17 15-32 32-32h192c17 0 32 15 32 32zm1536 768v192c0 17-15 32-32 32H416c-17 0-32-15-32-32v-192c0-17 15-32 32-32h1344c17 0 32 15 32 32zM256 160v192c0 17-15 32-32 32H32c-17 0-32-15-32-32V160c0-17 15-32 32-32h192c17 0 32 15 32 32zm1536 768v192c0 17-15 32-32 32H416c-17 0-32-15-32-32V928c0-17 15-32 32-32h1344c17 0 32 15 32 32zm0-384v192c0 17-15 32-32 32H416c-17 0-32-15-32-32V544c0-17 15-32 32-32h1344c17 0 32 15 32 32zm0-384v192c0 17-15 32-32 32H416c-17 0-32-15-32-32V160c0-17 15-32 32-32h1344c17 0 32 15 32 32z"/>
</svg>

After

Width:  |  Height:  |  Size: 841 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M320 768h512V576c0-141-115-256-256-256S320 435 320 576v192zm832 96v576c0 53-43 96-96 96H96c-53 0-96-43-96-96V864c0-53 43-96 96-96h32V576c0-246 202-448 448-448s448 202 448 448v192h32c53 0 96 43 96 96z"/>
</svg>

After

Width:  |  Height:  |  Size: 291 B

View file

@ -0,0 +1,3 @@
<svg width="1800" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1764 11c21 15 31 39 27 64l-256 1536c-3 19-15 35-32 45-9 5-20 8-31 8-8 0-16-2-24-5l-527-215-298 327c-12 14-29 21-47 21-8 0-16-1-23-4-25-10-41-34-41-60v-452L40 1083c-23-9-38-30-40-55-2-24 11-47 32-59L1696 9c21-13 48-12 68 2zm-342 1499l221-1323-1434 827 336 137 863-639-478 797z"/>
</svg>

After

Width:  |  Height:  |  Size: 368 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M363 1536l91-91-235-235-91 91v107h128v128h107zm523-928c0-13-9-22-22-22-6 0-12 2-17 7l-542 542c-5 5-7 11-7 17 0 13 9 22 22 22 6 0 12-2 17-7l542-542c5-5 7-11 7-17zm-54-192l416 416-832 832H0v-416zm683 96c0 34-14 67-37 90l-166 166-416-416 166-165c23-24 56-38 90-38s67 14 91 38l235 234c23 24 37 57 37 91z"/>
</svg>

After

Width:  |  Height:  |  Size: 391 B

View file

@ -0,0 +1,3 @@
<svg width="1900" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M640 576c0 106-86 192-192 192s-192-86-192-192 86-192 192-192 192 86 192 192zm1024 384v448H256v-192l320-320 160 160 512-512zm96-704H160c-17 0-32 15-32 32v1216c0 17 15 32 32 32h1600c17 0 32-15 32-32V288c0-17-15-32-32-32zm160 32v1216c0 88-72 160-160 160H160c-88 0-160-72-160-160V288c0-88 72-160 160-160h1600c88 0 160 72 160 160z"/>
</svg>

After

Width:  |  Height:  |  Size: 417 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1228.8pt" height="1228.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(149.59, 142.837)" fill="#000000" d="M1232.12 657.106L49.8546 1258.01C22.2565 1271.86 0 1259.64 0 1231.14L0 32.5835C0 4.08507 22.2565 -8.12853 49.8546 5.71355L1232.12 606.623C1259.72 620.465 1259.72 643.264 1232.12 657.106Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 653 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M1408 736v192c0 53-43 96-96 96H896v416c0 53-43 96-96 96H608c-53 0-96-43-96-96v-416H96c-53 0-96-43-96-96V736c0-53 43-96 96-96h416V224c0-53 43-96 96-96h192c53 0 96 43 96 96v416h416c53 0 96 43 96 96z"/>
</svg>

After

Width:  |  Height:  |  Size: 288 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1511 1056c0 2 0 5-1 7-85 354-377 601-746 601-195 0-384-77-526-212l-129 129c-12 12-28 19-45 19-35 0-64-29-64-64v-448c0-35 29-64 64-64h448c35 0 64 29 64 64 0 17-7 33-19 45l-137 137c94 88 219 138 348 138 178 0 343-92 436-244 24-39 36-77 53-117 5-14 15-23 30-23h192c18 0 32 15 32 32zm25-800v448c0 35-29 64-64 64h-448c-35 0-64-29-64-64 0-17 7-33 19-45l138-138c-95-88-220-137-349-137-178 0-343 92-436 244-24 39-36 77-53 117-5 14-15 23-30 23H50c-18 0-32-15-32-32v-7c86-355 381-601 750-601 196 0 387 78 529 212l130-129c12-12 28-19 45-19 35 0 64 29 64 64z"/>
</svg>

After

Width:  |  Height:  |  Size: 639 B

View file

@ -0,0 +1,3 @@
<svg width="1800" height="1800" xmlns="http://www.w3.org/2000/svg">
<path d="M1536 256v448c0 35-29 64-64 64h-448c-26 0-49-16-59-40-10-23-5-51 14-69l138-138c-94-87-218-137-349-137-282 0-512 230-512 512s230 512 512 512c159 0 306-72 404-199 5-7 14-11 23-12 9 0 18 3 25 9l137 138c12 11 12 30 2 43-146 176-362 277-591 277-423 0-768-345-768-768s345-768 768-768c197 0 388 79 529 212l130-129c18-19 46-24 70-14 23 10 39 33 39 59z"/>
</svg>

After

Width:  |  Height:  |  Size: 435 B

View file

@ -0,0 +1,3 @@
<svg width="1836" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1792 1120c0 140-70 323-127 451-11 23-22 55-37 76-7 10-14 17-28 17-20 0-32-16-32-35 0-16 4-34 5-50 3-41 5-82 5-123 0-477-283-560-714-560H640v256c0 35-29 64-64 64-17 0-33-7-45-19L19 685C7 673 0 657 0 640s7-33 19-45L531 83c12-12 28-19 45-19 35 0 64 29 64 64v256h224c328 0 736 58 875 403 42 106 53 221 53 333z"/>
</svg>

After

Width:  |  Height:  |  Size: 398 B

View file

@ -0,0 +1,3 @@
<svg width="1836" height="1836" xmlns="http://www.w3.org/2000/svg">
<path d="M1152 832c0-247-201-448-448-448S256 585 256 832s201 448 448 448 448-201 448-448zm512 832c0 70-58 128-128 128-34 0-67-14-90-38l-343-342c-117 81-257 124-399 124-389 0-704-315-704-704s315-704 704-704 704 315 704 704c0 142-43 282-124 399l343 343c23 23 37 56 37 90z"/>
</svg>

After

Width:  |  Height:  |  Size: 352 B

View file

@ -0,0 +1,3 @@
<svg width="1536" height="1536" xmlns="http://www.w3.org/2000/svg">
<path d="M640 1440c0 28 13 96-32 96H288c-159 0-288-129-288-288V544c0-159 129-288 288-288h320c17 0 32 15 32 32 0 28 13 96-32 96H288c-88 0-160 72-160 160v704c0 88 72 160 160 160h288c25 0 64-5 64 32zm928-544c0 17-7 33-19 45l-544 544c-12 12-28 19-45 19-35 0-64-29-64-64v-288H448c-35 0-64-29-64-64V704c0-35 29-64 64-64h448V352c0-35 29-64 64-64 17 0 33 7 45 19l544 544c12 12 19 28 19 45z"/>
</svg>

After

Width:  |  Height:  |  Size: 464 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1134 1075c-50 161-197 269-366 269s-316-108-366-269c-11-34 8-69 42-80 33-11 69 8 80 42 33 107 132 179 244 179s211-72 244-179c11-34 47-53 81-42 33 11 52 46 41 80zM640 640c0 71-57 128-128 128s-128-57-128-128 57-128 128-128 128 57 128 128zm512 0c0 71-57 128-128 128s-128-57-128-128 57-128 128-128 128 57 128 128zm256 256c0-353-287-640-640-640S128 543 128 896s287 640 640 640 640-287 640-640zm128 0c0 424-344 768-768 768S0 1320 0 896s344-768 768-768 768 344 768 768z"/>
</svg>

After

Width:  |  Height:  |  Size: 554 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1137 1004l306-297-422-62-189-382-189 382-422 62 306 297-73 421 378-199 377 199zm527-357c0 18-13 35-26 48l-363 354 86 500c1 7 1 13 1 20 0 27-12 50-41 50-14 0-28-5-40-12l-449-236-449 236c-13 7-26 12-40 12-29 0-42-24-42-50 0-7 1-13 2-20l86-500L25 695c-12-13-25-30-25-48 0-30 31-42 56-46l502-73L783 73c9-19 26-41 49-41s40 22 49 41l225 455 502 73c24 4 56 16 56 46z"/>
</svg>

After

Width:  |  Height:  |  Size: 452 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1664 647c0 18-13 35-26 48l-363 354 86 500c1 7 1 13 1 20 0 26-12 50-41 50-14 0-28-5-40-12l-449-236-449 236c-13 7-26 12-40 12-29 0-42-24-42-50 0-7 1-13 2-20l86-500L25 695c-12-13-25-30-25-48 0-30 31-42 56-46l502-73L783 73c9-19 26-41 49-41s40 22 49 41l225 455 502 73c24 4 56 16 56 46z"/>
</svg>

After

Width:  |  Height:  |  Size: 373 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M1149 1122c0-17-7-33-19-45L949 896l181-181c12-12 19-28 19-45s-7-34-19-46l-90-90c-12-12-29-19-46-19s-33 7-45 19L768 715 587 534c-12-12-28-19-45-19s-34 7-46 19l-90 90c-12 12-19 29-19 46s7 33 19 45l181 181-181 181c-12 12-19 28-19 45s7 34 19 46l90 90c12 12 29 19 46 19s33-7 45-19l181-181 181 181c12 12 28 19 45 19s34-7 46-19l90-90c12-12 19-29 19-46zm387-226c0 424-344 768-768 768S0 1320 0 896s344-768 768-768 768 344 768 768z"/>
</svg>

After

Width:  |  Height:  |  Size: 513 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1228.8pt" height="1228.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(157.306, 189.918)" fill="#000000" d="M1188 956C1188 981 1178 1006 1160 1024L1024 1160C1006 1178 981 1188 956 1188C931 1188 906 1178 888 1160L594 866L300 1160C282 1178 257 1188 232 1188C207 1188 182 1178 164 1160L28 1024C10 1006 0 981 0 956C0 931 10 906 28 888L322 594L28 300C10 282 0 257 0 232C0 207 10 182 28 164L164 28C182 10 207 0 232 0C257 0 282 10 300 28L594 322L888 28C906 10 931 0 956 0C981 0 1006 10 1024 28L1160 164C1178 182 1188 207 1188 232C1188 257 1178 282 1160 300L866 594L1160 888C1178 906 1188 931 1188 956Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 954 B

View file

@ -0,0 +1,3 @@
<svg width="1736" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M512 1376V672c0-18-14-32-32-32h-64c-18 0-32 14-32 32v704c0 18 14 32 32 32h64c18 0 32-14 32-32zm256 0V672c0-18-14-32-32-32h-64c-18 0-32 14-32 32v704c0 18 14 32 32 32h64c18 0 32-14 32-32zm256 0V672c0-18-14-32-32-32h-64c-18 0-32 14-32 32v704c0 18 14 32 32 32h64c18 0 32-14 32-32zM480 384h448l-48-117c-3-4-12-10-17-11H546c-6 1-14 7-17 11zm928 32v64c0 18-14 32-32 32h-96v948c0 110-72 204-160 204H288c-88 0-160-90-160-200V512H32c-18 0-32-14-32-32v-64c0-18 14-32 32-32h309l70-167c20-49 80-89 133-89h320c53 0 113 40 133 89l70 167h309c18 0 32 14 32 32z"/>
</svg>

After

Width:  |  Height:  |  Size: 635 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1228.8pt" height="1228.8pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(70.6399, 130.078)" fill="#000000" d="M1414.88 368.474L1414.88 579.031C1414.88 607.818 1390.22 631.67 1360.46 631.67L1306.04 631.67C1276.28 631.67 1251.62 607.818 1251.62 579.031L1251.62 368.474C1251.62 252.504 1153.84 157.918 1033.95 157.918C914.057 157.918 816.275 252.504 816.275 368.474L816.275 526.392L897.902 526.392C942.967 526.392 979.529 561.759 979.529 605.351L979.529 1079.1C979.529 1122.7 942.967 1158.06 897.902 1158.06L81.6275 1158.06C36.5623 1158.06 0 1122.7 0 1079.1L0 605.351C0 561.759 36.5623 526.392 81.6275 526.392L653.02 526.392L653.02 368.474C653.02 165.32 823.927 0 1033.95 0C1243.97 0 1414.88 165.32 1414.88 368.474Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1440pt" height="1280pt">
<defs/>
<g id="layer0">
<path id="shape0" transform="translate(67.1447, 207.932)" fill="#000000" d="M570.324 601.053C398.578 601.053 259.238 466.442 259.238 300.527C259.238 134.611 398.578 0 570.324 0C742.069 0 881.409 134.611 881.409 300.527C881.409 466.442 742.069 601.053 570.324 601.053ZM1348.04 701.229L1633.2 701.229C1646.97 701.229 1659.12 712.968 1659.12 726.272L1659.12 876.536C1659.12 889.84 1646.97 901.58 1633.2 901.58L1348.04 901.58L1348.04 1177.06C1348.04 1190.37 1335.89 1202.11 1322.11 1202.11L1166.57 1202.11C1152.8 1202.11 1140.65 1190.37 1140.65 1177.06L1140.65 901.58L855.485 901.58C841.713 901.58 829.562 889.84 829.562 876.536L829.562 726.272C829.562 712.968 841.713 701.229 855.485 701.229L1140.65 701.229L1140.65 425.746C1140.65 412.441 1152.8 400.702 1166.57 400.702L1322.11 400.702C1335.89 400.702 1348.04 412.441 1348.04 425.746ZM751.79 876.536C751.79 931.319 798.777 976.711 855.485 976.711L1062.88 976.711L1062.88 1162.98C1023.18 1191.15 972.953 1202.11 924.345 1202.11L216.302 1202.11C86.6827 1202.11 0 1126.97 0 999.407C0 822.535 42.9363 550.965 280.301 550.965C293.263 550.965 302.174 556.444 311.896 564.27C391.287 622.966 468.249 659.75 570.324 659.75C672.399 659.75 749.36 622.966 828.751 564.27C838.473 556.444 847.384 550.965 860.346 550.965C929.206 550.965 989.965 576.009 1036.14 626.097L855.485 626.097C798.777 626.097 751.79 671.489 751.79 726.272Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,3 @@
<svg width="1936" height="1736" xmlns="http://www.w3.org/2000/svg">
<path d="M593 896c-104 3-198 48-265 128H194C94 1024 0 976 0 865c0-81-3-353 124-353 21 0 125 85 260 85 46 0 90-8 133-23-3 22-5 44-5 66 0 91 29 181 81 256zm1071 637c0 162-107 259-267 259H523c-160 0-267-97-267-259 0-226 53-573 346-573 34 0 158 139 358 139s324-139 358-139c293 0 346 347 346 573zM640 256c0 141-115 256-256 256S128 397 128 256 243 0 384 0s256 115 256 256zm704 384c0 212-172 384-384 384S576 852 576 640s172-384 384-384 384 172 384 384zm576 225c0 111-94 159-194 159h-134c-67-80-161-125-265-128 52-75 81-165 81-256 0-22-2-44-5-66 43 15 87 23 133 23 135 0 239-85 260-85 127 0 124 272 124 353zm-128-609c0 141-115 256-256 256s-256-115-256-256S1395 0 1536 0s256 115 256 256z"/>
</svg>

After

Width:  |  Height:  |  Size: 761 B

59
src/common/alarm.h Normal file
View file

@ -0,0 +1,59 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef ALARM_H
#define ALARM_H
#include <QObject>
class ALARM : public QObject
{
Q_OBJECT
//Q_PROPERTY(int alarmtime READ alarmtime WRITE setAlarm NOTIFY alarmChanged)
public:
static ALARM *instance();
explicit ALARM(QObject *parent = 0);
//int alarmtime() const;
signals:
void alarmChanged(QString url);
public slots:
void setAlarm(int time);
void notify(QString title, QString text, int id);
private:
int m_time;
};
#endif // UPDATENEWS_H

View file

@ -0,0 +1,64 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "alarm.h"
#include <QtCore/QDebug>
#include "AndroidNative/systemdispatcher.h"
ALARM *ALARM::instance()
{
static ALARM alarm;
return &alarm;
}
ALARM::ALARM(QObject *parent) : QObject(parent){}
void ALARM::setAlarm(int interval)
{
//qDebug() << "alarm "<< interval;
QVariantMap message;
message["value"] = interval;
AndroidNative::SystemDispatcher::instance()->loadClass("androidnative.Util");
AndroidNative::SystemDispatcher::instance()->dispatch("androidnative.Util.setPostNotification", message);
AndroidNative::SystemDispatcher::instance()->dispatch("androidnative.Util.setSchedule", message);
AndroidNative::SystemDispatcher::instance()->dispatch("androidnative.Util.stopService", message);
}
void ALARM::notify(QString title, QString text, int id)
{
//qDebug() << "notify "<< title << text;
QVariantMap message;
message["title"] = title;
message["message"] = text;
message["id"] = id;
AndroidNative::SystemDispatcher::instance()->loadClass("androidnative.Util");
AndroidNative::SystemDispatcher::instance()->dispatch("androidnative.Util.setNotification", message);
}

80
src/common/alarmlinux.cpp Normal file
View file

@ -0,0 +1,80 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2017 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "alarm.h"
#include <QtCore/QDebug>
#include <QtDBus/QtDBus>
#include <QJsonArray>
#include <QStringList>
ALARM *ALARM::instance()
{
static ALARM alarm;
return &alarm;
}
ALARM::ALARM(QObject *parent) : QObject(parent){}
void ALARM::setAlarm(int interval)
{
//QVariantMap message;
//message["value"] = interval;
if (interval==0){
QProcess processDisable;
int intDisable = processDisable.execute("systemctl", QStringList() <<"--user"<<"disable"<<"--now"<<"friendiqa-sync.timer");
qDebug() << " intEnable "<<intDisable;
}else{
QProcess processReload;
int intReload = processReload.execute("systemctl", QStringList()<<"--user"<<"daemon-reload");
QProcess processEnable;
int intEnable = processEnable.execute("systemctl", QStringList() <<"--user"<<"enable"<<"--now"<<"friendiqa-sync.timer");
qDebug() << "intReload "<<intReload << " intEnable "<<intEnable;
}
}
void ALARM::notify(QString title, QString text, int id)
{
//qDebug() << title << text;
// QVariantMap message;
// message["title"] = title;
// message["message"] = text;
QStringList actionlist;
QMap<QString,QVariant> hint;
QDBusConnection bus = QDBusConnection::sessionBus();
QDBusInterface dbus_iface("org.freedesktop.Notifications", "/org/freedesktop/Notifications",
"org.freedesktop.Notifications", bus);
QString appname="Friendiqa";
uint v=12321;
if (dbus_iface.isValid()){
dbus_iface.call("Notify",appname,v,"",title,text,actionlist,hint,10000);
//qDebug() << "Qdebug error " << dbus_iface.lastError();
}
}

View file

@ -0,0 +1,262 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QQuickTextDocument>
#include <QTextCharFormat>
#include <QStringDecoder>
#include <QTextDocument>
#include <QTextDocumentFragment>
#include <QTextList>
#include <QDebug>
#include "documenthandler.h"
DocumentHandler::DocumentHandler(QObject *parent)
: QObject(parent)
, m_document(nullptr)
, m_cursorPosition(-1)
, m_selectionStart(0)
, m_selectionEnd(0)
{
}
QQuickTextDocument *DocumentHandler::document() const
{
return m_document;
}
void DocumentHandler::setDocument(QQuickTextDocument *document)
{
if (document == m_document)
return;
if (m_document)
disconnect(m_document->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
m_document = document;
if (m_document)
connect(m_document->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
emit documentChanged();
}
int DocumentHandler::cursorPosition() const
{
return m_cursorPosition;
}
void DocumentHandler::setCursorPosition(int position)
{
if (position == m_cursorPosition)
return;
m_cursorPosition = position;
emit cursorPositionChanged();
}
int DocumentHandler::selectionStart() const
{
return m_selectionStart;
}
void DocumentHandler::setSelectionStart(int position)
{
if (position == m_selectionStart)
return;
m_selectionStart = position;
emit selectionStartChanged();
}
int DocumentHandler::selectionEnd() const
{
return m_selectionEnd;
}
void DocumentHandler::setSelectionEnd(int position)
{
if (position == m_selectionEnd)
return;
m_selectionEnd = position;
emit selectionEndChanged();
}
QTextCursor DocumentHandler::textCursor() const
{
QTextDocument *doc = textDocument();
if (!doc)
return QTextCursor();
QTextCursor cursor = QTextCursor(doc);
if (m_selectionStart != m_selectionEnd) {
cursor.setPosition(m_selectionStart);
cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
} else {
cursor.setPosition(m_cursorPosition);
}
return cursor;
}
QTextDocument *DocumentHandler::textDocument() const
{
if (!m_document)
return nullptr;
return m_document->textDocument();
}
void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
{
QTextCursor cursor = textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
}
bool DocumentHandler::modified() const
{
return m_document && m_document->textDocument()->isModified();
}
void DocumentHandler::setModified(bool m)
{
if (m_document)
m_document->textDocument()->setModified(m);
}
QFont DocumentHandler::font() const
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return m_document->textDocument()->defaultFont();
QTextCharFormat format = cursor.charFormat();
return format.font();
}
void DocumentHandler::setFont(const QFont & font){
QTextCursor cursor = textCursor();
if (!cursor.isNull() && cursor.charFormat().font() == font)
return;
QTextCharFormat format;
format.setFont(font);
mergeFormatOnWordOrSelection(format);
emit fontChanged();
}
bool DocumentHandler::bold() const
{
const QTextCursor cursor = textCursor();
if (cursor.isNull())
return m_document->textDocument()->defaultFont().bold();
return cursor.charFormat().font().bold();
}
void DocumentHandler::setBold(bool bold)
{
const QTextCursor cursor = textCursor();
if (!cursor.isNull() && cursor.charFormat().font().bold() == bold)
return;
QFont font = cursor.charFormat().font();
font.setBold(bold);
QTextCharFormat format;
format.setFont(font);
mergeFormatOnWordOrSelection(format);
emit boldChanged();
}
bool DocumentHandler::italic() const
{
const QTextCursor cursor = textCursor();
if (cursor.isNull())
return m_document->textDocument()->defaultFont().italic();
return cursor.charFormat().font().italic();
}
void DocumentHandler::setItalic(bool italic)
{
const QTextCursor cursor = textCursor();
if (!cursor.isNull() && cursor.charFormat().font().italic() == italic)
return;
QFont font = cursor.charFormat().font();
font.setItalic(italic);
QTextCharFormat format;
format.setFont(font);
mergeFormatOnWordOrSelection(format);
emit italicChanged();
}
bool DocumentHandler::liststyle() const
{
const QTextCursor cursor = textCursor();
if (cursor.isNull())
return false;
return bool(cursor.currentList());
}
void DocumentHandler::setListstyle(bool liststyle)
{
QTextCursor cursor = textCursor();
if (!cursor.isNull() && !liststyle){
cursor.currentList()->remove(cursor.block());
emit liststyleChanged();
}else{
cursor.createList(QTextListFormat::ListDisc);
emit liststyleChanged();
}
}
bool DocumentHandler::codeblock() const
{
const QTextCursor cursor = textCursor();
if (cursor.isNull())
return false;
qDebug()<< QTextDocumentFragment(cursor).toPlainText();
return bool(QTextDocumentFragment(cursor).toMarkdown().contains("```"));
}
void DocumentHandler::setCodeblock(bool codeblock)
{
QTextCursor cursor = textCursor();
if (!cursor.isNull() && !codeblock){
qDebug()<< "!codeblock ```\n" + QTextDocumentFragment(cursor).toMarkdown() + "\n```";
cursor.insertMarkdown("```\n" + QTextDocumentFragment(cursor).toMarkdown() + "\n```");
emit codeblockChanged();
}
else{
qDebug()<< "```\n" + QTextDocumentFragment(cursor).toMarkdown() + "\n```";
cursor.insertMarkdown(QTextDocumentFragment(cursor).toMarkdown().remove("```"));
emit codeblockChanged();
}
}

View file

@ -0,0 +1,114 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef DOCUMENTHANDLER_H
#define DOCUMENTHANDLER_H
//#include <QFont>
#include <QObject>
#include <QTextCursor>
#include <QTextDocument>
#include <QQuickTextDocument>
//QT_BEGIN_NAMESPACE
//class QTextDocument;
//class QQuickTextDocument;
//QT_END_NAMESPACE
class DocumentHandler : public QObject{
Q_OBJECT
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
Q_PROPERTY(bool italic READ italic WRITE setItalic NOTIFY italicChanged)
Q_PROPERTY(bool liststyle READ liststyle WRITE setListstyle NOTIFY liststyleChanged)
Q_PROPERTY(bool codeblock READ codeblock WRITE setCodeblock NOTIFY codeblockChanged)
Q_PROPERTY(bool modified READ modified WRITE setModified NOTIFY modifiedChanged)
public:
explicit DocumentHandler(QObject *parent = nullptr);
QQuickTextDocument *document() const;
void setDocument(QQuickTextDocument *document);
int cursorPosition() const;
void setCursorPosition(int position);
int selectionStart() const;
void setSelectionStart(int position);
int selectionEnd() const;
void setSelectionEnd(int position);
QFont font() const;
void setFont(const QFont & font);
bool bold() const;
void setBold(bool bold);
bool italic() const;
void setItalic(bool italic);
bool liststyle() const;
void setListstyle(bool liststyle);
bool modified() const;
void setModified(bool m);
bool codeblock() const;
void setCodeblock(bool codeblock);
signals:
void documentChanged();
void cursorPositionChanged();
void selectionStartChanged();
void selectionEndChanged();
void fontChanged();
void boldChanged();
void italicChanged();
void liststyleChanged();
void codeblockChanged();
void error(const QString &message);
void modifiedChanged();
private:
QTextCursor textCursor() const;
QTextDocument *textDocument() const;
void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
QQuickTextDocument *m_document;
int m_cursorPosition;
int m_selectionStart;
int m_selectionEnd;
};
#endif // DOCUMENTHANDLER_H

192
src/common/filesystem.cpp Normal file
View file

@ -0,0 +1,192 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "filesystem.h"
#include <QDebug>
FILESYSTEM *FILESYSTEM::instance()
{
static FILESYSTEM filesystem;
return &filesystem;
}
FILESYSTEM::FILESYSTEM(QObject *parent) : QObject(parent){}
void FILESYSTEM::setDirectory(QString Directory)
{
if (Directory!=m_Directory) {
m_Directory = Directory;
emit directoryChanged();
}
}
QString FILESYSTEM::Directory() const
{
return m_Directory;
}
void FILESYSTEM::setVisibility(bool Visibility)
{
if (Visibility!=m_Visibility) {
m_Visibility = Visibility;
emit visibilityChanged();
}
}
bool FILESYSTEM::Visibility()
{
return m_Visibility;
}
QString FILESYSTEM::homePath() const
{
QString homeDir=QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);;
return homeDir;
}
//QString FILESYSTEM::cameraPath() const
//{
//QAndroidJniObject object = QAndroidJniObject::getStaticObjectField<jstring>("android.os.Environment", "DIRECTORY_DCIM");
//QAndroidJniObject dcim =QAndroidJniObject::callStaticObjectMethod("android.os.Environment","getExternalStoragePublicDirectory", "(Ljava/lang/String;)Ljava/io/File;", object.object<jobject>());
// return dcim.toString();
//}
//bool FILESYSTEM::direxist(QString Directory) const
//{QDir dir(Directory);
// return dir.exists();
//}
bool FILESYSTEM::fileexist(QString name)
{ return QFile::exists(name);
}
void FILESYSTEM::makeDir(QString name)
{
QDir dir(m_Directory);
if (dir.mkdir(name)){
emit success(name);
}
else {
emit error(name,1);
}
}
void FILESYSTEM::makePath(QString name)
{
QDir dir(m_Directory);
if (dir.mkpath(name)){
emit success(name);
}
else {
qDebug() << "makepath error" <<name;
emit error(name,1);}
}
void FILESYSTEM::rmDir()
{
QDir dir(m_Directory);
if (dir.removeRecursively()){
emit success(m_Directory);
}
else {emit error(m_Directory,1);}
}
void FILESYSTEM::rmFile(QString name)
{
QDir dir(m_Directory);
if(dir.remove(name)){
emit success(name);
}
else {emit error(name,1);}
}
QFileInfoList FILESYSTEM::fileList()
{
QDir dir(m_Directory);
QStringList filters;
filters << "*.png" <<"*.PNG" << "*.jpg" << "*.JPG" << "*.JPEG";
dir.setNameFilters(filters);
dir.setSorting(QDir::Time | QDir::Reversed);
return dir.entryInfoList();
}
bool FILESYSTEM::isAutostart() {
QFileInfo check_file(QDir::homePath() + "/.config/autostart/friendiqa.desktop");
if (check_file.exists() && check_file.isFile()) {
return true;
}
return false;
}
void FILESYSTEM::setAutostart(bool autostart) {
QString path = QDir::homePath() + "/.config/autostart/";
QString name ="friendiqa.desktop";
QFile file(path+name);
file.remove();
if(autostart) {
QDir dir(path);
if(!dir.exists()) {
dir.mkpath(path);
}
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << "[Desktop Entry]" << Qt::endl;
stream << "Name=Friendiqa" << Qt::endl;
stream << "Exec=friendiqa -background %u" << Qt::endl;
stream << "Terminal=false" << Qt::endl;
stream << "Icon=Friendiqa.svg" << Qt::endl;
stream << "Type=Application" << Qt::endl;
stream << "StartupNotify=false" << Qt::endl;
stream << "X-GNOME-Autostart-enabled=true" << Qt::endl;
}
}
}
QString FILESYSTEM::osType() const
{
QString m_osType;
if(QSysInfo::productType()==QString("android")){
m_osType="Android";
}else{
m_osType="Linux";
}
return m_osType;
}
QString FILESYSTEM::hostname() const
{
return QSysInfo::machineHostName();
}

97
src/common/filesystem.h Normal file
View file

@ -0,0 +1,97 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include <QDir>
#include <QObject>
#include <QStandardPaths>
#include <QSysInfo>
//#include <QtAndroidExtras>
//#include <QAndroidActivityResultReceiver>
class FILESYSTEM : public QObject//, public QAndroidActivityResultReceiver
{
Q_OBJECT
Q_PROPERTY(QString Directory READ Directory WRITE setDirectory NOTIFY directoryChanged)
Q_PROPERTY(bool Visibility READ Visibility WRITE setVisibility NOTIFY visibilityChanged)
//Q_PROPERTY(bool direxist READ direxist)
Q_PROPERTY(QString homePath READ homePath)
Q_PROPERTY(bool isAutostart READ isAutostart NOTIFY isAutostartChanged)
Q_PROPERTY(QString osType READ osType CONSTANT)
Q_PROPERTY(QString hostname READ hostname CONSTANT)
//Q_PROPERTY(QString cameraPath READ cameraPath)
public:
static FILESYSTEM *instance();
explicit FILESYSTEM(QObject *parent = 0);
void setDirectory(QString Directory);
void setVisibility(bool Visibility);
QString Directory() const;
QFileInfoList fileList();
//bool direxist(QString Directory);
QString homePath() const;
bool Visibility();
bool isAutostart();
QString osType() const;
QString hostname() const;
//QString cameraPath() const;
// virtual void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data);
signals:
//void imageselected(QString);
void directoryChanged();
void visibilityChanged();
void isAutostartChanged();
//void fileListContent(QList data);
void success(QString data);
void error(QString data, int code);
public slots:
bool fileexist(QString name);
void makeDir(QString name);
void makePath(QString name);
void rmDir();
void rmFile(QString name);
void setAutostart(bool autostart);
//void searchImage();
//void fileList();
private:
QString m_Directory;
QString homeDir;
bool m_Visibility;
//QList m_Filelist;
};
#endif // FILSYSTEM_H

View file

@ -0,0 +1,170 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "filesystem.h"
#include <QtAndroidExtras>
FILESYSTEM *FILESYSTEM::instance()
{
static FILESYSTEM filesystem;
return &filesystem;
}
FILESYSTEM::FILESYSTEM(QObject *parent) : QObject(parent){}
void FILESYSTEM::setDirectory(QString Directory)
{
if (Directory!=m_Directory) {
m_Directory = Directory;
emit directoryChanged();
}
}
QString FILESYSTEM::Directory() const
{
return m_Directory;
}
void FILESYSTEM::setVisibility(bool Visibility)
{
if (Visibility!=m_Visibility) {
m_Visibility = Visibility;
emit visibilityChanged();
}
}
bool FILESYSTEM::Visibility()
{
return m_Visibility;
}
QString FILESYSTEM::homePath() const
{
QAndroidJniObject activity =QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative","activity", "()Landroid/app/Activity;");
QAndroidJniObject dir = activity.callObjectMethod("getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;", NULL);
QString homeDir=dir.toString();
return homeDir;
}
bool FILESYSTEM::fileexist(QString name)
{ return QFile::exists(name);
}
void FILESYSTEM::makeDir(QString name)
{
QDir dir(m_Directory);
if (dir.mkdir(name)){
emit success(name);
}
else {
emit error(name,1);
}
}
void FILESYSTEM::makePath(QString name)
{
QDir dir(m_Directory);
if (dir.mkpath(name)){
emit success(name);
}
else {
emit error(name,1);}
}
void FILESYSTEM::rmDir()
{
QDir dir(m_Directory);
if (dir.removeRecursively()){
emit success(m_Directory);
}
else {emit error(m_Directory,1);}
}
void FILESYSTEM::rmFile(QString name)
{
QDir dir(m_Directory);
if(dir.remove(name)){
emit success(name);
}
else {emit error(name,1);}
}
QFileInfoList FILESYSTEM::fileList()
{
QDir dir(m_Directory);
QStringList filters;
filters << "*.png" <<"*.PNG" << "*.jpg" << "*.JPG" << "*.JPEG";
dir.setNameFilters(filters);
dir.setSorting(QDir::Time | QDir::Reversed);
return dir.entryInfoList();
}
bool FILESYSTEM::isAutostart() {
QFileInfo check_file(QDir::homePath() + "/.config/autostart/friendiqa.desktop");
if (check_file.exists() && check_file.isFile()) {
qDebug()<<"autostart "<<true;
return true;
}
qDebug()<<"autostart "<<false;
return false;
}
void FILESYSTEM::setAutostart(bool autostart) {
QString path = QDir::homePath() + "/.config/autostart/";
QString name ="friendiqa.desktop";
QFile file(path+name);
file.remove();
if(autostart) {
QDir dir(path);
if(!dir.exists()) {
dir.mkpath(path);
}
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << "[Desktop Entry]" << Qt::endl;
stream << "Exec=friendiqa -background %u" << Qt::endl;
stream << "Type=Application" << Qt::endl;
}
}
}
QString FILESYSTEM::osType() const
{
return QSysInfo::productType();
}
QString FILESYSTEM::hostname() const
{
return QSysInfo::machineHostName();
}

131
src/common/friendiqa.cpp Normal file
View file

@ -0,0 +1,131 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2017 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QApplication>
#include <QtQml/QQmlEngine>
#include <QtQml/qqml.h>
//#include <QtWebEngine>
//#include <QAndroidService>
//#include <QtAndroid>
#include <QtQuick>
#include "xhr.h"
#include "updatenews.h"
#include "filesystem.h"
//#include "remoteauthasyncimageprovider.h"
#include "alarm.h"
#include "oauth.h"
#include "documenthandler.h"
//#include "AndroidNative/systemdispatcher.h"
//#include "AndroidNative/environment.h"
//#include "AndroidNative/debug.h"
//#include "AndroidNative/mediascannerconnection.h"
//#include <QQuickWidget>
#include <QSystemTrayIcon>
#include <QQmlContext>
#include <QQuickStyle>
// Declare a user-defined data type to work with an icon in QML
Q_DECLARE_METATYPE(QSystemTrayIcon::ActivationReason)
#ifdef Q_OS_ANDROID
#include <QtAndroidExtras/QAndroidJniObject>
#include <QtAndroidExtras/QAndroidJniEnvironment>
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
Q_UNUSED(vm);
qDebug("NativeInterface::JNI_OnLoad()"); // It must call this function within JNI_OnLoad to enable System Dispatcher
AndroidNative::SystemDispatcher::registerNatives();
return JNI_VERSION_1_6;
}
#endif
int main(int argc, char *argv[]) {
//qDebug()<< "argv Friendiqa"<< (qstrcmp(argv[1],"-service")==0) <<" argv2" <<argv[1];
QCoreApplication::setApplicationName("Friendiqa");
QCoreApplication::setOrganizationName("Friendiqa");
QApplication app(argc, argv);
if (qstrcmp(argv[1],"-service")==0){
//qDebug()<<"FriendiqaMain Service";
//QAndroidService app(argc, argv);
UPDATENEWS* updatenews= UPDATENEWS::instance();
updatenews->setDatabase();
updatenews->login();
updatenews->setSyncAll(true);
updatenews->startsync();
app.connect (updatenews,SIGNAL(quitapp()),&app,SLOT(quit()));
//QtAndroid::androidService().callMethod<void>("stopSelf");
return app.exec();
}
else{
QQmlApplicationEngine view;
//view.setResizeMode(QQuickView::SizeRootObjectToView);
app.setWindowIcon(QIcon(":/assets/Friendiqa.ico"));
QTranslator qtTranslator;
if(qtTranslator.load("friendiqa-" + QLocale::system().name(),":/translations"))
{app.installTranslator(&qtTranslator);}
// RemoteAuthAsyncImageProvider *imageProvider = new RemoteAuthAsyncImageProvider;
// view.addImageProvider("remoteauthimage",imageProvider);
// view.rootContext()->setContextProperty("remoteauth", imageProvider);
XHR* xhr = XHR::instance();
view.rootContext()->setContextProperty("xhr", xhr);
FILESYSTEM* filesystem = FILESYSTEM::instance();
if (qstrcmp(argv[1],"-background")==0){
filesystem->setVisibility(false);
} else{filesystem->setVisibility(true);}
view.rootContext()->setContextProperty("filesystem", filesystem);
ALARM* alarm = ALARM::instance();
view.rootContext()->setContextProperty("alarm", alarm);
UPDATENEWS* updatenews = UPDATENEWS::instance();
view.rootContext()->setContextProperty("updatenews", updatenews);
updatenews->setDatabase();
OAuthWrapper* oauth2 = OAuthWrapper::instance();
view.rootContext()->setContextProperty("oauth2", oauth2);
qmlRegisterType<DocumentHandler>("io.qt.examples.texteditor", 1, 0, "DocumentHandler");
qmlRegisterType<QSystemTrayIcon>("QSystemTrayIcon", 1, 0, "QSystemTrayIcon");
qRegisterMetaType<QSystemTrayIcon::ActivationReason>("ActivationReason");
view.rootContext()->setContextProperty("iconTrayBlack", QIcon(QPixmap(":/assets/friendica-tray-black.svg")));
view.rootContext()->setContextProperty("iconTrayWhite", QIcon(QPixmap(":/assets/friendica-tray-white.svg")));
view.rootContext()->setContextProperty("iconTrayAvailable", QSystemTrayIcon::isSystemTrayAvailable());
if(updatenews->getStyle() != 0){
QQuickStyle::setStyle("Material");
}
view.load(QUrl("qrc:/qml/friendiqa.qml"));
view.connect(view.rootContext()->engine(), SIGNAL(quit()), &app, SLOT(quit()));
return app.exec();
}
}

88
src/common/oauth.cpp Normal file
View file

@ -0,0 +1,88 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2023 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "oauth.h"
#include <QtGui>
#include <QtCore>
#include <QtNetworkAuth>
OAuthWrapper *OAuthWrapper::instance()
{
static OAuthWrapper oa2;
return &oa2;
}
OAuthWrapper::OAuthWrapper(QObject *parent) : QObject(parent)
{
auto replyHandler = new QOAuthHttpServerReplyHandler(1337, this);
oauth2.setReplyHandler(replyHandler);
oauth2.setScope("read+write+follow+push");
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::statusChanged, [=](
QAbstractOAuth::Status status) {
if (status == QAbstractOAuth::Status::Granted){
QMapIterator<QString, QVariant> i(oauth2.extraTokens());
while (i.hasNext()) {
i.next();
//qDebug() << i.key() << ": " << i.value() << Qt::endl;
}
emit success(oauth2.token());
}
});
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser,
&QDesktopServices::openUrl);
}
void OAuthWrapper::setClientId(QString clientid)
{
m_clientid = clientid;
}
void OAuthWrapper::setServer(QString server)
{
m_server = server;
}
void OAuthWrapper::setClientSecret(QString clientsecret)
{
m_clientsecret = clientsecret;
}
void OAuthWrapper::grant()
{
oauth2.setClientIdentifier(m_clientid);
oauth2.setAuthorizationUrl(QUrl(m_server + "/oauth/authorize"));
oauth2.setAccessTokenUrl(QUrl(m_server + "/oauth/token"));
oauth2.setClientIdentifierSharedKey(m_clientsecret);
oauth2.grant();
}

71
src/common/oauth.h Normal file
View file

@ -0,0 +1,71 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef OAUTH_H
#define OAUTH_H
#include <QtCore>
#include <QtNetwork>
#include <QOAuth2AuthorizationCodeFlow>
class OAuthWrapper : public QObject//, public QAndroidActivityResultReceiver
{
Q_OBJECT
public:
OAuthWrapper(QObject *parent = nullptr);;
OAuthWrapper(const QString &clientIdentifier, QObject *parent = nullptr);
static OAuthWrapper *instance();
// bool isPermanent() const;
// void setPermanent(bool value);
signals:
void success(QString token);
void error(QString error);
public slots:
void grant();
void setServer(QString server);
void setClientId(QString clientid);
void setClientSecret(QString clientsecret);
private:
QOAuth2AuthorizationCodeFlow oauth2;
// bool permanent = false;
QString m_clientid;
QString m_clientsecret;
QString m_server;
};
#endif //OAuthWrapper

View file

@ -0,0 +1,123 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "remoteauthasyncimageprovider.h"
#include <QQuickAsyncImageProvider>
#include <QImage>
AsyncImageResponse::AsyncImageResponse(QNetworkRequest req, QSize reqSize)
{
m_reply = m_imageLoader.get(req);
m_requestedSize = reqSize;
connect(m_reply, &QNetworkReply::finished, this, &AsyncImageResponse::onResponseFinished);
}
void AsyncImageResponse::onResponseFinished()
{
QByteArray myImageData = m_reply->readAll();
m_resultImage = QImage::fromData(myImageData);
if (m_requestedSize.isValid())
{
m_resultImage = m_resultImage.scaled(m_requestedSize);
}
emit finished();
}
QQuickTextureFactory *AsyncImageResponse::textureFactory() const
{
return QQuickTextureFactory::textureFactoryForImage(m_resultImage);
}
RemoteAuthAsyncImageProvider::RemoteAuthAsyncImageProvider()
{
}
QQuickImageResponse* RemoteAuthAsyncImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
{
QUrl iUrl=url()+"/api/friendica/remoteauth?c_url="+contacturl()+"&url="+id;
QByteArray loginData = m_login.toLocal8Bit().toBase64();
QString headerData = "Basic " + loginData;
QNetworkRequest request(iUrl);
request.setRawHeader("Authorization", headerData.toLocal8Bit());
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute,true);
request.setUrl(iUrl);
return new AsyncImageResponse(request, requestedSize);
}
void RemoteAuthAsyncImageProvider::setContacturl(QString contacturl)
{
if (contacturl!=m_contacturl) {
m_contacturl = contacturl;
emit contacturlChanged();
}
}
void RemoteAuthAsyncImageProvider::setUrl(QString url)
{
if (url!=m_url) {
m_url = url;
emit urlChanged();
}
}
void RemoteAuthAsyncImageProvider::setLogin(QString login)
{
if (login!=m_login) {
m_login = login;
emit loginChanged();
}
}
QString RemoteAuthAsyncImageProvider::contacturl() const
{
return m_contacturl;
}
QString RemoteAuthAsyncImageProvider::url() const
{
return m_url;
}
QString RemoteAuthAsyncImageProvider::login() const
{
return m_login;
}

View file

@ -0,0 +1,95 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef REMOTEAUTHIMAGEPROVIDER_H
#define REMOTEAUTHIMAGEPROVIDER_H
#include <QObject>
#include <QQuickAsyncImageProvider>
#include <QImage>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
class AsyncImageResponse : public QQuickImageResponse
{
Q_OBJECT
public:
explicit AsyncImageResponse(QNetworkRequest req, QSize requestedSize);
QQuickTextureFactory *textureFactory() const;
public slots:
void onResponseFinished();
protected:
QNetworkAccessManager m_imageLoader;
QNetworkReply* m_reply;
QSize m_requestedSize;
QImage m_resultImage;
int m_index;
QString m_id;
QImage m_image;
};
class RemoteAuthAsyncImageProvider : public QObject, public QQuickAsyncImageProvider
{
Q_OBJECT
Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
Q_PROPERTY(QString contacturl READ contacturl WRITE setContacturl NOTIFY contacturlChanged)
Q_PROPERTY(QString login READ login WRITE setLogin NOTIFY loginChanged)
public:
explicit RemoteAuthAsyncImageProvider();
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
QString url() const;
QString contacturl() const;
QString login() const;
signals:
void contacturlChanged();
void urlChanged();
void loginChanged();
public slots:
void setContacturl(QString contacturl);
void setUrl(QString url);
void setLogin(QString login);
private:
QByteArray buffer;
QString m_url;
QString m_contacturl;
QString m_login;
QString bufferToString();
};
#endif // REMOTEAUTHIMAGEPROVIDER_H

861
src/common/updatenews.cpp Normal file
View file

@ -0,0 +1,861 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "updatenews.h"
#include <QHttpPart>
//#include <QTextCodec>
#include <QUrlQuery>
#include <QList>
#include <QDataStream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QQmlEngine>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QSqlDatabase>
#include <QSqlError>
#include <QDateTime>
UPDATENEWS *UPDATENEWS::instance()
{
static UPDATENEWS udn;
return &udn;
}
UPDATENEWS::UPDATENEWS(QObject *parent) : QObject(parent)
{
}
//void UPDATENEWS::setUrl(QString url)
//{
// if (url!=m_url) {
// m_url = url;
// xhr.setUrl(url);
// emit urlChanged(m_url);
// }
//}
void UPDATENEWS::setSyncAll(bool syncAll)
{
m_syncAll=syncAll;
}
void UPDATENEWS::setDatabase()
{
static QQmlEngine qe;
QString db_url=qe.offlineStorageDatabaseFilePath("Friendiqa");
if (!m_db.open())
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName(QUrl("file://"+db_url+".sqlite").toLocalFile());
}
if (!m_db.open())
{
qDebug() << "Error: connection with database fail " << m_db.lastError();
}
}
int UPDATENEWS::getStyle()
{
if (m_db.open()){
QSqlQuery syncquery("SELECT * FROM globaloptions",m_db);
while (syncquery.next()){
if (syncquery.value(0).toString()=="view_darkmode"){
return syncquery.value(1).toInt();
}
}
}
return 0;
}
QJsonArray UPDATENEWS::getAccounts(QString filtername, QString filtervalue){
QString filterstring="";
if (filtername!=""){
bool ok;
int filternumber = filtervalue.toInt(&ok, 10);
if(ok){
filterstring=" WHERE " + filtername + " = " + filtervalue;
}
else{
filterstring=" WHERE " + filtername + " = '" + filtervalue +"'";
}
}
QSqlQuery query("SELECT * FROM config" + filterstring + " ORDER BY isActive ASC, username ASC",m_db);
QJsonArray accountlist;
while(query.next()){
{
QJsonObject accountData;
accountData.insert("server",query.value(0).toString());
accountData.insert("username",query.value(1).toString());
accountData.insert("password",query.value(2).toString());
accountData.insert("imagestore",query.value(3).toString());
accountData.insert("isActive",query.value(7).toString());
QJsonArray m_permissions=query.value(7).toJsonArray();
accountData.insert("permissions",m_permissions);
accountData.insert("token",QString(QByteArray::fromBase64(query.value(10).toByteArray())));
QJsonDocument m_client;
if(query.value(12).toByteArray()!=""){
m_client=QJsonDocument::fromJson(QByteArray::fromBase64(query.value(12).toByteArray()));
}
accountData.insert("client",m_client.object());
accountlist.append(accountData);
}
}
query.clear();
return accountlist;
}
void UPDATENEWS::login()
{
QSqlQuery syncquery("SELECT * FROM globaloptions",m_db);
m_updateInterval=0;
syncindex=0;
synclist.clear();
while (syncquery.next()){
if (syncquery.value(0).toString()=="syncinterval"){
m_updateInterval=syncquery.value(1).toInt();
}
if (syncquery.value(0).toString().left(5)=="sync_" && syncquery.value(1).toInt()==1){
synclist.append(syncquery.value(0).toString());
}
if (syncquery.value(0).toString().left(7)=="notify_" && syncquery.value(1).toInt()==1){
notifylist.append(syncquery.value(0).toString());
}
}
QSqlQuery synctimequery("SELECT * FROM globaloptions WHERE k='lastsync'",m_db);
if (synctimequery.next()){
QSqlQuery synctimequery2("UPDATE globaloptions SET v='"+QString::number(QDateTime::currentSecsSinceEpoch()) + "' WHERE k = 'lastsync'",m_db);
if(!(synctimequery2.exec())) {qDebug()<<" synctimequery2 " << synctimequery2.lastError();}
} else {
QSqlQuery synctimequery3("INSERT INTO globaloptions(k,v) VALUES('lastsync','"+QString::number(QDateTime::currentSecsSinceEpoch()) + "')",m_db);
if(!(synctimequery3.exec())) {qDebug() << " synctimequery3 " << synctimequery3.lastError();}
}
QJsonArray acc=getAccounts();
usernamelength=acc.size();
if (usernameindex<usernamelength){
QJsonObject currentAccount =acc[usernameindex].toObject();
xhr.setAccount(currentAccount.toVariantMap());
username = currentAccount["username"].toString();
m_url=currentAccount["server"].toString();
}
}
void UPDATENEWS::startsync()
{
if (syncindex<synclist.length()){
if (synclist[syncindex]=="sync_Timeline"){
timeline();
} else if (synclist[syncindex]=="sync_Replies") {
replies();
} else if (synclist[syncindex]=="sync_DirectMessages") {
directmessages();
} else if (synclist[syncindex]=="sync_Notifications") {
notifications();
} else if (synclist[syncindex]=="sync_FriendRequests") {
friendrequests();
}else if (synclist[syncindex]=="sync_Events") {
events();
}
} else if ((syncindex==synclist.length())&&(!(usernameindex<usernamelength-1))) {
m_api="";
if(m_updateInterval!=0){
syncindex=0;
usernameindex=0;
synclist.clear();
m_db.close();
m_db.removeDatabase(m_db.connectionName());
QObject::disconnect(&xhr,SIGNAL(error(QString,QString,QString,int)),this,SLOT(showError(QString,QString,QString,int)));
emit quitapp();
//alarm.setAlarm(m_updateInterval);
m_updateInterval=0;
}
}
else{
usernameindex+=1;
login();
startsync();
}
}
void UPDATENEWS::timeline()
{
m_api="/api/statuses/friends_timeline";
xhr.clearParams();
xhr.setUrl(m_url);
xhr.setApi(m_api);
QSqlQuery query("SELECT status_id FROM news WHERE messagetype=0 AND username='"+ username +"' ORDER BY status_id DESC LIMIT 1",m_db);
if (query.isActive() && query.isSelect()){
if (query.first()){
QString lastid=query.value(0).toString();
xhr.setParam("since_id",lastid);
}
}
xhr.setParam("count","100");
xhr.get();
QObject::connect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(store(QByteArray,QString)));
QObject::connect(&xhr,SIGNAL(error(QString,QString,QString,int)),this,SLOT(showError(QString,QString,QString,int)));
QObject::connect(&xhr, SIGNAL(downloaded(QString, QString, QString, int)), this, SLOT(updateImageLocation(QString,QString, QString, int)));
}
void UPDATENEWS::replies()
{
m_api="/api/statuses/replies";
xhr.clearParams();
xhr.setUrl(m_url);
xhr.setApi(m_api);
QSqlQuery query("SELECT status_id FROM news WHERE messagetype=3 AND username='"+ username +"' ORDER BY status_id DESC LIMIT 1",m_db);
if (query.isActive() && query.isSelect()){
if (query.first()){
QString lastid=query.value(0).toString();
xhr.setParam("since_id",lastid);
}
}
xhr.setParam("count","50");
xhr.get();
QObject::connect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(store(QByteArray,QString)));
QObject::connect(&xhr,SIGNAL(error(QString,QString,QString,int)),this,SLOT(showError(QString,QString,QString,int)));
QObject::connect(&xhr, SIGNAL(downloaded(QString, QString, QString, int)), this, SLOT(updateImageLocation(QString,QString, QString, int)));
}
void UPDATENEWS::directmessages()
{
m_api="/api/direct_messages/all";
xhr.clearParams();
xhr.setUrl(m_url);
xhr.setApi(m_api);
QSqlQuery query("SELECT status_id FROM news WHERE messagetype=1 AND username='"+ username +"' ORDER BY status_id DESC LIMIT 1",m_db);
if (query.isActive() && query.isSelect()){
if (query.first()){
QString lastid=query.value(0).toString();
xhr.setParam("since_id",lastid);
}
}
xhr.get();
QObject::connect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(store(QByteArray,QString)));
QObject::connect(&xhr,SIGNAL(error(QString,QString,QString,int)),this,SLOT(showError(QString,QString,QString,int)));
QObject::connect(&xhr, SIGNAL(downloaded(QString, QString, QString, int)), this, SLOT(updateImageLocation(QString,QString, QString, int)));
}
void UPDATENEWS::notifications()
{
m_api="/api/friendica/notification";
xhr.clearParams();
xhr.setUrl(m_url);
xhr.setApi(m_api);
xhr.get();
QObject::connect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(store(QByteArray,QString)));
QObject::connect(&xhr,SIGNAL(error(QString,QString,QString,int)),this,SLOT(showError(QString,QString,QString,int)));
QObject::connect(&xhr, SIGNAL(downloaded(QString, QString, QString, int)), this, SLOT(updateImageLocation(QString,QString, QString, int)));
}
void UPDATENEWS::events()
{ m_api="/api/friendica/events";
xhr.clearParams();
xhr.setUrl(m_url);
xhr.setApi(m_api);
QSqlQuery query("SELECT id FROM events WHERE username='"+ username +"' ORDER BY id DESC LIMIT 1",m_db);
if (query.isActive() && query.isSelect()){
if (query.first()){
QString lastid=query.value(0).toString();
xhr.setParam("since_id",lastid);
}
}
xhr.setParam("count","30");
xhr.get();
QObject::disconnect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(store(QByteArray,QString)));
QObject::connect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(storeEvents(QByteArray,QString)));
QObject::connect(&xhr,SIGNAL(error(QString,QString,QString,int)),this,SLOT(showError(QString,QString,QString,int)));
}
void UPDATENEWS::friendrequests()
{ m_api="/api/v1/follow_requests";
xhr.clearParams();
xhr.setUrl(m_url);
xhr.setApi(m_api);
xhr.get();
QObject::disconnect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(store(QByteArray,QString)));
QObject::connect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(storeFriendrequests(QByteArray,QString)));
QObject::connect(&xhr,SIGNAL(error(QString,QString,QString,int)),this,SLOT(showError(QString,QString,QString,int)));
QObject::connect(&xhr, SIGNAL(downloaded(QString, QString, QString, int)), this, SLOT(updateImageLocation(QString,QString, QString, int)));
}
void UPDATENEWS::store(QByteArray serverreply,QString apiname)
{ if (apiname!=m_api || xhr.downloadtype()!=""){} else {
QJsonDocument news;
QJsonParseError jsonerror;
news=QJsonDocument::fromJson(serverreply,&jsonerror);
if (news.isArray()){
for (int i=0; i < news.array().count();i++){
QJsonValue newsitem=news[i];
try{
if (apiname=="/api/friendica/notification"){
QSqlQuery testquery("SELECT status_id FROM news WHERE status_id=" + QString::number(newsitem["id"].toInt()) + " AND messagetype=2 AND username='"+ username +"'",m_db);
if (testquery.first()) {continue;}
}
QSqlQuery query(m_db);
query.prepare("INSERT INTO news (username,messagetype,text,created_at,in_reply_to_status_id,source,status_id,in_reply_to_user_id,"
"geo,favorited,uid,statusnet_html,statusnet_conversation_id,friendica_activities,friendica_activities_self,attachments,friendica_owner) "
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
query.bindValue(0,username);
query.bindValue(1,"0");
query.bindValue(2, newsitem["text"].toString().toUtf8().toBase64());
QString sourcedate=newsitem["created_at"].toString();
QString formateddate=sourcedate.mid(0,3)+", "+sourcedate.mid(8,3)+sourcedate.mid(4,3)+sourcedate.mid(25,5)+sourcedate.mid(10,15);
query.bindValue(3,QDateTime::fromString(formateddate,Qt::RFC2822Date).toMSecsSinceEpoch() );
if(newsitem["in_reply_to_status_id"]!=QJsonValue::Null){query.bindValue(4, newsitem["in_reply_to_status_id"].toInt());}
query.bindValue(5,newsitem["source"]);
query.bindValue(6,newsitem["id"].toInt());
if(newsitem["in_reply_to_user_id"]!=QJsonValue::Null){ query.bindValue(7,newsitem["in_reply_to_user_id"].toInt());}
QJsonObject geo;
geo["external_url"]=newsitem["external_url"];
query.bindValue(8,QJsonDocument(geo).toJson(QJsonDocument::Compact).toStdString().c_str());
//query.bindValue(8,newsitem["geo"]);
query.bindValue( 9, newsitem["favorited"].toInt());
query.bindValue(10, newsitem["user"]["id"].toInt());
//if (newsitem["friendica_title"]!="") {
// QString friendicaHtml="<b>" + newsitem["friendica_title"].toString() +"</b><br><br>"+newsitem["friendica_html"].toString();
// query.bindValue(11, friendicaHtml.toUtf8().toBase64());}
//else{
query.bindValue(11, newsitem["friendica_html"].toString().toUtf8().toBase64());//}
if (newsitem["statusnet_conversation_id"].isDouble()){
query.bindValue(12, newsitem["statusnet_conversation_id"].toInt());
}else{query.bindValue(12, newsitem["statusnet_conversation_id"].toString());}
QJsonArray likeArray;QJsonArray dislikeArray;QJsonArray attendyesArray;QJsonArray attendnoArray;QJsonArray attendmaybeArray;
if (newsitem.toObject().contains("friendica_activities")){
for (int a=0; a < newsitem["friendica_activities"]["like"].toArray().count();a++){
likeArray.append(newsitem["friendica_activities"]["like"][a]["url"].toString());
}
for (int b=0; b < newsitem["friendica_activities"]["dislike"].toArray().count();b++){
dislikeArray.append(newsitem["friendica_activities"]["dislike"][b]["url"].toString());
}
for (int c=0; c < newsitem["friendica_activities"]["attendyes"].toArray().count();c++){
attendyesArray.append(newsitem["friendica_activities"]["attendyes"][c]["url"].toString());
}
for (int d=0; d < newsitem["friendica_activities"]["attendno"].toArray().count();d++){
attendnoArray.append(newsitem["friendica_activities"]["attendno"][d]["url"].toString());
}
for (int e = 0; e < newsitem["friendica_activities"]["attendmaybe"].toArray().count();e++){
attendmaybeArray.append(newsitem["friendica_activities"]["attendmaybe"][e]["url"].toString());
}
}
QJsonArray friendica_activities; friendica_activities={likeArray,dislikeArray,attendyesArray,attendnoArray,attendmaybeArray};
QJsonDocument activities; activities.setArray(friendica_activities);
query.bindValue(13,activities.toJson(QJsonDocument::Compact).toBase64());
query.bindValue(14,"[]");
if (newsitem["attachments"]!=QJsonValue::Undefined){
query.bindValue(15, QJsonDocument(newsitem["attachments"].toArray()).toJson(QJsonDocument::Compact).toBase64());
}else {
query.bindValue(15, "");
}
if (newsitem["friendica_author"]!=QJsonValue::Undefined){
query.bindValue(16, newsitem["friendica_author"]["url"]);
}else {
query.bindValue(16, newsitem["user"]["url"]);
}
if (apiname=="/api/statuses/replies"){
query.bindValue(1,"3");
}
if (apiname == "/api/direct_messages/all"){
query.bindValue(1,"1");
query.bindValue(5,"Friendica");
query.bindValue(6,newsitem["id"].toInt());
if(newsitem["recipient"]["id"]!=QJsonValue::Null){ query.bindValue(7,newsitem["recipient"]["id"].toInt());}
query.bindValue(10, newsitem["sender_id"].toInt());
query.bindValue(11, newsitem["text"].toString().toUtf8().toBase64());
if(newsitem["friendica_parent_uri"]!=QJsonValue::Null){ query.bindValue(12,newsitem["friendica_parent_uri"]);}
query.bindValue(16, newsitem["sender"]["url"]);
}
if (apiname == "/api/friendica/notification"){
query.bindValue(1,"2");
query.bindValue(3,QDateTime::fromString(newsitem["date"].toString(),"yyyy-MM-dd hh:mm:ss").toMSecsSinceEpoch());
query.bindValue(5,"Friendica");
QJsonObject cleancontact= findNotificationContact(newsitem["url"].toString());
query.bindValue(10, cleancontact["id"].toInt());
query.bindValue(11, newsitem["msg_html"].toString().toUtf8().toBase64());
if(newsitem["parent"]!=QJsonValue::Null){ query.bindValue(12,newsitem["parent"]);}
query.bindValue(16, newsitem["url"]);
}
if(!(query.exec())) {qDebug()<< "store news " << query.lastError();}
// notifications
if (apiname=="/api/statuses/friends_timeline"){
if(notifylist.contains("notify_Timeline")){
alarm.notify("Home: "+ newsitem["user"]["name"].toString(),newsitem["text"].toString(),0);
}
}
if (apiname=="/api/statuses/replies"){
if(notifylist.contains("notify_Replies")){
alarm.notify("Replies: "+newsitem["user"]["name"].toString(),newsitem["text"].toString(),1);
}
}
if (apiname=="/api/direct_messages/all"){
if(notifylist.contains("notify_DirectMessages")){
alarm.notify("DirectMessage: "+newsitem["sender"]["name"].toString(),newsitem["text"].toString(),2);
}
}
if (apiname=="/api/friendica/notification"){
if(notifylist.contains("notify_Notifications")){
alarm.notify("Notification: "+newsitem["name"].toString(),newsitem["text"].toString(),3);
}
}
}catch(...){
//qDebug() << "Friendiqasync Error inserting news" << newsitem["text"].toString() << " " << newsitem.toString();
}
}
QList<QJsonValue> newcontacts=findNewContacts(news);
emit this->success(m_api);
if (newcontacts.size()>0){
updateContacts(newcontacts);
startImagedownload("contactlist");
} else {
if((m_updateInterval!=0) && m_syncAll){
syncindex+=1;
startsync();
}
}
}
else {
qDebug()<< m_api <<username << "Friendiqa updatenews error " << serverreply ;
//emit this->error(m_api,QTextCodec::codecForName("utf-8")->toUnicode(serverreply));
emit this->error(m_api,QString(serverreply));
if(m_syncAll){
syncindex+=1;
startsync();
}
}
}
}
void UPDATENEWS::updateImageLocation(QString downloadtype,QString imageurl, QString filename, int index){
if (downloadtype=="contactlist"){
QSqlQuery testquery("SELECT profile_image FROM contacts WHERE profile_image_url ='"+imageurl+ "' AND username = '" +username+"'",m_db);
testquery.first();
QSqlQuery query("UPDATE contacts SET profile_image='"+ filename +"' WHERE profile_image_url ='"+imageurl+ "' AND username = '" +username+"'",m_db);
if(!(query.exec())) {qDebug()<< "updateImagelocation " << query.lastError();}
if (index==(newcontactnames.length()-1)){
newcontactnames.clear();
newcontactimagelinks.clear();
if((m_updateInterval!=0) && m_syncAll){
syncindex+=1;
startsync();
}
}
}
else if (downloadtype=="friendrequests"){
QSqlQuery testquery("SELECT avatar_static FROM friendshiprequests WHERE avatar ='"+imageurl+ "' AND username = '" +username+"'",m_db);
testquery.first();
QSqlQuery query("UPDATE friendshiprequests SET avatar_static='"+ filename +"' WHERE avatar ='"+imageurl+ "' AND username = '" +username+"'",m_db);
if(!(query.exec())) {qDebug()<< "update friendrequests Imagelocation " << query.lastError();}
if (index==(newcontactnames.length()-1)){
newcontactnames.clear();
newcontactimagelinks.clear();
if((m_updateInterval!=0) && m_syncAll){
syncindex+=1;
startsync();
}
}
}
}
QJsonObject UPDATENEWS::findNotificationContact(QString contacturl){
QSqlQuery query("SELECT id,url FROM contacts WHERE url='"+contacturl+"' AND username='"+ username+"'",m_db);
query.first();
QJsonObject contact{
{"id", query.value(0).toInt()},
{"url", query.value(1).toString()}
};
return contact;
}
QList <QJsonValue> UPDATENEWS::findNewContacts(QJsonDocument news){
QSqlQuery query("SELECT profile_image_url FROM contacts",m_db);
QList<QString> imageurls;
while (query.next()){
imageurls.append(query.value(0).toString());
}
QList<QJsonValue> newcontacts;
for (int i=0; i<news.array().count();i++){
//main contacts
if (news[i].toObject().contains("sender") ){
if(imageurls.contains(news[i]["sender"]["profile_image_url"].toString().section('?',0,0)) || newcontactimagelinks.contains(news[i]["sender"]["profile_image_url"].toString().section('?',0,0))){
}
else{
newcontacts.append(news[i]["sender"]);
newcontactimagelinks.append(news[i]["sender"]["profile_image_url"].toString().section('?',0,0));
newcontactnames.append(news[i]["sender"]["screen_name"].toString());
}
}
if (news[i].toObject().contains("user") ){
if(imageurls.contains(news[i]["user"]["profile_image_url"].toString().section('?',0,0)) || newcontactimagelinks.contains(news[i]["user"]["profile_image_url"].toString().section('?',0,0))){
}
else{
newcontacts.append(news[i]["user"]);
newcontactimagelinks.append(news[i]["user"]["profile_image_url"].toString().section('?',0,0));
newcontactnames.append(news[i]["user"]["screen_name"].toString());
}
}
//like/dislike contacts
if (news[i].toObject().contains("friendica_activities") ){
for (int a=0; a < news[i]["friendica_activities"]["like"].toArray().count();a++){
if(imageurls.contains(news[i]["friendica_activities"]["like"][a]["profile_image_url"].toString().section('?',0,0)) || newcontactimagelinks.contains(news[i]["friendica_activities"]["like"][a]["profile_image_url"].toString().section('?',0,0))){
}
else{
newcontacts.append(news[i]["friendica_activities"]["like"][a]);
newcontactimagelinks.append(news[i]["friendica_activities"]["like"][a]["profile_image_url"].toString().section('?',0,0));
newcontactnames.append(news[i]["friendica_activities"][a]["screen_name"].toString());
}
}
for (int b=0; b < news[i]["friendica_activities"]["dislike"].toArray().count();b++){
if(imageurls.contains(news[i]["friendica_activities"]["dislike"][b]["profile_image_url"].toString().section('?',0,0)) || newcontactimagelinks.contains(news[i]["friendica_activities"]["dislike"][b]["profile_image_url"].toString().section('?',0,0))){
}
else{
newcontacts.append(news[i]["friendica_activities"]["dislike"][b]);
newcontactimagelinks.append(news[i]["friendica_activities"]["dislike"][b]["profile_image_url"].toString().section('?',0,0));
newcontactnames.append(news[i]["friendica_activities"][b]["screen_name"].toString());
}
}
}
//owner contacts
if (news[i].toObject().contains("friendica_author") ){
if(imageurls.contains(news[i]["friendica_author"]["profile_image_url"].toString().section('?',0,0)) || newcontactimagelinks.contains(news[i]["friendica_owner"]["profile_image_url"].toString().section('?',0,0))){
}
else{
newcontacts.append(news[i]["friendica_author"]);
newcontactimagelinks.append(news[i]["friendica_author"]["profile_image_url"].toString().section('?',0,0));
newcontactnames.append(news[i]["friendica_author"]["screen_name"].toString());
}
}
}
return newcontacts;
}
void UPDATENEWS::updateContacts(QList<QJsonValue> contacts){
qint64 currentTime =QDateTime::currentMSecsSinceEpoch();
for (int i=0; i < contacts.count();i++){
QJsonValue contact=contacts[i];
QSqlQuery query(m_db);
try{
QSqlQuery testquery("SELECT url FROM contacts WHERE username='"+ username +"' AND url='" + contact["url"].toString() +"'",m_db);
if (testquery.first()){
query.prepare("UPDATE contacts SET id=?, name=?, screen_name=?, location=?,imageAge=?,"
"profile_image_url=?, description=?, protected=?, followers_count=?,"
"friends_count=?, created_at=?, favourites_count=?, utc_offset=?, time_zone=?, statuses_count=?,"
"following=?, verified=?, statusnet_blocking=?, notifications=?, statusnet_profile_url=?, cid=?, network=?, timestamp=? "
" WHERE username='"+ username +"' AND url='" + contact["url"].toString() +"'");
query.bindValue(0, contact["id"].toInt());
query.bindValue(1, contact["name"].toString().toUtf8().toBase64());
query.bindValue(2, contact["screen_name"]);
query.bindValue(3, contact["location"]);
query.bindValue(4, currentTime);
query.bindValue(5, contact["profile_image_url"].toString().section('?',0,0));
if(contact["description"].isNull() ){query.bindValue(6,"");}else{query.bindValue(6, contact["description"].toString().toUtf8().toBase64());}
query.bindValue(7,contact["protected"].toBool());
query.bindValue(8,contact["followers_count"].toInt());
query.bindValue(9,contact["friends_count"].toInt());
QString sourcedate=contact["created_at"].toString();
QString formateddate=sourcedate.mid(0,3)+", "+sourcedate.mid(8,3)+sourcedate.mid(4,3)+sourcedate.mid(25,5)+sourcedate.mid(10,15);
query.bindValue(10,QDateTime::fromString(formateddate,Qt::RFC2822Date).toMSecsSinceEpoch() );
query.bindValue(11,contact["favorites_count"].toInt());
query.bindValue(12,contact["utc_offset"].toInt());
query.bindValue(13,contact["time_zone"].toString());
query.bindValue(14,contact["statuses_count"].toInt());
query.bindValue(15,contact["following"].toBool());
query.bindValue(16,contact["verfied"].toBool());
query.bindValue(17,contact["statusnet_blocking"].toBool());
query.bindValue(18,contact["notifications"].toBool());
query.bindValue(19,contact["statusnet_profile_url"]);
query.bindValue(20,contact["cid"].toInt());
query.bindValue(21,contact["network"]);
qint64 timestamp=0;
QString timestamphelper=contact["profile_image_url"].toString();
try {timestamp=timestamphelper.mid(timestamphelper.indexOf("?ts")+4,timestamphelper.length()).toUInt();} catch(...){}
query.bindValue(22,timestamp);
}
else{
query.prepare("INSERT INTO contacts (username, id, name, screen_name, location,imageAge,"
"profile_image_url, description, profile_image, url, protected, followers_count,"
"friends_count, created_at, favourites_count, utc_offset, time_zone, statuses_count,"
"following, verified, statusnet_blocking, notifications, statusnet_profile_url, cid, network, isFriend, timestamp)"
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
query.bindValue(0,username);
query.bindValue(1, contact["id"].toInt());
query.bindValue(2, contact["name"].toString().toUtf8().toBase64());
query.bindValue(3, contact["screen_name"]);
query.bindValue(4, contact["location"]);
query.bindValue(5, currentTime);
query.bindValue(6, contact["profile_image_url"].toString().section('?',0,0));
if(contact["description"].isNull() ){query.bindValue(7,"");}else{query.bindValue(7, contact["description"].toString().toUtf8().toBase64());}
query.bindValue(8,"none");
query.bindValue(9, contact["url"].toString());
query.bindValue(10,contact["protected"].toBool());
query.bindValue(11,contact["followers_count"].toInt());
query.bindValue(12,contact["friends_count"].toInt());
QString sourcedate=contact["created_at"].toString();
QString formateddate=sourcedate.mid(0,3)+", "+sourcedate.mid(8,3)+sourcedate.mid(4,3)+sourcedate.mid(25,5)+sourcedate.mid(10,15);
query.bindValue(13,QDateTime::fromString(formateddate,Qt::RFC2822Date).toMSecsSinceEpoch() );
query.bindValue(14,contact["favorites_count"].toInt());
query.bindValue(15,contact["utc_offset"].toInt());
query.bindValue(16,contact["time_zone"].toString());
query.bindValue(17,contact["statuses_count"].toInt());
query.bindValue(18,contact["following"].toBool());
query.bindValue(19,contact["verified"].toBool());
query.bindValue(20,contact["statusnet_blocking"].toBool());
query.bindValue(21,contact["notifications"].toBool());
query.bindValue(22,contact["statusnet_profile_url"]);
query.bindValue(23,contact["cid"].toInt());
query.bindValue(24,contact["network"]);
query.bindValue(25, 0);
qint64 timestamp=0;
QString timestamphelper=contact["profile_image_url"].toString();
try {timestamp=timestamphelper.mid(timestamphelper.indexOf("?ts")+4,timestamphelper.length()).toUInt();} catch(...){}
query.bindValue(26,timestamp);
}
if(!(query.exec())) {qDebug()<< "updatecontacts " << query.lastError();}
} catch(...){
qDebug() << "Friendiqasync Error inserting contact" << contact["screen_name"] << " " << contact.toString();
}
}
}
void UPDATENEWS::storeFriendrequests(QByteArray serverreply,QString apiname)
{ if (apiname!=m_api || xhr.downloadtype()!=""){} else {
QJsonDocument friendrequests;
QJsonParseError jsonerror;
friendrequests=QJsonDocument::fromJson(serverreply,&jsonerror);
if (friendrequests.isArray()){
QSqlQuery imagequery("SELECT avatar FROM friendshiprequests",m_db);
QList<QString> imageurls;
while (imagequery.next()){
imageurls.append(imagequery.value(0).toString());
}
for (int i=0; i < friendrequests.array().count();i++){
QJsonValue friendrequestitem=friendrequests[i];
try{
QSqlQuery testquery("SELECT url FROM friendshiprequests WHERE username='"+ username +"' AND url='" + friendrequestitem["url"].toString() +"'",m_db);
QSqlQuery query(m_db);
if (testquery.first()){
query.prepare("UPDATE friendshiprequests SET id=?, usernamef=?, acct=?, display_name=?,locked=?,"
"bot=?, discoverable=?, groupf=?, created_at=?,"
"note=?, avatar=?, header=?, header_static=?, followers_count=?,"
"following_count=?, statuses_count=?, last_status_at=?, emojis=?, fields=? "
" WHERE username='"+ username +"' AND url='" + friendrequestitem["url"].toString() +"'");
query.bindValue(0, friendrequestitem["id"].toInt());
query.bindValue(1, friendrequestitem["username"]);
query.bindValue(2, friendrequestitem["acct"]);
query.bindValue(3, friendrequestitem["display_name"].toString().toUtf8().toBase64());
query.bindValue(4, friendrequestitem["locked"].toBool());
query.bindValue(5, friendrequestitem["bot"].toBool());
query.bindValue(6, friendrequestitem["discoverable"].toBool());
query.bindValue(7, friendrequestitem["group"].toBool());
query.bindValue(8, QDateTime::fromString(friendrequestitem["created_at"].toString(),Qt::ISODate).toMSecsSinceEpoch() );
query.bindValue(9, friendrequestitem["note"].toString().toUtf8().toBase64());
query.bindValue(10, friendrequestitem["avatar"]);
query.bindValue(11, friendrequestitem["header"]);
query.bindValue(12, friendrequestitem["header_static"]);
query.bindValue(13, friendrequestitem["followers_count"].toInt());
query.bindValue(14, friendrequestitem["following_count"].toInt());
query.bindValue(15, friendrequestitem["statuses_count"].toInt());
query.bindValue(16, QDateTime::fromString(friendrequestitem["last_status_at"].toString(),Qt::ISODate).toMSecsSinceEpoch() );
query.bindValue(17, friendrequestitem["emojis"].toString().toUtf8().toBase64());
query.bindValue(18, friendrequestitem["fields"].toString().toUtf8().toBase64());
}
else{
query.prepare("INSERT INTO friendshiprequests (username, id, usernamef, acct, display_name, locked,"
" created_at, followers_count, following_count, statuses_count, note, url, avatar, avatar_static, "
"header, header_static, emojis, moved, fields, bot, groupf, discoverable, last_status_at) "
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
query.bindValue(0, username);
query.bindValue(1, friendrequestitem["id"].toInt());
query.bindValue(2, friendrequestitem["username"].toString());
query.bindValue(3, friendrequestitem["acct"].toString());
query.bindValue(4, friendrequestitem["display_name"].toString().toUtf8().toBase64());
query.bindValue(5, friendrequestitem["locked"].toBool());
query.bindValue(6, QDateTime::fromString(friendrequestitem["created_at"].toString(),Qt::ISODate).toMSecsSinceEpoch());
query.bindValue(7, friendrequestitem["followers_count"].toInt());
query.bindValue(8, friendrequestitem["following_count"].toInt());
query.bindValue(9, friendrequestitem["statuses_count"].toInt());
query.bindValue(10, friendrequestitem["note"].toString().toUtf8().toBase64());
query.bindValue(11, friendrequestitem["url"].toString());
query.bindValue(12, friendrequestitem["avatar"].toString());
query.bindValue(13, "");
query.bindValue(14, friendrequestitem["header"].toString());
query.bindValue(15, friendrequestitem["header_static"].toString());
query.bindValue(16, friendrequestitem["emojis"].toString().toUtf8().toBase64());
query.bindValue(17, "false");
query.bindValue(18, friendrequestitem["fields"].toString().toUtf8().toBase64());
query.bindValue(19, friendrequestitem["bot"].toBool());
query.bindValue(20, friendrequestitem["group"].toBool());
query.bindValue(21, friendrequestitem["discoverable"].toBool());
query.bindValue(22, QDateTime::fromString(friendrequestitem["last_status_at"].toString(),Qt::ISODate).toMSecsSinceEpoch());
if(notifylist.contains("notify_FriendRequests")){
alarm.notify("Friend Request: "+ friendrequestitem["acct"].toString(),friendrequestitem["note"].toString(),0);
}
}
if(!(query.exec())) {qDebug()<< "friendrequestitem error " << query.lastError() << " " << query.lastQuery();}
} catch(...){
qDebug() << "Friendiqasync Error inserting friendrequestitem" << friendrequestitem["acct"] << " " ;
}
if(imageurls.contains(friendrequestitem["avatar"].toString() )){
}
else{
newcontactimagelinks.append(friendrequestitem["avatar"].toString());
newcontactnames.append(friendrequestitem["username"].toString());
}
}
if (newcontactimagelinks.length()>0){
startImagedownload("friendrequests");
}else{
if((m_updateInterval!=0) && m_syncAll){
syncindex+=1;
startsync();
}
}
}
}
QObject::disconnect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(storeFriendrequests(QByteArray,QString)));
}
void UPDATENEWS::storeEvents(QByteArray serverreply,QString apiname)
{ if (apiname!=m_api || xhr.downloadtype()!=""){} else {
QJsonDocument events;
QJsonParseError jsonerror;
events=QJsonDocument::fromJson(serverreply,&jsonerror);
if (events.isArray()){
for (int i=0; i < events.array().count();i++){
QJsonValue eventitem=events[i];
try{
QSqlQuery query(m_db);
query.prepare("INSERT INTO events (username,id,cid,start,end,title,uri,desc,location,type,nofinish,adjust,ignore,permissions) " "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
query.bindValue(0,username);
query.bindValue(1,eventitem["id"].toInt());
query.bindValue(2,eventitem["cid"].toInt());
QString sourcedateStart="";
if (eventitem["start_time"].toString()!=""){
sourcedateStart=eventitem["start_time"].toString();}else{
sourcedateStart=eventitem["startTime"].toString();
};
query.bindValue(3,QDateTime::fromString(sourcedateStart,Qt::ISODate).toMSecsSinceEpoch() );
QString sourcedateEnd="";
if (eventitem["end_time"].toString()!=""){
sourcedateEnd=eventitem["end_time"].toString();} else{
sourcedateEnd=eventitem["endTime"].toString();
}
if(QDateTime::fromString(sourcedateEnd,Qt::ISODate).toMSecsSinceEpoch()>QDateTime::fromString(sourcedateStart,Qt::ISODate).toMSecsSinceEpoch()){
//check if end is later than start
query.bindValue(4,QDateTime::fromString(sourcedateEnd,Qt::ISODate).toMSecsSinceEpoch() );
} else {
query.bindValue(4,0 );
}
query.bindValue(4,QDateTime::fromString(sourcedateEnd,Qt::ISODate).toMSecsSinceEpoch() );
query.bindValue(5,eventitem["name"].toString());
query.bindValue(6,eventitem["uri"].toString());
query.bindValue(7,eventitem["desc"].toString().toUtf8().toBase64());
query.bindValue(8,eventitem["place"].toString());
query.bindValue(9,eventitem["type"].toString());
query.bindValue(10,eventitem["nofinish"].toInt());
query.bindValue(11,eventitem["adjust"].toInt());
query.bindValue(12,eventitem["ignore"].toInt());
QJsonArray permissions; permissions={eventitem["allow_cid"].toString().replace("<","[").replace(">","]"),eventitem["allow_gid"].toString().replace("<","[").replace(">","]"),eventitem["deny_cid"].toString().replace("<","[").replace(">","]"),eventitem["deny_gid"].toString().replace("<","[").replace(">","]")};
QJsonDocument permissionDocument; permissionDocument.setArray(permissions);
query.bindValue(13,permissionDocument.toJson(QJsonDocument::Compact));
if(!(query.exec())) {qDebug()<< "store events " << query.lastError();}
} catch(...){
qDebug() << "Friendiqasync Error event" << eventitem["name"];
}
}
emit this->success(m_api);
}
}
if(notifylist.contains("notify_Events")){
QSqlQuery eventnotifyquery("SELECT start,title FROM events WHERE (start BETWEEN " + QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + " AND "+QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()+(m_updateInterval*119*1000))+") AND username='"+ username +"'",m_db);
while (eventnotifyquery.next()) {
alarm.notify("Event: "+ QDateTime::fromMSecsSinceEpoch(eventnotifyquery.value(0).toLongLong()).toString("dd.MM.yyyy hh:mm"),eventnotifyquery.value(1).toString(),1);
}
}
if((m_updateInterval!=0) && m_syncAll){
syncindex+=1;
startsync();
}
QObject::disconnect(&xhr,SIGNAL(success(QByteArray,QString)),this,SLOT(storeEvents(QByteArray,QString)));
}
void UPDATENEWS::startImagedownload(QString downloadtype)
{
xhr.setDownloadtype(downloadtype );
xhr.setFilelist(newcontactimagelinks);
xhr.setContactlist(newcontactnames);
xhr.getlist();
}
void UPDATENEWS::showError(QString data, QString url,QString api, int code )
{
emit this->error(api,data);
if (api!=m_api || xhr.downloadtype()!=""){} else{
if((m_updateInterval!=0) && m_syncAll){
syncindex+=1;
startsync();
}
}
}

99
src/common/updatenews.h Normal file
View file

@ -0,0 +1,99 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef UPDATENEWS_H
#define UPDATENEWS_H
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QSqlDatabase>
#include "xhr.h"
#include "alarm.h"
//#include "AndroidNative/systemdispatcher.h"
class UPDATENEWS : public QObject
{
Q_OBJECT
public:
static UPDATENEWS *instance();
explicit UPDATENEWS(QObject *parent = 0);
signals:
void success(QString api);
void error(QString api, QString content);
void quitapp();
public slots:
void setSyncAll(bool syncAll);
void setDatabase();
int getStyle();
void login();
void timeline();
void replies();
void startsync();
void directmessages();
void notifications();
void friendrequests();
void events();
void startImagedownload(QString downloadtype);
void updateImageLocation(QString downloadtype,QString imageurl, QString filename, int index);
void store(QByteArray serverreply,QString apiname);
void storeFriendrequests(QByteArray serverreply,QString apiname);
void storeEvents(QByteArray serverreply,QString apiname);
void showError(QString data, QString url,QString api, int code);
QJsonArray getAccounts(QString filtername="",QString filtervalue="");
private:
QString m_api;
QString m_url;
QString username;
bool m_syncAll;
int syncindex;
int usernameindex;
int usernamelength;
QSqlDatabase m_db;
QList<QString> synclist;
QList <QString> notifylist;
QList<QJsonValue> findNewContacts(QJsonDocument news);
QJsonObject findNotificationContact(QString imagelink);
int m_updateInterval;
void updateContacts(QList<QJsonValue> contacts);
XHR xhr;
ALARM alarm;
QList<QString> newcontactimagelinks;
QList<QString> newcontactnames;
};
#endif // UPDATENEWS_H

View file

@ -0,0 +1,143 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "uploadableimage.h"
#include <QBuffer>
#include <QDebug>
#include <QFileInfo>
#include <QUrl>
#include <QTransform>
void UploadableImage::setAngle(const int &b) {
if (b != m_angle) {
m_angle = b;
//qDebug() << "UploadableImage::setAngle : " << m_angle;
if (m_angle==0) {
emit angleChanged();
return;
}
}
}
void UploadableImage::setSource(const QString &a) {
if (a != m_source) {
m_source = a;
//m_base64 = "";
m_mimetype = "";
m_filename = "";
//qDebug() << "UploadableImage::setSource : " << m_source;
if (m_source=="") {
emit sourceChanged();
//emit base64Changed();
emit mimetypeChanged();
emit filenameChanged();
return;
}
QImage fullimage = QImage(QUrl(m_source).toLocalFile());
if (m_angle!=0){
QTransform transform;
transform.rotate(qreal(m_angle));
fullimage=fullimage.transformed(transform);
}
if (fullimage.width() > 800 || fullimage.height() > 800) {
if (fullimage.width() > fullimage.height()) {
m_image = fullimage.scaledToWidth(800);
} else {
m_image = fullimage.scaledToHeight(800);
}
} else {
m_image = fullimage;
}
//qDebug() << "UploadableImage::setSource : " << m_image.width() << "x" << m_image.height();
emit sourceChanged();
QFileInfo fi(m_source);
m_filename = fi.fileName();
emit filenameChanged();
QString filetype = fi.suffix().toUpper();
if (filetype!="PNG" && filetype!="JPG") {
filetype = "JPG";
}
//qDebug() << "UploadableImage::setSource : " << "Saving as " << filetype;
m_mimetype = "image/"+filetype.toLower();
emit mimetypeChanged();
/*
QByteArray byteArray;
QBuffer buffer(&byteArray);
m_image.save(&buffer, filetype.toLatin1().constData());
QString b64 = QString::fromLatin1(byteArray.toBase64().data());
for(int k=0; k<b64.length(); k+=76) {
m_base64 += b64.mid(k,76) + "\n";
}
m_base64 = m_base64.trimmed();
emit base64Changed();
*/
}
}
QString UploadableImage::source() const {
return m_source;
}
int UploadableImage::angle() const{
return m_angle;
}
//QString UploadableImage::base64() const {
// return m_base64;
//}
QString UploadableImage::filename() const {
return m_filename;
}
QString UploadableImage::mimetype() const {
return m_mimetype;
}
QByteArray UploadableImage::bytes() {
QByteArray byteArray;
QBuffer buffer(&byteArray);
m_image.save(&buffer, "PNG");
return byteArray;
}

View file

@ -0,0 +1,75 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef UPLOADABLEIMAGE_H
#define UPLOADABLEIMAGE_H
#include <QObject>
#include <QQuickItem>
#include <QImage>
class UploadableImage : public QObject
{
Q_OBJECT
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(int angle READ angle WRITE setAngle NOTIFY angleChanged)
//Q_PROPERTY(QString base64 READ base64 NOTIFY base64Changed)
Q_PROPERTY(QString filename READ filename NOTIFY filenameChanged)
Q_PROPERTY(QString mimetype READ mimetype NOTIFY mimetypeChanged)
Q_PROPERTY(QByteArray bytes READ bytes)
public:
void setSource(const QString &a);
void setAngle(const int &b);
QString source() const;
int angle() const;
//QString base64() const;
QString filename() const;
QString mimetype() const;
QByteArray bytes();
signals:
void sourceChanged();
void angleChanged();
//void base64Changed();
void filenameChanged();
void mimetypeChanged();
private:
QString m_source;
QImage m_image;
int m_angle;
//QString m_base64;
QString m_filename;
QString m_mimetype;
};
#endif // UPLOADABLEIMAGE_H

445
src/common/xhr.cpp Normal file
View file

@ -0,0 +1,445 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "xhr.h"
#include <QHttpPart>
//#include <QTextCodec>
#include <QUrlQuery>
#include <QList>
#include <QDataStream>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include "uploadableimage.h"
XHR *XHR::instance()
{
static XHR xhr;
return &xhr;
}
XHR::XHR(QObject *parent) : QObject(parent)
{
//request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
}
void XHR::setUrl(QString url)
{
if (url!=m_url) {
m_url = url;
emit urlChanged();
}
}
void XHR::setApi(QString api)
{
if (api!=m_api) {
m_api = api;
emit apiChanged();
}
}
void XHR::setLogin(QString login)
{
if (login!=m_login) {
m_login = login;
m_token="";
emit loginChanged();
}
}
void XHR::setAccount(QVariantMap account)
{
clearParams();
if (account["password"].toString() !=""){
setLogin(account["username"].toString()+":"+QByteArray::fromBase64(account["password"].toByteArray()));
}
else if (account["token"].toString() !="" && !account["token"].isNull()){
setToken(account["token"].toString());
}
setUrl(account["server"].toString());
setImagedir(account["imagestore"].toString());
m_account = account;
emit accountChanged();
}
void XHR::setToken(QString token)
{
if (token!=m_token) {
m_token = token;
m_login="";
emit tokenChanged();
}
}
void XHR::setFilename(QString filename)
{
if (filename!=m_filename) {
m_filename = filename;
emit filenameChanged();
}
}
void XHR::setContactlist(QList<QString> contactlist)
{
if (contactlist!=m_contactlist) {
m_contactlist = contactlist;
emit contactlistChanged();
}
}
void XHR::setFilelist(QList<QString> filelist)
{
if (filelist!=m_filelist) {
m_filelist = filelist;
emit filelistChanged();
}
}
void XHR::setImagedir(QString imagedir)
{
if (imagedir!=m_imagedir) {
m_imagedir = imagedir;
emit imagedirChanged();
}
}
void XHR::setDownloadtype(QString downloadtype)
{
if (downloadtype!=m_downloadtype) {
m_downloadtype = downloadtype;
emit downloadtypeChanged();
}
}
QString XHR::url() const
{
return m_url;
}
QString XHR::api() const
{
return m_api;
}
QString XHR::login() const
{
return m_login;
}
QString XHR::token() const
{
return m_token;
}
QVariantMap XHR::account() const
{
return m_account;
}
QString XHR::filename() const
{
return m_filename;
}
QList<QString> XHR::contactlist() const
{
return m_contactlist;
}
QList<QString> XHR::filelist() const
{
return m_filelist;
}
QString XHR::imagedir() const
{
return m_imagedir;
}
QString XHR::downloadtype() const
{
return m_downloadtype;
}
void XHR::setParam(QString name, QString value)
{
params.insert(name, value);
}
void XHR::setImageFileParam(QString name, QString url)
{
files.insert(name, url);
}
void XHR::clearParams()
{
files.clear();
params.clear();
}
void XHR::download()
{
QUrl requrl(m_url);
QNetworkRequest request;
if(m_downloadtype=="picturelist"){
if(m_login!=""){
QByteArray loginData = m_login.toLocal8Bit().toBase64();
QString headerData = "Basic " + loginData;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
} else{
QString headerData = "Bearer " + m_token;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
}
}
request.setUrl(requrl);
reply = manager.get(request);
reply->ignoreSslErrors();
connect(reply, &QNetworkReply::readyRead,this, &XHR::onReadyRead);
//connect(reply,SIGNAL(downloadProgress(qint64,qint64)), this,SLOT(updateDownloadProgress(qint64,qint64)));
connect(reply, &QNetworkReply::finished,this, &XHR::onRequestFinished);
connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError);
connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError)));
}
void XHR::get()
{
QUrlQuery query;
//qDebug() << "get";
QHashIterator<QString, QString> i(params);
while(i.hasNext()) {
i.next();
//qDebug() << i.key() << "value" << i.value();
query.addQueryItem(i.key(), i.value());
}
QUrl requrl(m_url+m_api);
requrl.setQuery(query);
QNetworkRequest request;
if(m_login!=""){
QByteArray loginData = m_login.toLocal8Bit().toBase64();
QString headerData = "Basic " + loginData;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
} else if (m_token!=""){
QString headerData = "Bearer " + m_token;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
}
request.setUrl(requrl);
reply = manager.get(request);
connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess);
//connect(reply,SIGNAL(downloadProgress(qint64,qint64)), this,SLOT(updateDownloadProgress(qint64,qint64)));
connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError)));
connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead);
connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError);
}
void XHR::getlist()
{
if(dlindex < m_filelist.size()) {
QString cleanfilename;
if (m_downloadtype=="contactlist" || m_downloadtype=="friendrequests" ){
cleanfilename = m_contactlist.at(dlindex)+"-"+ m_filelist.at(dlindex).section('/',-1).section('?',0,0);
XHR::setFilename(imagedir()+"contacts/"+cleanfilename);
XHR::setUrl(m_filelist.at(dlindex));}
else {
XHR::setUrl(m_filelist.at(dlindex));}
//qDebug() << "start download" << m_url;
XHR::download();
} else {dlindex=0;m_downloadtype="";m_contactlist.clear();m_filelist.clear();}
}
void XHR::post()
{
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHashIterator<QString, QString> iparams(params);
while(iparams.hasNext()) {
iparams.next();
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + iparams.key() + "\""));
textPart.setBody(iparams.value().toUtf8());
multiPart->append(textPart);
}
UploadableImage uimg;
if (files.contains("media")){
uimg.setAngle(files.value("angle").toInt());
uimg.setSource(files.value("media"));
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(uimg.mimetype()));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"media\"; filename=\""+uimg.filename()+"\""));
imagePart.setBody(uimg.bytes());
multiPart->append(imagePart);
}
QNetworkRequest request;
if(m_login!=""){
QByteArray loginData = m_login.toLocal8Bit().toBase64();
QString headerData = "Basic " + loginData;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
} else if (m_token!=""){
QString headerData = "Bearer " + m_token;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
}
request.setUrl(m_url+m_api);
reply = manager.post(request, multiPart);
qDebug() << "\t request sent";
connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess);
connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError)));
connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead);
connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError);
}
void XHR::postJSON()
{
if (params.contains("JSON")){
QByteArray mJSON=params.value("JSON").toUtf8();
QNetworkRequest request;
if(m_login!=""){
QByteArray loginData = m_login.toLocal8Bit().toBase64();
QString headerData = "Basic " + loginData;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
} else if (m_token!=""){
QString headerData = "Bearer " + m_token;
request.setRawHeader("Authorization", headerData.toLocal8Bit());
}
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json; charset=UTF-8");
request.setUrl(m_url+m_api);
reply = manager.post(request, mJSON);
qDebug() << "\t request sent";
connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError)));
connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead);
connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError);
qDebug() << "\t reply signals connected";
}
}
void XHR::onReplyError(QNetworkReply::NetworkError code)
{
qDebug() << code;
if(downloadtype()!="contactlist"){emit this->error( bufferToString(), m_url,m_api, (int) code);}
buffer.clear();
reply->deleteLater();
if((downloadtype()=="contactlist")||(downloadtype()=="picturelist")){dlindex=dlindex+1;XHR::getlist();}
}
void XHR::onReplySuccess()
{
qDebug() << "!";
QHashIterator<QString, QString> i(params);
// while(i.hasNext()) {
// i.next();
// //qDebug() << i.key()<< " " << i.value();
// }
//emit this->success(buffer, m_api);
emit success(buffer, m_api);
//emit this->error(m_downloadtype,m_url,m_api,1);
buffer.clear();
// reply->deleteLater();
}
void XHR::onRequestFinished()
{
// Save the file here
//qDebug() << "buffer " << buffer;
if (buffer.isNull()){qDebug() << "File empty"<<m_url;
buffer.clear();
emit this->error(m_downloadtype,m_url,m_api,1);
}
else if (m_downloadtype=="picturelist") {
QJsonDocument jsonResponse = QJsonDocument::fromJson(buffer);
QJsonObject jsonObject = jsonResponse.object();
int arraystart=buffer.indexOf("{\"data\":\"")+8;
int arraylength=buffer.indexOf('"',9)-arraystart;
QByteArray b64=buffer.mid(arraystart,arraylength);
QString helpfilename=jsonObject["filename"].toString();
QString helpfile=helpfilename.left(helpfilename.lastIndexOf("."));
QString filesuffix="";
if (jsonObject["type"].toString()=="image/jpeg" || jsonObject["type"].toString()=="image/jpg"){filesuffix=".jpg";}
else if (jsonObject["type"].toString()=="image/png"){filesuffix=".png";}
else {filesuffix="";}
if (helpfilename==""){// check if file has any filename
helpfile=jsonObject["id"].toString();
setFilename(imagedir()+"albums/"+jsonObject["album"].toString()+"/"+jsonObject["id"].toString()+filesuffix);
}
else{setFilename(imagedir()+"albums/"+jsonObject["album"].toString()+"/"+helpfile+filesuffix);}
//qDebug()<<"Filename "<<m_filename;
QFile file(m_filename);
file.open(QIODevice::WriteOnly);
file.write(QByteArray::fromBase64(b64));
buffer.clear();
b64.clear();
file.close();
jsonObject["data"]="";
jsonObject["filename"]=helpfile+filesuffix;
emit this->downloadedjson(m_downloadtype,m_url,m_filename,dlindex,jsonObject);
}
else {
QFile file(m_filename);
file.open(QIODevice::WriteOnly);
file.write(buffer);
buffer.clear();
file.close();
emit this->downloaded(m_downloadtype,m_url,m_filename,dlindex);
//reply->deleteLater();
}
if(downloadtype()=="contactlist" || downloadtype()=="friendrequests" || downloadtype()=="picturelist"){
dlindex=dlindex+1;XHR::getlist();
}
}
void XHR::onReadyRead()
{
qDebug() << ".";
buffer += reply->readAll();
}
//void XHR::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes)
//{
// qDebug() << "Bytes: " << bytesRead<<" / "<<totalBytes;
//}
void XHR::onSSLError(const QList<QSslError> &errors)
{
qDebug() << "XHR::onSSLError :" ;
QListIterator<QSslError> ierrs(errors);
while(ierrs.hasNext()) {
qDebug() << "\t" << ierrs.next().errorString();
}
}
QString XHR::bufferToString()
{
//return QTextCodec::codecForName("utf-8")->toUnicode(buffer);
return QString(buffer);
}

145
src/common/xhr.h Normal file
View file

@ -0,0 +1,145 @@
// This file is part of Friendiqa
// https://github.com/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef XHR_H
#define XHR_H
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QObject>
#include <QJsonObject>
//#include <QNetworkConfiguration>
class XHR : public QObject
{
Q_OBJECT
Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
Q_PROPERTY(QString login READ login WRITE setLogin NOTIFY loginChanged)
Q_PROPERTY(QString token READ token WRITE setToken NOTIFY tokenChanged)
Q_PROPERTY(QVariantMap account READ account WRITE setAccount NOTIFY accountChanged)
Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)
Q_PROPERTY(QString imagedir READ imagedir WRITE setImagedir NOTIFY imagedirChanged)
Q_PROPERTY(QList<QString> contactlist READ contactlist WRITE setContactlist NOTIFY contactlistChanged)
Q_PROPERTY(QList<QString> filelist READ filelist WRITE setFilelist NOTIFY filelistChanged)
Q_PROPERTY(QString downloadtype READ downloadtype WRITE setDownloadtype NOTIFY downloadtypeChanged)
//Q_PROPERTY(QString networktype READ networktype() NOTIFY networktypeChanged)
public:
static XHR *instance();
explicit XHR(QObject *parent = 0);
QString url() const;
QString api() const;
QString login() const;
QString token() const;
QVariantMap account() const;
QString filename() const;
QList<QString> contactlist() const;
QList<QString> filelist() const;
QString imagedir() const;
QString downloadtype() const;
// QString networktype();
signals:
void urlChanged();
void apiChanged();
void loginChanged();
void tokenChanged();
void accountChanged();
void filenameChanged();
void contactlistChanged();
void filelistChanged();
void imagedirChanged();
void downloadtypeChanged();
void networktypeChanged();
void downloaded(QString type, QString url, QString filename, int i);
void downloadedjson(QString type, QString url, QString filename, int i,QJsonObject jsonObject);
void success(QByteArray data, QString api);
void error(QString data, QString url,QString api, int code);
public slots:
void setUrl(QString url);
void setApi(QString api);
void setLogin(QString login);
void setToken(QString token);
void setAccount(QVariantMap account);
void setDownloadtype(QString downloadtype);
void setFilename(QString filename);
void setContactlist(QList<QString> filename);
void setFilelist(QList<QString> filename);
void setImagedir(QString filename);
void setParam(QString name, QString value);
void setImageFileParam(QString name, QString url);
void clearParams();
void post();
void postJSON();
void get();
void getlist();
void download();
// void networktype();
private slots:
void onReplyError(QNetworkReply::NetworkError code);
void onReplySuccess();
void onRequestFinished();
void onReadyRead();
void onSSLError(const QList<QSslError> &errors);
//void updateDownloadProgress(qint64 bytesRead, qint64 totalBytes);
private:
QByteArray buffer;
QString m_url;
QString m_api;
QString m_login;
QString m_token;
QVariantMap m_account;
QString m_filename;
QString m_downloadtype;
// QString m_networktype;
QHash<QString, QString> params;
QHash<QString, QString> files;
QList<QString> m_filelist;
QList<QString> m_contactlist;
QString m_imagedir;
int dlindex;
QNetworkAccessManager manager;
//QNetworkRequest request;
QNetworkReply *reply;
//QNetworkConfiguration nc;
QString bufferToString();
};
#endif // XHR_H

41
src/js/friendworker.js Normal file
View file

@ -0,0 +1,41 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
WorkerScript.onMessage = function(msg) {
msg.model.clear();
for (var j=0;j<msg.albums.length;j++){
if (msg.albums[j]) {
var albumobject=msg.albums[j];
var data=({"albumobject": albumobject,"foreignPicture": msg.foreignPicture})}
msg.model.append(data);}
if (j==msg.albums.length){
msg.model.sync()
};
}

204
src/js/helper.js Normal file
View file

@ -0,0 +1,204 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
.pragma library
.import QtQuick.LocalStorage 2.0 as Sql
function friendicaRequest(login,api,rootwindow,callback) {
var xhrequest= new XMLHttpRequest();
xhrequest.onreadystatechange = function() {
if(xhrequest.readyState === XMLHttpRequest.DONE) {
try{
if (xhrequest.status==200){
callback(xhrequest.responseText)
}else{print("xhrequest.status "+xhrequest.status)
callback(xhrequest.responseText)
}
}
catch (e){
showMessage("Error", "API:\n" +login.server+api+"\n"+e+"\n Return: "+xhrequest.responseText,rootwindow)
}
}
}
if (login.password==""){
xhrequest.open("GET", login.server+api,true);
xhrequest.setRequestHeader("Authorization","Bearer "+login.token);
}
else{
xhrequest.open("GET", login.server+api,true,login.username,Qt.atob(login.password));
}
xhrequest.send();
}
function friendicaPostRequest(login,api,data,method,rootwindow,callback) {
var xhrequest= new XMLHttpRequest();
xhrequest.onreadystatechange = function() {
if (xhrequest.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
} else if(xhrequest.readyState === XMLHttpRequest.DONE) {
try{ if (xhrequest.responseText!=""){//print (xhrequest.responseText)
callback(xhrequest.responseText)
}else{
callback(xhrequest.responseText)
}
}
catch (e){
print("API:\n" + api+" "+e+"\n Return:"+xhrequest.responseText);
}
}
}
if (login.password==""){
xhrequest.open(method, login.server+api,true);
xhrequest.setRequestHeader("Authorization","Bearer "+login.token);
}
else{
xhrequest.open(method, login.server+api,true,login.username,Qt.atob(login.password));
}
xhrequest.send(data);
}
function getCount(database,login,table,field,countvalue){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var count=0;
db.transaction( function(tx) {
var countrs = tx.executeSql('SELECT COUNT(*) from '+table+' WHERE username= "'+ login.username +'" AND '+field+' = "'+countvalue+'"');
count = parseInt(countrs.rows.item(0)["COUNT(*)"])
})
return count
}
function friendicaRemoteAuthRequest(login,url,c_url,rootwindow,callback) {
var xhrequest = new XMLHttpRequest();
xhrequest.onreadystatechange = function() {
if (xhrequest.readyState === XMLHttpRequest.HEADERS_RECEIVED) {}
else if(xhrequest.readyState === XMLHttpRequest.DONE) {
try{callback(xhrequest.responseText)}
catch (e){showMessage("Error","Url:\n" +url+" "+e+"\n Return: "+xhrequest.responseText, rootwindow)}
}
}
xhrequest.open("GET", login.server+"/api/friendica/remoteauth?c_url="+c_url+"&url="+url,true,login.username,Qt.atob(login.password));
xhrequest.send();
}
function readData(database,table,username,callback,filter,filtervalue, sort) { // reads and applies data from DB
if (filter){
if (username){var where = " AND "+ filter +" = '" + filtervalue+"'";} else{
var where = " WHERE "+ filter +" = '" + filtervalue+"'";}
} else { var where="";}
if (username){
var user = ' where username= "'+ username +'"';
} else { var user='';}
if (sort){
var sortparam = " ORDER BY "+ sort;
} else { var sortparam="";}
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
if(!db) { return; }
db.transaction( function(tx) {
var rsArray=[];
var rs = tx.executeSql('select * from '+table+user+where+sortparam);
for(var i = 0; i < rs.rows.length; i++) {
rsArray.push(rs.rows.item(i))
}
callback(rsArray);
});
}
function readField(field,database,table, username, callback,filter,filtervalue) { // reads and applies data from DB
if (filter){
var where = " AND "+ filter +" = '" + filtervalue+"'";
} else { var where="";}
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
if(!db) { return; }
db.transaction( function(tx) {
//print('... read from database ' + field)
var rsArray=[];
var rs = tx.executeSql('select DISTINCT '+field+' from '+table+' WHERE username="'+username+'"'+where+' ORDER BY '+field+' ASC');
for(var i = 0; i < rs.rows.length; i++) {
rsArray.push(rs.rows.item(i)[field])
}
callback(rsArray);
});
}
function deleteData(database,table, username, callback,filter,filtervalue) { // reads and applies data from DB
if (filter){
var where = " AND "+ filter +" = '" + filtervalue+"'";
} else { var where="";}
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
if(!db) { return; }
db.transaction( function(tx) {
var rsArray=[];
var rs = tx.executeSql('DELETE from '+table+' WHERE username="'+username+'"'+where);
callback();
});
}
function updateData(database,table, username, key, value, callback,filter,filtervalue) { // reads and applies data from DB
if (filter){
var where = " AND "+ filter +" = '" + filtervalue+"'";
} else { var where="";}
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
if(!db) { return; }
db.transaction( function(tx) {
var rsArray=[];
var rs = tx.executeSql('UPDATE '+table+' SET '+key+' = '+value+' WHERE username="'+username+'"'+where);
callback();
});
}
function showMessage(header,message,rootwindow){//print(message);
var cleanmessage=message.replace(/"/g,"-");
if(cleanmessage.length>200){cleanmessage=cleanmessage.slice(0,200)+'...'}
var messageString='import QtQuick 6.3; import QtQuick.Controls 2.15; Dialog{ visible: true; title:"'+header+'";standardButtons: Dialog.Ok;anchors.centerIn: parent;Label{text:" '+cleanmessage+'"}}';
var messageObject=Qt.createQmlObject(messageString,rootwindow,"messageOutput");
}
function inArray(list, prop, val) {
if (list.length > 0 ) {
for (var i in list) { if (list[i][prop] == val) {
return i;
}
}
} return -1;
}
function cleanArray(array) {
var arraystring=JSON.stringify(array);
arraystring=arraystring.replace(/[\[\]]/g , '');
return arraystring;
}
function cleanDate(date){
var cleanedDate= date.slice(0,3)+", "+date.slice(8,11)+date.slice(4,7)+date.slice(25,30)+date.slice(10,25);
return cleanedDate
}

269
src/js/image.js Normal file
View file

@ -0,0 +1,269 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
.pragma library
.import QtQuick.LocalStorage 2.0 as Sql
.import "qrc:/js/helper.js" as Helperjs
function requestList(login,database,onlynew,rootwindow,callback) {
//get list of own images and call download function
Helperjs.friendicaRequest(login,"/api/friendica/photos/list", rootwindow,function (helperobject){
//print("return"+helperobject);
var obj=JSON.parse(helperobject);
if (onlynew){Helperjs.readField("id",database,"imageData",login.username,function(AllStoredImages){
if (AllStoredImages.length>0){
for(var i=0;i< AllStoredImages.length;i++){
var position=Helperjs.inArray(obj,"id",AllStoredImages[i]);
if (position>-1){obj.splice(position,1)}
}
}
callback(obj)
})}
else{callback(obj)}
})}
function dataRequest(login,photo,database,xhr,rootwindow) {
// check if image exist and call download function
Helperjs.friendicaRequest(login,"/api/friendica/photo?photo_id="+photo.id, rootwindow, function (image){
if(image=="" || typeof(image)=="undefined"){currentimageno=currentimageno+1}else{
try{
var obj = JSON.parse(image);
if (obj.hasOwnProperty('status')){
var helpfilename=photo.filename.substring(0,photo.filename.lastIndexOf("."));
var filesuffix="";
if (photo.type=="image/jpeg" || photo.type=="image/jpg"){filesuffix=".jpg"}
else if (photo.type=="image/png"){filesuffix=".png"}
else {filesuffix=""}
if (helpfilename==""){// check if file has any filename
photo.filename=photo["id"]+filesuffix;
}
else{photo.filename=helpfilename+filesuffix}
var link="";
xhr.setUrl(Qt.resolvedUrl(photo.thumb));
if(login.password!=""){xhr.setLogin(login.username+":"+Qt.atob(login.password));}
else{xhr.setToken(login.token)}
xhr.setFilename(login.imagestore+'albums/'+photo.album+"/"+photo["filename"]);
xhr.setDownloadtype("picture");
xhr.download();
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from imageData where id = "'+obj["id"]+'"');
if(result.rows.length === 1) {// use update
result = tx.executeSql('UPDATE imageData SET username ="' +login.username+ '",id="'+photo.id+'", created="'+photo.created+'", edited="'+photo.edited+'", profile="0", link="'+photo["thumb"]+'", filename="'+photo.filename+'",title="", desc="'+photo.desc+'", type="'+photo.type+'", width="0", height="0", album="'+photo.album+'", location="file://'+login.imagestore+'albums/'+photo.album+'/" where id="'+photo["id"]+'"');
} else {// use insert print('... does not exists, create it')
result = tx.executeSql('INSERT INTO imageData VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,photo.id,photo.created,photo.edited,'', photo.desc, photo.album, photo.filename, photo.type, '', '',0,photo["thumb"],'file://'+login.imagestore+'albums/'+photo.album+"/"]);
}
})
}else{
var helpfilename=obj.filename.substring(0,obj.filename.lastIndexOf("."));
var filesuffix="";
if (obj.type=="image/jpeg" || photo.type=="image/jpg"){filesuffix=".jpg"}
else if (obj.type=="image/png"){filesuffix=".png"}
else {filesuffix=""}
if (helpfilename==""){// check if file has any filename
obj.filename=obj["id"]+filesuffix;
}
else{obj.filename=helpfilename+filesuffix}
var link="";
if(obj["link"][0]){link=obj["link"][0]} else{link=obj["link"]["4"]}
xhr.setUrl(Qt.resolvedUrl(link));
if(login.password!=""){xhr.setLogin(login.username+":"+Qt.atob(login.password));}
else{xhr.setToken(login.token)}
xhr.setFilename(login.imagestore+'albums/'+obj.album+"/"+obj["filename"]);
xhr.setDownloadtype("picture");
xhr.download();
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from imageData where id = "'+obj["id"]+'"');
if(result.rows.length === 1) {// use update
result = tx.executeSql('UPDATE imageData SET username ="' +login.username+ '",id="'+obj.id+'", created="'+obj.created+'", edited="'+obj.edited+'", profile="'+obj.profile+'", link="'+link+'", filename="'+obj.filename+'",title="'+obj.title+'", desc="'+obj.desc+'", type="'+obj.type+'", width="'+obj.width+'", height="'+obj.height+'", album="'+obj.album+'", location="file://'+login.imagestore+'albums/'+obj.album+'/" where id="'+obj["id"]+'"');
} else {// use insert print('... does not exists, create it')
result = tx.executeSql('INSERT INTO imageData VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,obj.id,obj.created,obj.edited, obj.title, obj.desc, obj.album, obj.filename, obj.type, obj.height, obj.width,obj. profile,link,'file://'+login.imagestore+'albums/'+obj.album+"/"]);
}
})
}} catch (e){
var helpfilename=photo.filename.substring(0,photo.filename.lastIndexOf("."));
var filesuffix="";
if (photo.type=="image/jpeg" || photo.type=="image/jpg"){filesuffix=".jpg"}
else if (photo.type=="image/png"){filesuffix=".png"}
else {filesuffix=""}
if (helpfilename==""){// check if file has any filename
photo.filename=photo["id"]+filesuffix;
}
else{photo.filename=helpfilename+filesuffix}
var link="";
xhr.setUrl(Qt.resolvedUrl(photo.thumb));
if(login.password!=""){xhr.setLogin(login.username+":"+Qt.atob(login.password));}
else{xhr.setToken(login.token)}
xhr.setFilename(login.imagestore+'albums/'+photo.album+"/"+photo["filename"]);
xhr.setDownloadtype("picture");
xhr.download();
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from imageData where id = "'+photo["id"]+'"');
if(result.rows.length === 1) {// use update
result = tx.executeSql('UPDATE imageData SET username ="' +login.username+ '",id="'+photo.id+'", created="'+photo.created+'", edited="'+photo.edited+'", profile="0", link="'+photo["thumb"]+'", filename="'+photo.filename+'",title="", desc="'+photo.desc+'", type="'+photo.type+'", width="0", height="0", album="'+photo.album+'", location="file://'+login.imagestore+'albums/'+photo.album+'/" where id="'+photo["id"]+'"');
} else {// use insert print('... does not exists, create it')
result = tx.executeSql('INSERT INTO imageData VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,photo.id,photo.created,photo.edited,'', photo.desc, photo.album, photo.filename, photo.type, '', '',0,photo["thumb"],'file://'+login.imagestore+'albums/'+photo.album+"/"]);
}
})
print("Data retrieval failure! "+ e+obj);
}
}})
}
function storeImagedata(login,database,imagedata,rootwindow) {
// check if image exist and call download function
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from imageData where id = "'+imagedata["id"]+'"');
if(result.rows.length === 1) {// use update
result = tx.executeSql('UPDATE imageData SET username ="' +login.username+ '",id="'+imagedata.id+'", created="'+imagedata.created+'", edited="'+imagedata.edited+'", profile="'+imagedata.profile+'", link="'+imagedata.link[0]+'", filename="'+imagedata.filename+'",title="'+imagedata.title+'", desc="'+imagedata.desc+'", type="'+imagedata.type+'", width="'+imagedata.width+'", height="'+imagedata.height+'", album="'+imagedata.album+'", location="file://'+login.imagestore+'albums/'+imagedata.album+'/" where id="'+imagedata["id"]+'"');
} else {// use insert print('... does not exists, create it')
result = tx.executeSql('INSERT INTO imageData VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,imagedata.id,imagedata.created,imagedata.edited, imagedata.title, imagedata.desc, imagedata.album, imagedata.filename, imagedata.type, imagedata.height, imagedata.width,imagedata. profile,imagedata.link[0],'file://'+login.imagestore+'albums/'+imagedata.album+"/"]);
}
})}
function deleteImage(database,login,type,location,filesystem,rootwindow,callback) { // delete image locally and on server
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var rsfilename=location.substring(location.lastIndexOf("/")+1,location.length);
var rslocation=location.substring(0,location.lastIndexOf("/")+1);
db.transaction( function(tx) {
if (type=='image'){
var rs= tx.executeSql('SELECT * FROM imageData WHERE filename="'+rsfilename+'" AND location="'+rslocation+'"')
var imageId=rs.rows.item(0).id;
Helperjs.friendicaPostRequest(login,"/api/friendica/photo/delete?photo_id="+imageId,"","DELETE",rootwindow, function (obj){
//var deletereturn = JSON.parse(obj); print(obj);
//if (deletereturn.result=="deleted"){
db.transaction( function(tx) {
var deleters=tx.executeSql('DELETE FROM imageData WHERE location="'+rslocation+'" AND filename="'+rsfilename+'"'); });
filesystem.Directory=rslocation.substring(7,rslocation.length-1);
filesystem.rmFile(rsfilename)
//}
})
}
else{
Helperjs.friendicaPostRequest(login,"/api/friendica/photoalbum/delete?album="+rsfilename,"","DELETE",rootwindow, function (obj){
//var deletereturn = JSON.parse(obj);
//if (deletereturn.result=="deleted"){
db.transaction( function(tx) {
var rs= tx.executeSql('SELECT DISTINCT location FROM imageData WHERE album="'+rsfilename+'" AND username="'+login.username+'"');
var locationstring=rs.rows.item(0).location;
filesystem.Directory=locationstring.substring(7,locationstring.length-1);
filesystem.rmDir();
var deleters=tx.executeSql('DELETE FROM imageData WHERE album="'+location+'"');
})
//}
})
}
callback(location)
})
}
function updateImage(database,login,type,filesystem,imageId,rootwindow,callback) { // delete image locally and on server
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
Helperjs.readData(database,"imageData",login.username,function(obj){
db.transaction( function(tx) {
if (type=='image'){
var deleters=tx.executeSql('DELETE FROM imageData WHERE location="'+obj[0].location+'" AND filename="'+obj[0].filename+'"');
filesystem.Directory=obj[0].location
filesystem.rmFile(obj[0].filename)
}
})
},"id",imageId);
callback()
}
function deleteContacts(database,user,callback) { // does nothing useful at the moment
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
//print(' delete Image Data() for ' + field +"="+selection)
db.transaction( function(tx) {
result1= tx.executeSql('SELECT * FROM contacts a LEFT OUTER JOIN news b ON a.url==b.uid');
result2= tx.executeSql('SELECT * FROM contacts a LEFT OUTER JOIN news b ON a.url==b.uid');
callback(result)})
}
function getAlbumFromHtml(photohtml,remoteAuthBool,rootwindow,callback){
var photoarray=[];
var arr = photohtml.split("sidebar-photos-albums-li");
for (var i=2;i<arr.length;i++){
var albumlink = arr[i].substring(arr[i].indexOf('http'),arr[i].indexOf('class')-2);
var albumname=arr[i].substring(arr[i].indexOf('/span')+6,arr[i].indexOf('</a>')-1);
var album={'link':albumlink,'name':albumname}
photoarray.push(album);
}
callback(photoarray,remoteAuthBool)
}
function newRequestFriendsPictures(login,link,friend,remoteAuthBool,remoteauth,rootwindow,callback){
// screenscraping of pictures page for given album
if (remoteAuthBool){
remoteauth.setUrl(login.server);
remoteauth.setLogin(login.username+":"+Qt.atob(login.password));
remoteauth.setContacturl(friend.url);
Helperjs.friendicaRemoteAuthRequest(login,link,friend.url,rootwindow,function(photohtml){
getPictureFromHtml(photohtml,remoteAuthBool,function(photoarray){
callback(photoarray)
})
})}
}
function getPictureFromHtml(photohtml,remoteAuthBool,callback){
var photoarray=[];
var basehtml=photohtml.substring(photohtml.indexOf('<base')+12,photohtml.indexOf('/>',photohtml.indexOf('<base'))-2);
// old theme
if (photohtml.indexOf("photo-album-image-wrapper-end")>-1){ //theme 1
var arr = photohtml.split("photo-album-image-wrapper-end");}
// other themes
if (photohtml.indexOf("photo-album-wrapper")>-1){ //theme 2
var photoarea=photohtml.substring(photohtml.indexOf("photo-album-wrapper"),photohtml.indexOf("photo-album-end"))
var arr = photoarea.split("</a>");}
for (var i=0;i<arr.length-1;i++){
var photoname=arr[i].substring(arr[i].lastIndexOf('alt')+5,arr[i].lastIndexOf('title')-2);
var thumblink=arr[i].substring(arr[i].lastIndexOf('<img')+10,arr[i].lastIndexOf('alt')-2);
var imagetype=thumblink.substring(thumblink.lastIndexOf("."));
var photolink=thumblink.substring(0,thumblink.length-imagetype.length-2)+"-0"+imagetype
if(thumblink.substring(0,4)!=="http"){thumblink=basehtml+thumblink}
if(photolink.substring(0,4)!=="http"){photolink=basehtml+photolink}
if(remoteAuthBool){
thumblink="image://remoteauthimage/"+thumblink;
photolink="image://remoteauthimage/"+photolink;
}
var photo={'link':photolink,'name':photoname,'thumb':thumblink}
photoarray.push(photo);
}
callback(photoarray)
}

606
src/js/news.js Normal file
View file

@ -0,0 +1,606 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
.pragma library
.import QtQuick.LocalStorage 2.0 as Sql
.import "qrc:/js/helper.js" as Helperjs
function requestFriends(login,database,rootwindow,callback){
// return array of friends
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var result = tx.executeSql('UPDATE contacts SET isFriend=0 where username="'+login.username+'"'); // clean old friends
var result2 = tx.executeSql('DELETE from groups where username="'+login.username+'"'); // clean old groups
})
// /api/statuses/friends not working in Friendica 2/2022 , switching to api/v1/lists and download of all list members
// Helperjs.friendicaRequest(login,"/api/statuses/friends?count=9999", rootwindow,function (obj){
var allfriends=[];
Helperjs.friendicaRequest(login,"/api/v1/lists",rootwindow,function(listsobj){
var lists=JSON.parse(listsobj)
for (var list in lists){
Helperjs.friendicaRequest(login,"/api/v1/lists/"+lists[list].id+"/accounts?limit=0", rootwindow,function (obj){
var friends=JSON.parse(obj);
var memberarray=[];
for (var i=0;i<friends.length;i++){
if (friends[i].note!=null){
friends[i].description=friends[i].note;}
else{friends[i].description=""}
friends[i].name=friends[i].display_name;
friends[i].screen_name=friends[i].acct;
friends[i].location="";
friends[i].profile_image=friends[i].avatar_static;
friends[i].profile_image_url=friends[i].avatar;
friends[i].protected=false;
friends[i].friends_count=friends[i].following_count;
friends[i].created_at=Date.parse(friends[i].created_at);
friends[i].favorites_count=0;
friends[i].utc_offset=0;
friends[i].isFriend=1
friends[i].cid=0
friends[i].following=true
memberarray.push(parseInt(friends[i].id))
}
//requestGroups() not working with Friendica 02/2022
db.transaction( function(tx) {
var result3 = tx.executeSql('INSERT INTO groups VALUES (?,?,?,?)', [login.username,lists[list].title,lists[list].id,JSON.stringify(memberarray)])
})
callback(friends)
})};
});
}
function requestGroups(login,database,rootwindow,callback){
// retrieve, save and return groups. Other features currently not implemented
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
Helperjs.friendicaRequest(login,"/api/friendica/group_show",rootwindow, function (obj){
var groups=JSON.parse(obj);
db.transaction( function(tx) {
var result = tx.executeSql('DELETE from groups where username="'+login.username+'"'); // clean old groups
for (var i=0;i<groups.length;i++){
var memberarray=[]; for (var user in groups[i].user){memberarray.push(parseInt(groups[i].user[user].id))}
//print("Members: "+groups[i].user)
var result2 = tx.executeSql('INSERT INTO groups VALUES (?,?,?,?)', [login.username,groups[i].name,groups[i].gid,JSON.stringify(memberarray)])}
callback()
});
})}
function listFriends(login,database,callback,filter,isFriend=0){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var filtertext='';
if (filter!=null){var filtertext=new RegExp(".*"+filter.toLowerCase()+".*")}else{var filtertext=new RegExp(".*")}
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from contacts WHERE username="'+login.username+'" AND isFriend>'+isFriend+' ORDER BY screen_name');
// check for friends
var contactlist=[];
for (var i=0;i<result.rows.length;i++){
var contact=result.rows.item(i)
contact.name=Qt.atob(contact.name);
if (contact.screen_name==null){contact.screen_name=""}
if(filtertext.test(contact.name.toLowerCase())|| filtertext.test(contact.screen_name.toLowerCase())){
contactlist.push(contact)}
}
callback(contactlist)
});
}
function listBlocked(login,database,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from contacts WHERE username="'+login.username+'" AND statusnet_blocking=1 ORDER BY screen_name');
// check for friends
var contactlist=[];
for (var i=0;i<result.rows.length;i++){
var contact=result.rows.item(i)
contact.name=Qt.atob(contact.name);
if (contact.screen_name==null){contact.screen_name=""}
contactlist.push(contact)
}
callback(contactlist)
});
}
function listHashtags(login,database,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from hashtags WHERE username="'+login.username+'" ORDER BY date DESC LIMIT 50');
// check for friends
var taglist=[];
for (var i=0;i<result.rows.length;i++){
var tag=result.rows.item(i).tag;
tag=Qt.atob(tag);
taglist.push(tag)}
callback(taglist)
});
}
function storeHashtags(login,database,newstext,rootwindow){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var curDate= Date.now();
var hashtags=[];
hashtags=findTags(newstext);
for (var tag in hashtags){
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from hashtags where username="'+login.username+'" AND tag = "'+Qt.btoa(tag)+'"'); // check for tag
if(result.rows.length > 0) {// use update
result = tx.executeSql('UPDATE hashtags SET tag="'+'", date='+curDate+', ownership=0 where username="'+login.username+'" AND tag="'+Qt.btoa(hashtags[tag])+'"');
} else {// use insert
result = tx.executeSql('INSERT INTO hashtags (username,tag,date,statuses,ownership) VALUES (?,?,?,?,?)', [login.username,Qt.btoa(hashtags[tag]),curDate,"[]",0])
}
})
}
}
function deleteGroup(login,database,rootwindow,group, callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
Helperjs.friendicaPostRequest(login,"/api/friendica/group_delete?gid="+group.gid+"&name="+group.groupname,"","POST",rootwindow, function (obj){
var deletereturn=JSON.parse(obj);
if(deletereturn.success){
db.transaction( function(tx) {
var result = tx.executeSql('DELETE from groups where username="'+login.username+'" AND groupname="'+group.name+'"'); // delete group
callback()
});
}})}
function getLastNews(login,database,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var lastnewsid=0;
db.transaction( function(tx) {
var result = tx.executeSql('SELECT status_id from news WHERE username="'+login.username+'" AND messagetype=0 ORDER BY status_id DESC LIMIT 1');
try{lastnewsid=result.rows.item(0).status_id;}catch(e){lastnewsid=0};
callback(lastnewsid)
})
}
//function getFriendsTimeline(login,database,contacts,onlynew,rootwindow,callback){
// // retrieve and return timeline since last news, return contacts which are not friends and older than 2 days for update (friends can be updated in Contactstab)
// var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
// var parameter = "?count=50";
// if(onlynew){db.transaction( function(tx) {
// var result = tx.executeSql('SELECT status_id from news WHERE username="'+login.username+'" AND messagetype=0 ORDER BY status_id DESC LIMIT 1'); // check for last news id
// try{parameter=parameter+"&since_id="+result.rows.item(0).status_id;}catch(e){};})}
// var newContacts=[];
// Helperjs.friendicaRequest(login,"/api/statuses/friends_timeline"+parameter, rootwindow,function (obj){
// var news=JSON.parse(obj);
// if (news.hasOwnProperty('status')){
// Helperjs.showMessage(qsTr("Error"),"API:\n" +login.server+"/api/statuses/friends_timeline"+parameter+"\n Return: \n"+obj,rootwindow)
// }
// var newContacts=findNewContacts(news,contacts);
// callback(news,newContacts)
//})}
function getCurrentContacts(login,database,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var contactlist=[];
db.transaction( function(tx) {
var result = tx.executeSql('SELECT url from contacts WHERE username="'+login.username+'" AND isFriend=1'); // check for friends
for (var i=0;i<result.rows.length;i++){
contactlist.push(result.rows.item(i).url )
//print(result.rows.item(i).url)
}
var lastDate=Date.now()-604800000;// 7 days old
//print('SELECT url from contacts WHERE username="'+login.username+'" AND isFriend=0 AND imageAge>'+lastDate);
var result2 = tx.executeSql('SELECT url from contacts WHERE username="'+login.username+'" AND isFriend=0 AND imageAge > '+lastDate);
for (var j=0;j<result2.rows.length;j++){
contactlist.push(result2.rows.item(j).url )
}
})
callback(contactlist)
}
function findNewContacts(news,contacts){
var newContacts=[];
for (var i=0;i<news.length;i++){
var url=news[i].user.url;
if(contacts.indexOf(url)==-1 && !(inArray(newContacts,"url",url))){
news[i].user.isFriend=0;
newContacts.push(news[i].user);
}
if (news[i].hasOwnProperty('friendica_activities') && news[i].friendica_activities.like.length>0){
for (var j=0;j<news[i].friendica_activities.like.length;j++){
var like_url=news[i].friendica_activities.like[j].url;
if(contacts.indexOf(like_url)==-1 && !(inArray(newContacts,"url",like_url))){
news[i].friendica_activities.like[j].isFriend=0;
newContacts.push(news[i].friendica_activities.like[j]);
}
}
}
if (news[i].hasOwnProperty('friendica_activities') && news[i].friendica_activities.dislike.length>0){
for (var k=0;j<news[k].friendica_activities.dislike.length;k++){
var dislike_url=news[i].friendica_activities.dislike[k].url;
if(contacts.indexOf(dislike_url)==-1 && !(inArray(newContacts,"url",dislike_url))){
news[i].friendica_activities.dislike[k].isFriend=0;
newContacts.push(news[i].friendica_activities.dislike[k]);
}
}
}
if(news[i].hasOwnProperty('friendica_author')){
var owner_url=news[i].friendica_author.url;
if(contacts.indexOf(owner_url)==-1 && !(inArray(newContacts,"url",owner_url))){
news[i].friendica_author.isFriend=0;
newContacts.push(news[i].friendica_author);
}
}
}
return newContacts
}
function storeNews(login,database,news,rootwindow){
// save news after contacts download, call next function
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
for (var i=0;i<news.length;i++){
//print('store news data for ' + login.username+news[i].messagetype+Qt.btoa(news[i].text)+news[i].created_at+ news[i].in_reply_to_status_id+ news[i].source+ news[i].id+news[i].in_reply_to_user_id+news[i].geo+news[i].favorited+ news[i].user.id+Qt.btoa(news[i].statusnet_html)+news[i].statusnet_conversation_id+ Qt.btoa(JSON.stringify(friendica_activities))+"[]"+attachments+news[i].friendica_author.url);
//var ausdruck=news[i];
var likearray=[];var dislikearray=[];var attendyesarray=[];var attendnoarray=[];var attendmaybearray=[];
if(news[i].hasOwnProperty('friendica_activities')){
for (var user in news[i].friendica_activities.like){likearray.push(news[i].friendica_activities.like[user].url)}
for (var user in news[i].friendica_activities.dislike){dislikearray.push(news[i].friendica_activities.dislike[user].url)}
for (var user in news[i].friendica_activities.attendyes){attendyesarray.push(news[i].friendica_activities.attendyes[user].url)}
for (var user in news[i].friendica_activities.attendno){attendnoarray.push(news[i].friendica_activities.attendno[user].url)}
for (var user in news[i].friendica_activities.attendmaybe){attendmaybearray.push(news[i].friendica_activities.attendmaybe[user].url)}
}
var friendica_activities=[likearray,dislikearray,attendyesarray,attendnoarray,attendmaybearray]
var attachments="";if (news[i].attachments){attachments=Qt.btoa(JSON.stringify(news[i].attachments))}
//if (news[i].friendica_title!="") {news[i].statusnet_html="<b>"+news[i].friendica_title +"</b><br><br>"+news[i].friendica_html;}
//else{
news[i].statusnet_html=news[i].friendica_html//}
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from news where username="'+login.username+'" AND status_id = "'+news[i].id+'" AND messagetype='+news[i].messagetype); // check for news id
if(result.rows.length === 1) {// use update
result = tx.executeSql('UPDATE news SET username="'+login.username+'", messagetype='+news[i].messagetype+', text="'+Qt.btoa(news[i].text)+'", created_at="'+news[i].created_at+'", in_reply_to_status_id="'+news[i].in_reply_to_status_id+'", source="'+news[i].source+'", status_id="'+news[i].id+'", in_reply_to_user_id="'+news[i].in_reply_to_user_id+'", geo="'+news[i].geo+'", favorited="'+news[i].favorited+'", uid="'+news[i].user.id+'", statusnet_html="'+Qt.btoa(news[i].statusnet_html)+'", statusnet_conversation_id="'+news[i].statusnet_conversation_id+'",friendica_activities="'+Qt.btoa(JSON.stringify(friendica_activities))+'",attachments="'+attachments+'",friendica_owner="'+news[i].friendica_author.url+'" where username="'+login.username+'" AND status_id="'+news[i].status_id+'" AND messagetype=0');
} else {// use insert
result = tx.executeSql('INSERT INTO news (username,messagetype,text,created_at,in_reply_to_status_id,source,status_id,in_reply_to_user_id,geo,favorited,uid,statusnet_html,statusnet_conversation_id,friendica_activities,friendica_activities_self,attachments,friendica_owner) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,news[i].messagetype,Qt.btoa(news[i].text),news[i].created_at, news[i].in_reply_to_status_id, news[i].source, news[i].id,news[i].in_reply_to_user_id,news[i].geo,news[i].favorited, news[i].user.id,Qt.btoa(news[i].statusnet_html),news[i].statusnet_conversation_id, Qt.btoa(JSON.stringify(friendica_activities)),"[]",attachments,news[i].friendica_author.url])}})
}
}
function getActivitiesUserData(allcontacts,userUrlArray){//print(JSON.stringify(userUrlArray));
var helpArray=[];
for (var i=0;i<userUrlArray.length;i++){
helpArray.push(objFromArray(allcontacts,"url",userUrlArray[i]));
}
return helpArray
}
function newsfromdb(database,login,messagetype,callback,contact,stop_time){
// return news before stop_time (used by More button), in brackets of 20 entries, or by specified contact
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var result = tx.executeSql('SELECT status_id from news WHERE username="'+login.username+'" AND messagetype=0 ORDER BY status_id DESC LIMIT 1');
try{var lastid=result.rows.item(0).status_id;}catch(e){var lastid=0};
if (!stop_time){var stop="";
try{var rs = tx.executeSql('select created_at from news WHERE username="'+login.username+'" AND messagetype="'+messagetype+'" ORDER BY created_at DESC LIMIT 1');
stop="<="+rs.rows.item(0).created_at}catch(e){stop="<99999999999999"}}
else{var stop="<"+stop_time}
var contactfilter="";if(contact){contactfilter=" AND (uid='"+contact+"' OR friendica_owner='"+contact+"')"}
if (messagetype=="0"){messagetype="0,5"}
var newsrs=tx.executeSql('select * from news WHERE username="'+login.username+'" AND messagetype IN ( '+messagetype+' ) AND created_at'+stop+contactfilter+' ORDER BY created_at DESC LIMIT 20');
var newsArray=[];
var allcontacts=getAllContacts(database,login.username);
for(var i = 0; i < newsrs.rows.length; i++) {
newsArray.push(newsrs.rows.item(i));
if(newsArray[i].statusnet_html==""){
newsArray[i].statusnet_html=newsArray[i].text
}
newsArray[i].statusnet_html=Qt.atob(newsArray[i].statusnet_html);
newsArray[i].text=Qt.atob(newsArray[i].text);
newsArray[i].id=newsArray[i].status_id;
newsArray[i].friendica_author=objFromArray(allcontacts,"url",newsArray[i].friendica_owner)
newsArray[i]=fetchUsersForNews(database,login.username,newsArray[i],allcontacts);
if (newsArray[i].attachments!="" && newsArray[i].attachments!==null){newsArray[i].attachments=JSON.parse(Qt.atob(newsArray[i].attachments))};
}
callback(newsArray,lastid)});
}
function fetchUsersForNews(database,username,news,allcontacts){//print("fetchusers "+JSON.stringify(news));
news.user=objFromArray(allcontacts,"id",news.uid);
if(news.in_reply_to_user_id){news.reply_user=objFromArray(allcontacts,"id",news.in_reply_to_user_id)}
//news.friendica_owner_object=objFromArray(allcontacts,"url",news.friendica_owner);
news.friendica_author=objFromArray(allcontacts,"url",news.friendica_author);
if (news.messagetype==0){
var friendicaArray=JSON.parse(Qt.atob(news.friendica_activities));
delete news.friendica_activities;
news.friendica_activities={};
//for(var j=0;j<friendicaArray.length;j++){
news.friendica_activities.like=getActivitiesUserData(allcontacts,friendicaArray[0]);
news.friendica_activities.dislike=getActivitiesUserData(allcontacts,friendicaArray[1]);
news.friendica_activities.attendyes=getActivitiesUserData(allcontacts,friendicaArray[2]);
news.friendica_activities.attendno=getActivitiesUserData(allcontacts,friendicaArray[3]);
news.friendica_activities.attendmaybe=getActivitiesUserData(allcontacts,friendicaArray[4]);
//}
}
return news
}
function deleteNews(login,database,newsid,messagetype,rootwindow,callback){
var api="" ;
if (messagetype==0){ api="/api/statuses/destroy?id="}
else if (messagetype==1){ api="/api/direct_messages/destroy?id="}
else if (messagetype==2){ api="/api/friendica/notifications/seen?id="}
Helperjs.friendicaPostRequest(login,api+newsid,"","POST", rootwindow,function (obj){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {var result = tx.executeSql('DELETE from news where username="'+login.username+'" AND messagetype='+messagetype+' AND status_id ='+newsid); // delete news id
callback(obj)
});
})}
function retweetNews(login,database,newsid,rootwindow,callback){
Helperjs.friendicaPostRequest(login,"/api/v1/statuses/"+newsid+"/reblog","","POST", rootwindow,function (obj){
var answer=JSON.parse(obj);
if(answer.hasOwnProperty('status'))//('error' in answer.status)
{Helperjs.showMessage("Repost",answer.status.code,rootwindow);}
else{Helperjs.showMessage("Repost",answer.content,rootwindow)}
})
}
function favorite(login,favorited,newsid,rootwindow){
// toggle favorites
if(favorited){ Helperjs.friendicaPostRequest(login,"/api/v1/statuses/"+newsid+"/bookmark","","POST", rootwindow,function (obj){
})}
else {Helperjs.friendicaPostRequest(login,"/api/v1/statuses/"+newsid+"/unbookmark","","POST",rootwindow,function (obj){
})}
}
function likerequest(login,database,verb,newsid,rootwindow){
Helperjs.friendicaPostRequest(login,"/api/friendica/activity/"+verb+"?id="+newsid, "","POST",rootwindow,function (obj){
if (obj=='"ok"'){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var currentActivities_rs=tx.executeSql('select friendica_activities_self from news WHERE username="'+login.username+'" AND status_id='+newsid) ;
var currentActivities=JSON.parse(currentActivities_rs.rows.item(0).friendica_activities_self);
if ((verb=="like")&&(currentActivities.indexOf(1)==-1)){ currentActivities.push(1);
if (currentActivities.indexOf(2)!=-1){currentActivities.splice(currentActivities.indexOf(2),1)}
}
if ((verb=="dislike")&&(currentActivities.indexOf(2)==-1)){ currentActivities.push(2);
if (currentActivities.indexOf(1)!=-1){currentActivities.splice(currentActivities.indexOf(1),1)}
}
if (verb=="unlike"){ if (currentActivities.indexOf(1)!=-1){currentActivities.splice(currentActivities.indexOf(1),1)}}
if (verb=="undislike"){ if (currentActivities.indexOf(2)!=-1){currentActivities.splice(currentActivities.indexOf(2),1)}}
var result = tx.executeSql('UPDATE news SET friendica_activities_self ="'+JSON.stringify(currentActivities)+'" where username="'+login.username+'" AND status_id ='+newsid);
})}
else{}})
}
function like(login,database,toggle,verb,newsid,rootwindow){
if(verb=="like"&& toggle==1){likerequest(login,database,"like",newsid,rootwindow);
}
if(verb=="dislike"&& toggle==1){likerequest(login,database,"dislike",newsid,rootwindow);
}
if(toggle==0){
likerequest(login,database,"un"+verb,newsid,rootwindow);}
}
function attend(login,database,attend,newsid,rootwindow,callback){
Helperjs.friendicaPostRequest(login,"/api/friendica/activity/attend"+attend+"?id="+newsid, "","POST",rootwindow,function (obj){
if (obj=='"ok"')
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var currentActivities_rs=tx.executeSql('select friendica_activities_self from news WHERE username="'+login.username+'" AND status_id='+newsid) ;
var currentActivities=JSON.parse(currentActivities_rs.rows.item(0).friendica_activities_self);
if ((attend=="yes")&&(currentActivities.indexOf(3)==-1)){
currentActivities.push(3);
if (currentActivities.indexOf(4)!=-1){currentActivities.splice(currentActivities.indexOf(4),1)}
if (currentActivities.indexOf(5)!=-1){currentActivities.splice(currentActivities.indexOf(5),1)}
}
if ((attend=="no")&&(currentActivities.indexOf(4)==-1)){
currentActivities.push(4);
if (currentActivities.indexOf(3)!=-1){currentActivities.splice(currentActivities.indexOf(3),1)}
if (currentActivities.indexOf(5)!=-1){currentActivities.splice(currentActivities.indexOf(5),1)}
}
if ((attend=="maybe")&&(currentActivities.indexOf(5)==-1)){
currentActivities.push(5);
if (currentActivities.indexOf(3)!=-1){currentActivities.splice(currentActivities.indexOf(3),1)}
if (currentActivities.indexOf(4)!=-1){currentActivities.splice(currentActivities.indexOf(4),1)}
}
var result = tx.executeSql('UPDATE news SET friendica_activities_self ="'+JSON.stringify(currentActivities)+'" where username="'+login.username+'" AND status_id ='+newsid);
callback();
})})}
function requestConversation(login,database,newsid,contacts,rootwindow,callback){
Helperjs.friendicaRequest(login,"/api/conversation/show?id="+newsid,rootwindow, function (obj){
var news=JSON.parse(obj);
var newContacts=findNewContacts(news,contacts);
// storeNews(login,database,news,rootwindow,callback)
callback(news,newContacts)
})}
function requestFavorites(login,database,contacts,rootwindow,callback){
Helperjs.friendicaRequest(login,"/api/favorites",rootwindow, function (obj){
var news=JSON.parse(obj);
var newContacts=findNewContacts(news,contacts);
// storeNews(login,database,news,rootwindow,callback)
callback(news,newContacts)
})}
function chatsfromdb(database,login,messagetype,currentconversations,callback,stop_id){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
if (!stop_id){var stop="";
try{var rs = tx.executeSql('select status_id from news WHERE username="'+login.username+'" AND messagetype="'+messagetype+'" ORDER BY status_id DESC LIMIT 1');
stop="<="+rs.rows.item(0).status_id}catch(e){stop="<99999999999999"}}
else{var stop="<"+stop_id}
if (messagetype=="1"){messagetype="1,5"}
let conversationfilter="";
if (currentconversations.length>0){conversationfilter="AND statusnet_conversation_id NOT IN ("+currentconversations.toString()+") "}
var conversationsrs=tx.executeSql('select DISTINCT statusnet_conversation_id from news WHERE username="'+login.username+'" AND status_id'+stop+' AND messagetype IN ( "'+messagetype+'" ) '+ conversationfilter +'ORDER BY created_at DESC LIMIT 20'); //+' ORDER BY created_at DESC LIMIT 20');
var result = tx.executeSql('SELECT status_id from news WHERE username="'+login.username+'" AND messagetype=0 ORDER BY status_id DESC LIMIT 1');
try{var lastid=result.rows.item(0).status_id;}catch(e){var lastid=0};
var conversations=[];
for(var i = 0; i < conversationsrs.rows.length; i++) {
conversations.push(conversationsrs.rows.item(i).statusnet_conversation_id);
}
var newsArray=[];
var allcontacts=getAllContacts(database,login.username);
for(var j = 0; j< conversations.length; j++) {
var newsrs=tx.executeSql('select * from news WHERE username="'+login.username+'" AND statusnet_conversation_id="'+conversations[j] +'" AND messagetype="'+messagetype+'" ORDER BY created_at ASC');
var helpernews=newsrs.rows.item(0);
helpernews=cleanhelpernews(database,login.username,helpernews,allcontacts)
helpernews.currentconversation=[];
for (var h = 0;h<newsrs.rows.length;h++){
var helpernews2=newsrs.rows.item(h);
if(helpernews.id!=helpernews2.status_id){
helpernews2.newscount=0;
helpernews2=cleanhelpernews(database,login.username,helpernews2,allcontacts)
helpernews.currentconversation.push(helpernews2)
}
}
helpernews.newscount=newsrs.rows.length;
newsArray.push(helpernews);
}
callback(newsArray,lastid);
})}
function allchatsfromdb(database,user,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
// if (!stop_time){var stop="";
// try{var rs = tx.executeSql('select created_at from news WHERE username="'+username+'" ORDER BY created_at DESC LIMIT 1');
// stop="<="+rs.rows.item(0).created_at}catch(e){stop="<99999999999999"}}
// else{var stop="<"+stop_time}
var conversationsrs=tx.executeSql('select DISTINCT statusnet_conversation_id from news WHERE username="'+user+'" ORDER BY created_at DESC'); //+' ORDER BY created_at DESC LIMIT 20');
var conversationIds=[];
for(var i = 0; i < conversationsrs.rows.length; i++) {
conversationIds.push(conversationsrs.rows.item(i).statusnet_conversation_id);
}
var newsArray=[];
var countArray=[];
var allcontacts=getAllContacts(database,user);
for(var j = 0; j< conversationIds.length; j++) {
var newsrs=tx.executeSql('select * from news WHERE username="'+user+'" AND statusnet_conversation_id="'+conversationIds[j] +'" ORDER BY created_at ASC');
var helpernews=newsrs.rows.item(0);
if (helpernews){
var helpernews=cleanhelpernews(database,user,helpernews,allcontacts)
helpernews.currentconversation=[];
for (var h = 0;h<newsrs.rows.length;h++){
var helpernews2=newsrs.rows.item(h);
if(helpernews.id!=helpernews2.status_id){
helpernews2.newscount=0;
helpernews2=cleanhelpernews(database,user,helpernews2,allcontacts)
helpernews.currentconversation.push(helpernews2)
}
}
}
newsArray.push(helpernews);
countArray.push(newsrs.rows.length)
}
var conversationsobject=({});
conversationsobject.conversationIds=conversationIds;
conversationsobject.newsArray=newsArray;
conversationsobject.countArray=countArray;
callback(conversationsobject);
})}
function oldchatfromdb(database,user,conversationId,lastpost,allcontacts,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var newsrs=tx.executeSql('select * from news WHERE username="'+user+'" AND statusnet_conversation_id="'+conversationId +'" AND status_id<'+lastpost+' ORDER BY created_at ASC');
let conversation=[];
var helpernews={currentconversation:conversation}
if(newsrs.rows.length>0){
for (var h = 0;h<newsrs.rows.length;h++){
var helpernews2=newsrs.rows.item(h);
if(helpernews.id!=helpernews2.status_id){
helpernews2.newscount=0;
helpernews2=cleanhelpernews(database,user,helpernews2,allcontacts)
//print(" helpernews "+JSON.stringify(helpernews.currentconversation))
helpernews.currentconversation.push(helpernews2)
}
}
}
var newscount=newsrs.rows.length;
callback(helpernews,newscount);
})
}
function cleanhelpernews(database,user,helpernews,allcontacts){
helpernews=fetchUsersForNews(database,user,helpernews,allcontacts);
helpernews.statusnet_html=Qt.atob(helpernews.statusnet_html);
helpernews.text=Qt.atob(helpernews.text);
helpernews.id=helpernews.status_id;
try{let geoobj=JSON.parse(helpernews.geo); helpernews.external_url=geoobj.external_url}catch(e){}
helpernews.friendica_author=objFromArray(allcontacts,"url",helpernews.friendica_owner);
if (helpernews.attachments!="" && helpernews.attachments!==null){helpernews.attachments=JSON.parse(Qt.atob(helpernews.attachments))};
return helpernews
}
function getAllContacts(database,user){
var allcontacts=[];
Helperjs.readData(database,"contacts",user,function(obj){
allcontacts=obj;
for (var n in allcontacts){
allcontacts[n].name=Qt.atob(allcontacts[n].name);
allcontacts[n].description=Qt.atob(allcontacts[n].description)
}
});
return allcontacts;
}
function inArray(list, prop, val) {
if (list.length > 0 ) {
for (var i in list) {if (list[i][prop] == val) {
return true;
}
}
} return false;
}
function objFromArray(list, prop, val) {
if (list.length > 0 ) {
for (var i in list) {if (list[i][prop] == val) {
return list[i];
}
}
} return false;
}
function cleanDate(date){
var cleanedDate= date.slice(0,3)+", "+date.slice(8,11)+date.slice(4,7)+date.slice(25,30)+date.slice(10,25);
return cleanedDate
}
function findTags(fulltext){
return fulltext.match(/\s+[#]+[A-Za-z0-9-_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]+/g)
}

170
src/js/newsworker.js Normal file
View file

@ -0,0 +1,170 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
function findend (text, startpos) {
var indexOf = text.substring(startpos || 0).search(/\s/);
return (indexOf >= 0) ? (indexOf + (startpos || 0)) : text.length;
}
function beautify(newsitemobject,msg){
var forumname="";
try{
if (newsitemobject.messagetype==0&&newsitemobject.hasOwnProperty('friendica_author')&&
((newsitemobject.friendica_author.url)!=(newsitemobject.user.url))&&((newsitemobject.friendica_author.url)!=null)){
//print(" Friendica Author "+JSON.stringify(newsitemobject));
forumname=" via "+newsitemobject.user.name;
newsitemobject.user=newsitemobject.friendica_author;
}
if (typeof(newsitemobject.friendica_activities_self)=="string"){
newsitemobject.friendica_activities_self=JSON.parse(newsitemobject.friendica_activities_self);
}
}catch(e){print("forum name "+e)}
var self=({})
if (newsitemobject.hasOwnProperty("friendica_activities_self")){
if (newsitemobject.friendica_activities_self.indexOf(3)!=-1){self.attending=qsTr("yes")}
if (newsitemobject.friendica_activities_self.indexOf(4)!=-1){self.attending=qsTr("no")}
if (newsitemobject.friendica_activities_self.indexOf(5)!=-1){self.attending=qsTr("maybe")}
if (newsitemobject.friendica_activities_self.indexOf(1)!=-1){self.liked=1}
if (newsitemobject.friendica_activities_self.indexOf(2)!=-1){self.disliked=1}
}
var friendica_activities={self:self}
var imageAttachmentList=[];
var videoAttachmentList=[];
var videoformats=["mp4", "avi", "webm","ogg","mp3"]
try{if(newsitemobject.attachments){
var attachArray=newsitemobject.attachments;
for (var image in attachArray){
var attachhelper={mimetype:attachArray[image].mimetype}
var attachhelperstring="<img"
var helperstringposition=newsitemobject.statusnet_html.indexOf(attachhelperstring);
attachhelper.url=attachArray[image].url
if (helperstringposition>-1){var removeme=newsitemobject.statusnet_html.substring(helperstringposition,newsitemobject.statusnet_html.indexOf('">',helperstringposition)+2);}
newsitemobject.statusnet_html=newsitemobject.statusnet_html.replace(removeme,"")
imageAttachmentList.push(attachhelper)
}
}
}catch(e){print("attachment "+e)}
newsitemobject.statusnet_html=newsitemobject.statusnet_html.replace(/class=\"attachment-image\"/g,"width=\"600\" ");
for (var format in videoformats){
if (newsitemobject.text.indexOf("."+videoformats[format])>-1){
var videohelper={mimetype:"video/"+videoformats[format]}
var videotext=newsitemobject.text;
while (videotext.indexOf("."+videoformats[format])>-1){
var videohelperstringposition=videotext.indexOf("."+videoformats[format]);
videohelper.url=videotext.substring(videotext.lastIndexOf("http",videohelperstringposition),videohelperstringposition+4);
videotext=videotext.substring(videohelperstringposition+4,videotext.length)
if ((videoAttachmentList.length==0) || (videoAttachmentList[videoAttachmentList.length-1].url!=videohelper.url)){videoAttachmentList.push(videohelper)}
}
}
}
if (newsitemobject.text.indexOf("/videos/watch/")>-1){
var ptvideohelper={mimetype:"video/mp4"}
var ptvideotext=newsitemobject.text;
while (ptvideotext.indexOf("/videos/watch/")>-1){
var ptvideohelperstringposition=ptvideotext.indexOf("/videos/watch/");
var ptposend=findend(ptvideotext,ptvideohelperstringposition);
if(ptposend==-1){ptposend=ptvideotext.length};
ptvideohelper.url=ptvideotext.substring(ptvideotext.lastIndexOf("http",ptvideohelperstringposition),ptposend)+"-480.mp4";
ptvideohelper.url=ptvideohelper.url.replace("/videos/watch","/static/webseed");
ptvideotext=ptvideotext.substring(ptposend,ptvideotext.length)
if ((videoAttachmentList.length==0) || (videoAttachmentList[videoAttachmentList.length-1].url!=ptvideohelper.url)){videoAttachmentList.push(ptvideohelper)}
}
}
newsitemobject.videoAttachmentList=videoAttachmentList;
newsitemobject.imageAttachmentList=imageAttachmentList;
if ((msg.options.hasOwnProperty("hide_nsfw"))&&(msg.options.hide_nsfw==1)&&(newsitemobject.text.indexOf("#nsfw")>-1)){
newsitemobject.nsfw=true
} else{newsitemobject.nsfw=false}
newsitemobject.dateDiff=(msg.currentTime-newsitemobject.created_at)/1000;
newsitemobject.friendica_activities_view=friendica_activities;
newsitemobject.forumname=forumname;
return newsitemobject;
}
WorkerScript.onMessage = function(msg) {
if(msg.deleteId!==undefined)
{msg.model.remove(msg.deleteId);
msg.model.sync()
}
else{
if(msg.method=="refresh" || msg.method=="contact" ||(msg.method=="conversation"&&msg.news.length>0)){msg.model.clear()};
msg.model.sync()
for (var j in msg.news){
let data=({});
if (typeof(msg.news[j])=='object') {
var newsitemobject=msg.news[j];
newsitemobject=beautify(newsitemobject,msg);
if (!(typeof(newsitemobject.currentconversation)=='undefined') && (newsitemobject.currentconversation.length>0)){
let n=1;
while ((n<newsitemobject.currentconversation.length)&&(newsitemobject.currentconversation[newsitemobject.currentconversation.length-n].user.statusnet_blocking==true)) {
n++;
}
if (n<newsitemobject.currentconversation.length){
newsitemobject.lastcomment=beautify(newsitemobject.currentconversation[newsitemobject.currentconversation.length-n],msg);
newsitemobject.lastcomment.indent=1
newsitemobject.lastcomment.isLastComment=true
}
}
if (msg.method=="conversation"){
if (j==0){newsitemobject.indent=0}else{
var count=0;
var firstReply=0;
for (var k=msg.model.count-1;k>-1;k--){
if (newsitemobject.in_reply_to_status_id==msg.model.get(k).newsitemobject.id){
newsitemobject.indent=(msg.model.get(k).newsitemobject.indent||0)+1;
if (newsitemobject.indent>6){newsitemobject.indent=6};
firstReply=k;
}
if (newsitemobject.in_reply_to_status_id==msg.model.get(k).newsitemobject.in_reply_to_status_id){
count+=1
}
}
}}
data=({"newsitemobject": newsitemobject})
}
if(data.newsitemobject.user.statusnet_blocking==true){break}
if(msg.method=="append") {
msg.model.insert(j, data)
} else if (msg.method=="conversation" && firstReply>0){
msg.model.insert(firstReply+count+1, data)
}
else{
msg.model.append(data)
}
}
msg.model.sync()
}
}

47
src/js/photoworker.js Normal file
View file

@ -0,0 +1,47 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
WorkerScript.onMessage = function(msg) {
if (msg.firstalbum==0){msg.model.clear();}
var contact={}; try{contact=msg.friend}catch(e){print(e)}
var limit=0; if (msg.albums.length-msg.firstalbum<20){limit=msg.albums.length} else{limit=msg.firstalbum+20}
for (var j=msg.firstalbum;j<limit;j++){
if (msg.albums[msg.firstalbum]) {
if(msg.foreignPicture){
var albumname=msg.albums[j].name.trim();var albumlink=msg.albums[j].link
}else{
var albumname=msg.albums[j].name;var albumlink=""
}
msg.model.append({"albumlink":albumlink,"foreignPicture":msg.foreignPicture,"albumname":albumname,"friend":contact});
msg.model.sync()
};
}
}

574
src/js/service.js Normal file
View file

@ -0,0 +1,574 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//.pragma library
.import QtQuick.LocalStorage 2.0 as Sql
.import "qrc:/js/helper.js" as Helperjs
.import "qrc:/js/news.js" as Newsjs
const newsViewType = 'Conversations'
const defaultNewsTypes = encodeURI(JSON.stringify(["Home","Replies","DirectMessages","Notifications"])) //'[\"Home\",\"Replies\",\"DirectMessages\",\"Notifications\"]'
// CONFIG FUNCTIONS
function initDatabase(database) { // initialize the database object
var db =Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
//print('initDatabase()'+database[0]+database[1]+database[2]+database[3])
db.transaction( function(tx) {
//var version=tx.executeSql('PRAGMA user_version');print(JSON.stringify(version.rows.item(0)))
tx.executeSql('CREATE TABLE IF NOT EXISTS imageData(username TEXT,id INT, created TEXT,edited TEXT, title TEXT, desc TEXT, album TEXT,filename TEXT, type TEXT, height INT, width INT, profile INT, link TEXT,location TEXT)');
tx.executeSql('CREATE TABLE IF NOT EXISTS config(server TEXT, username TEXT, password TEXT, imagestore TEXT, maxnews INT, timerInterval INT, newsViewType TEXT,isActive INT, permissions TEXT,maxContactAge INT,APIVersion TEXT,layout TEXT, addons TEXT)');
tx.executeSql('CREATE TABLE IF NOT EXISTS news(username TEXT, messagetype INT, text TEXT, created_at INT, in_reply_to_status_id INT, source TEXT, status_id INT, in_reply_to_user_id INT, geo TEXT,favorited TEXT, uid INT, statusnet_html TEXT, statusnet_conversation_id TEXT,friendica_activities TEXT, friendica_activities_self TEXT, attachments TEXT, friendica_owner TEXT)');
tx.executeSql('CREATE TABLE IF NOT EXISTS contacts(username TEXT, id INT, name TEXT, screen_name TEXT, location TEXT,imageAge INT, profile_image_url TEXT, description TEXT, profile_image BLOB, url TEXT, protected TEXT, followers_count INT, friends_count INT, created_at INT, favourites_count TEXT, utc_offset TEXT, time_zone TEXT, statuses_count INT, following TEXT, verified TEXT, statusnet_blocking TEXT, notifications TEXT, statusnet_profile_url TEXT, cid INT, network TEXT, isFriend INT, timestamp INT)');
// tx.executeSql('CREATE INDEX IF NOT EXISTS contact_id ON contacts(id)');
tx.executeSql('CREATE TABLE IF NOT EXISTS profiles(username TEXT, id INT, profiledata TEXT)');
tx.executeSql('CREATE TABLE IF NOT EXISTS groups(username TEXT, groupname TEXT, gid INT, members TEXT)');
tx.executeSql('CREATE TABLE IF NOT EXISTS events(username TEXT, id INT, start INT, end INT, allday INT, title TEXT, j INT, d TEXT, isFirst INT, uid INT, cid INT, uri TEXT, created INT, edited INT, desc TEXT, location TEXT, type TEXT, nofinish TEXT, adjust INT, ignore INT, permissions TEXT, guid INT, itemid INT, plink TEXT, authorName TEXT, authorAvatar TEXT, authorLink TEXT, html TEXT)');
tx.executeSql('CREATE TABLE IF NOT EXISTS globaloptions(k TEXT, v TEXT)');
tx.executeSql('CREATE TABLE IF NOT EXISTS friendshiprequests(username TEXT, id INT, usernamef TEXT, acct TEXT, display_name TEXT, locked TEXT, created_at INT, followers_count INT, following_count INT, statuses_count INT, note TEXT, url TEXT, avatar TEXT, avatar_static TEXT, header TEXT, header_static TEXT, emojis TEXT, moved TEXT, fields TEXT, bot TEXT, groupf TEXT, discoverable TEXT, last_status_at INT)');
tx.executeSql('CREATE TABLE IF NOT EXISTS hashtags(username TEXT, tag TEXT,date INT, statuses TEXT, ownership INT )');
tx.executeSql('CREATE TABLE IF NOT EXISTS drafts(username TEXT, header TEXT, statushtml TEXT, attachments TEXT, permissions TEXT, sendtime INT)');
})}
function cleanPermissions(oldperms){
var newperms=oldperms.replace("<","");newperms=newperms.replace(">","");newperms="["+newperms+"]";
var newpermArray=JSON.parse(newperms);
return (newpermArray)
}
function newscount(database, callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var newscountrs = tx.executeSql('SELECT COUNT(*) from news');
var newscount = newscountrs.rows.item(0)["COUNT(*)"];
callback(newscount)
})
}
function eventsfromdb(database, username,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var allcontacts=[];
allcontacts=Newsjs.getAllContacts(root.db,login.username);
db.transaction( function(tx) {
var eventrs=tx.executeSql('select * from events WHERE username="'+username+'" ORDER BY start ASC');
var eventArray=[];
var dayArray=[];
for(var i = 0; i < eventrs.rows.length; i++) {
eventArray.push(eventrs.rows.item(i));
if (eventArray[i].cid!=0){eventArray[i]["eventOwner"]=Newsjs.objFromArray(allcontacts,"cid",eventArray[i].cid);}
else{eventArray[i]["eventOwner"]=Newsjs.objFromArray(allcontacts,"isFriend",2);}
var startday=Math.floor((eventArray[i].start-new Date(eventArray[i].start).getTimezoneOffset() * 60 * 1000)/86400000);
var endday=Math.floor((eventArray[i].end-1-new Date(eventArray[i].end).getTimezoneOffset() * 60 * 1000)/86400000);if (endday<startday){endday=startday}
eventArray[i]["startday"]=startday;eventArray[i]["endday"]=endday;
dayArray.push(startday);
if (endday>startday){
for (var j=startday+1;j<endday+1;j++){dayArray.push(j)}
}
}
callback(eventArray,dayArray)});
}
function savePermissions(database,obj) { // stores config to DB
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var permissions=JSON.stringify(obj)
db.transaction( function(tx) {
var result = tx.executeSql( 'UPDATE config SET permissions="'+permissions+'" WHERE username="'+obj.username +'"');
})
}
function storeConfig(database,obj) { // stores config to DB
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
//print(JSON.stringify(obj));
var result = tx.executeSql('SELECT * from config WHERE username="'+obj.username+'"');
if(result.rows.length === 1) {// use update
var result2 = tx.executeSql('UPDATE config SET server="'+obj.server+'",password="'+obj.password+'", imagestore="'+obj.imagestore+'", maxnews="'+obj.accountId+'", timerInterval=0, newsViewType="'+obj.newsViewType+'", isActive=0, APIVersion="'+Qt.btoa(obj.token)+'", addons="'+obj.client + '" WHERE username="'+obj.username +'"');
var result3 = tx.executeSql('UPDATE config SET isActive=1 WHERE username !="'+obj.username +'"');
} else {// use insert print('... does not exists, create it')
var result2 = tx.executeSql('INSERT INTO config VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)', [obj.server, obj.username, obj.password, obj.imagestore, obj.accountId, 0,obj.newsViewType,0,"[[],[],[],[]]",0,Qt.btoa(obj.token),"",obj.client]);
var result3 = tx.executeSql('UPDATE config SET isActive=1 WHERE username !="'+obj.username +'"');
}
})}
function requestProfile(login,database,rootwindow,callback){
// return profile data
Helperjs.friendicaRequest(login,"/api/friendica/profile/show", rootwindow,function (obj){
var profiledata=JSON.parse(obj);
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
for (var i=0;i<profiledata.profiles.length;i++){
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from profiles where username="'+login.username+'" AND id = '+profiledata.profiles[i].profile_id); // check for profile id
if(result.rows.length === 1) {// use update
result = tx.executeSql('UPDATE profiles SET profiledata="'+ Qt.btoa(JSON.stringify(profiledata.profiles[i]))+'" WHERE username="'+login.username+'" AND id='+parseInt(profiledata.profiles[i].profile_id));
} else {// use insert
result = tx.executeSql('INSERT INTO profiles (username,id,profiledata) VALUES (?,?,?)', [login.username,profiledata.profiles[i].profile_id,Qt.btoa(JSON.stringify(profiledata.profiles[i]))])}
});
}
var profile=profiledata.friendica_owner;
profile.isFriend=2;
var profilearray=[];profilearray.push(profile);
callback(profilearray)
});
}
function readConfig(database,callback,filter,filtervalue) { // reads config
if (filter){var where = " WHERE "+ filter +" = '" + filtervalue+"'"} else { var where=""}
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3],initDatabase(database));
db.transaction( function(tx) {
var tables = tx.executeSql("SELECT * FROM sqlite_master WHERE type='table'");
if (tables.rows.length==0){print("no database");callback("")} else {
updatenews.setDatabase();
let rsArray=updatenews.getAccounts(filter,filtervalue);
let rsObject=rsArray[0];
callback(rsObject)}}
)
}
function readAllLogins(database,callback) { // reads config
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3],initDatabase(database));
db.transaction( function(tx) {
var tables = tx.executeSql("SELECT * FROM sqlite_master WHERE type='table'");
if (tables.rows.length==0){print("no database");callback("")} else {
updatenews.setDatabase();
var rsArray=updatenews.getAccounts();
}
callback(rsArray)}
)
}
function readActiveConfig(database){
var obj="";
readConfig(database,function(config){obj=config},"isActive", 0);
return obj;
}
function setDefaultOptions(database){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
let rs = tx.executeSql('INSERT INTO globaloptions (k,v) VALUES ("newsViewType","'+ newsViewType+'")');
let rs2 = tx.executeSql('INSERT INTO globaloptions (k,v) VALUES ("defaultNewsTypes","'+ defaultNewsTypes+'")');
})
}
function readGlobaloptions(database,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
var go=({});
db.transaction( function(tx) {
var rs = tx.executeSql('select * from globaloptions');
for (var r=0; r<rs.rows.length; r++){//print("rs.rows.item(r).k "+rs.rows.item(r).k +" rs.rows.item(r).v "+rs.rows.item(r).v)
if(rs.rows.item(r).k=="defaultNewsTypes"){
go[rs.rows.item(r).k]=JSON.parse(decodeURI(rs.rows.item(r).v))
} else{
go[rs.rows.item(r).k]=rs.rows.item(r).v
}
}
if (!go.hasOwnProperty('defaultNewsTypes')){
go.defaultNewsTypes=JSON.parse(decodeURI(defaultNewsTypes))
}
else if (!go.hasOwnProperty('newsViewType')){
go.newsViewType=newsViewType
}
callback(go)
})
}
function readGO(database){
var obj;
readGlobaloptions(database,function(go){obj=go});
return obj
}
function updateglobaloptions(database,key,value){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
if(key=='defaultNewsTypes'){var dbValue=encodeURI(JSON.stringify(value))} else {var dbValue=value}
db.transaction( function(tx) {
var result = tx.executeSql('SELECT * from globaloptions where k="'+key+'"'); // check for key
if(result.rows.length > 0) {// use update
result = tx.executeSql('UPDATE globaloptions SET v="'+dbValue+'" WHERE k="'+key+'"')
} else {// use insert
result = tx.executeSql('INSERT INTO globaloptions (k,v) VALUES (?,?)', [key,dbValue])
}
})
root.globaloptions[key]=value;
}
function deleteConfig(database,userobj,callback) { // delete user data from DB
if (userobj){var where = " WHERE username='"+ userobj.username+"' and server='"+userobj.server+"'";} else { return "no user selected!";}
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
if(!db) { return; }
db.transaction( function(tx) {
var rs1 = tx.executeSql('delete from config'+where);
var rs2 = tx.executeSql("delete from news WHERE username='"+ userobj.username+"'");
var rs3 = tx.executeSql("delete from contacts WHERE username='"+ userobj.username+"'");
var rs4 = tx.executeSql("delete from imageData WHERE username='"+ userobj.username+"'");
var rs5 = tx.executeSql("delete from groups WHERE username='"+ userobj.username+"'");
var rs6 = tx.executeSql("delete from events WHERE username='"+ userobj.username+"'");
callback();
})
}
//function updateNewsviewtype(database, newsViewtype){
// var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
// if(!db) { return; }
// db.transaction( function(tx) {
// var rs1 = tx.executeSql('INSERT INTO globaloptions (k,v) VALUES (?,?)', ["newsViewType",newsViewtype])
// var rs2 = tx.executeSql('UPDATE config SET newsViewType=""');
// })
//}
function cleanNews(database,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var maxnewsrs = tx.executeSql("SELECT v FROM globaloptions WHERE k='max_news'");
var maxnews=1000; if(maxnewsrs.rows.length>0){ maxnews=maxnewsrs.rows.item(0).v};
for (var i=0; i<6;i++){
if (i!=0){var maxnewsa=maxnews/5}else{maxnewsa=maxnews}
var newscountrs = tx.executeSql('SELECT COUNT(*) from news WHERE messagetype='+i);
var newscount = 0;
if (newscountrs.rows.length>0){newscount=newscountrs.rows.item(0)["COUNT(*)"]};
if (newscount>maxnewsa){
var lastvalidtimers= tx.executeSql('SELECT DISTINCT created_at FROM news WHERE messagetype='+i+' ORDER BY created_at ASC LIMIT ' +(newscount-maxnewsa));
var lastvalidtime=lastvalidtimers.rows.item(newscount-maxnewsa-1).created_at;
var deleters = tx.executeSql('DELETE from news WHERE messagetype='+i+' AND created_at<='+lastvalidtime)}
}
callback()
})
}
function cleanContacts(login,database,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var oldestnewsrs= tx.executeSql('SELECT created_at FROM news WHERE username="'+login.username+'" AND messagetype=0 ORDER BY created_at ASC LIMIT 1');
if (oldestnewsrs.rows.length>0){ var oldestnewsTime=oldestnewsrs.rows.item(0).created_at- 604800000;} else{var oldestnewsTime=0} //contacts can be 7 days old
var result = tx.executeSql('SELECT * from contacts WHERE username="'+login.username+'" AND isFriend=0 AND statusnet_blocking<>1 AND imageAge<'+oldestnewsTime); // check for friends
for (var i=0;i<result.rows.length;i++){
filesystem.rmFile(result.rows.item(i).profile_image);
var deleters = tx.executeSql('DELETE from contacts WHERE username="'+login.username+'" AND url="'+result.rows.item(i).url+'"');
}
callback()
})
}
function cleanHashtags(database,callback){
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var tagcountrs = tx.executeSql('SELECT COUNT(*) from hashtags');
var tagcount = 0;
if (tagcountrs.rows.length>0){tagcount=tagcountrs.rows.item(0)["COUNT(*)"]};
if (tagcount>50){
var lastvaliddaters= tx.executeSql('SELECT DISTINCT date FROM hashtags ORDER BY date ASC LIMIT ' +(tagcount-50));
var lastvaliddate=lastvaliddaters.rows.item(tagcount-49).date;
var deleters = tx.executeSql('DELETE from hashtags WHERE date<='+lastvaliddate)}
callback()
})
}
function updateContactInDB(login,database,isFriend,contact){// for newstab and friendstab
var currentTime=Date.now();
var image_timestamp=0;
var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
db.transaction( function(tx) {
var imagename_helper=[];
imagename_helper=contact.profile_image_url.split('?');
try {parseInt(image_timestamp=imagename_helper[1].substring(imagename_helper[1].indexOf("ts=")+3,imagename_helper[1].length))} catch(e){};
var result;
result = tx.executeSql('SELECT * from contacts where username="'+login.username+'" AND url = "'+contact.url+'"'); // check for news url
if(result.rows.length === 1) {// use update
result = tx.executeSql('UPDATE contacts SET id='+contact.id+', name="'+Qt.btoa(contact.name)+'", screen_name="'+contact.screen_name+'", location="'+contact.location+'",imageAge='+currentTime+', profile_image_url="'+contact.profile_image_url+'", description="'+Qt.btoa(contact.description)+'", protected="'+contact.protected+'", followers_count='+contact.followers_count+', friends_count='+contact.friends_count+', created_at="'+ contact.created_at+'", favourites_count="'+contact.favorites_count+'", utc_offset="'+contact.utc_offset+'", time_zone="'+contact.time_zone+'", statuses_count='+contact.statuses_count+', following="'+contact.following+'", verified ="'+contact.verified+'", statusnet_blocking="'+contact.statusnet_blocking+'", notifications="'+contact.notifictions+'", statusnet_profile_url="'+contact.statusnet_profile_url+'", cid='+contact.cid+', network="'+contact.network+'", isFriend='+isFriend+', timestamp='+ currentTime+' where username="'+login.username+'" AND url="'+contact.url+'"');
} else {// use insert
result = tx.executeSql('INSERT INTO contacts VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,contact.id,Qt.btoa(contact.name),contact.screen_name,contact.location,currentTime,contact.profile_image_url, Qt.btoa(contact.description),"",contact.url,contact.protected,contact.followers_count, contact.friends_count,contact.created_at,contact.favorites_count,contact.utc_offset,contact.time_zone,contact.statuses_count,contact.following,contact.verfied,contact.statusnet_blocking,contact.notifications,contact.statusnet_profile_url,contact.cid,contact.network,isFriend,image_timestamp]);}
});
}
function processNews(api,data){
try{var newslist=JSON.parse(data)} catch(e){print("processnews "+e+ " api "+ api + " data "+data)};
if (api=="/api/users/show"){
var usermessages=[];
usermessages.push(newslist.status);
newslist=usermessages;
}
if (data=="" || api=="/api/v1/statuses"){newsBusy.running=false}
else if (typeof(newslist)=='undefined'){
Helperjs.showMessage(qsTr("Undefined Array Error"),"API:\n" +login.server+api+"\n Return: \n"+data,root)
}
else if (newslist.hasOwnProperty('status')){
Helperjs.showMessage(qsTr("JSON status Error"),"API:\n" +login.server+api+"\n Return: \n"+data,root)
}
else {
var allcontacts=[];
allcontacts=Newsjs.getAllContacts(db,login.username);
if (!(Array.isArray(newslist)) && (typeof(newslist)==='object')){//answers return object, not array
newslist=[];newslist.push(JSON.parse(data));
}
if (api=="/api/direct_messages/all" || api=="/api/direct_messages/conversation" ||api=="/api/direct_messages/new"){
for (var n in newslist){
try{newslist[n].created_at=Date.parse(Newsjs.cleanDate(newslist[n].created_at));}catch(e){
newslist[n].created_at=Date.parse(newslist[n].created_at)
}
newslist[n].messagetype=1;
newslist[n].source=" Friendica";
newslist[n].status_id=newslist[n].id;
newslist[n].uid=newslist[n].sender.id;
newslist[n].statusnet_conversation_id=newslist[n].friendica_parent_uri;
newslist[n].user=cleanUser(newslist[n].sender);
newslist[n].friendica_owner=newslist[n].user;
newslist[n].friendica_author=newslist[n].user;
newslist[n].statusnet_html=newslist[n].text;
newslist[n].in_reply_to_user_id=newslist[n].recipient_id
if(newslist[n].in_reply_to_user_id){newslist[n].reply_user=Newsjs.objFromArray(allcontacts,"id",newslist[n].in_reply_to_user_id)}
}}
else if (api=="/api/friendica/notification"){
for (var n in newslist){
newslist[n].created_at=Date.parse(newslist[n].date);
newslist[n].messagetype=2;
newslist[n].user=Newsjs.objFromArray(allcontacts,"url",newslist[n].url)
if (newslist[n].user==false){
newslist[n].user={"profile_image_url": newslist[n].photo,"name": newslist[n].name," url":newslist[n].url, "created_at":newslist[n].date};
newslist[n].user=cleanUser(newslist[n].user);
}
newslist[n].friendica_author=newslist[n].user;
newslist[n].friendica_owner=newslist[n].user;
newslist[n].statusnet_html=newslist[n].msg_html;
newslist[n].text=newslist[n].msg;
}
}
else {//if(api!="/api/statuses/user_timeline"){
var chatlist=[];
var chatlistclean=[];
var conversationIds=[];
var commentCount=[];
for (var n in newslist){
if (newslist[n]!=null){
newslist[n].created_at=Date.parse(Newsjs.cleanDate(newslist[n].created_at));
newslist[n].messagetype=5;
newslist[n].status_id=newslist[n].id;
if (api=="/api/statuses/replies"){newslist[n].messagetype=3}else{newslist[n].messagetype=0;}
newslist[n].friendica_author=cleanUser(newslist[n].friendica_author);
newslist[n].user=cleanUser(newslist[n].user);
try{
let localContact=Newsjs.objFromArray(allcontacts,"id",newslist[n].user.id);
newslist[n].user.statusnet_blocking=localContact.statusnet_blocking
}catch(e){}
//if (newslist[n].friendica_title!="") {newslist[n].statusnet_html="<b>"+newslist[n].friendica_title +"</b><br><br>"+newslist[n].friendica_html;}
//else{ //friendica_title also included in html
newslist[n].statusnet_html=newslist[n].friendica_html
//}
if(newslist[n].in_reply_to_user_id){newslist[n].reply_user=Newsjs.objFromArray(allcontacts,"id",newslist[n].in_reply_to_user_id)}
if(newslist[n].hasOwnProperty('friendica_activities')){
for (var m in newslist[n].friendica_activities.like){
newslist[n].friendica_activities.like[m]=cleanUser(newslist[n].friendica_activities.like[m]);
}
for (var o in newslist[n].friendica_activities.dislike){
newslist[n].friendica_activities.dislike[o]=cleanUser(newslist[n].friendica_activities.dislike[o]);
}
for (var p in newslist[n].friendica_activities.attendyes){
newslist[n].friendica_activities.attendyes[p]=cleanUser(newslist[n].friendica_activities.attendyes[p]);
}
for (var q in newslist[n].friendica_activities.attendno){
newslist[n].friendica_activities.attendno[q]=cleanUser(newslist[n].friendica_activities.attendno[q]);
}
for (var r in newslist[n].friendica_activities.attendmaybe){
newslist[n].friendica_activities.attendmaybe[r]=cleanUser(newslist[n].friendica_activities.attendmaybe[r]);
}
}
if(!(newslist[n].hasOwnProperty('friendica_author'))){
newslist[n].friendica_author=newslist[n].user
}
var conversationindex=conversationIds.indexOf(newslist[n].statusnet_conversation_id);
//fill chatlist
if (conversationindex==-1){
let conversation=[];conversation.push(newslist[n]);
let firstmessage={currentconversation:conversation};
chatlist.push(firstmessage);
conversationIds.push(newslist[n].statusnet_conversation_id);
commentCount.push(1);
} else{
commentCount[conversationindex]=commentCount[conversationindex]+1;
chatlist[conversationindex].currentconversation.push(newslist[n]);
}
}
}
if ((root.globaloptions.hasOwnProperty("newsViewType") && (root.globaloptions.newsViewType=="Conversations"))&&!(api=="/api/conversation/show"|| api=="/api/direct_messages/conversation")){
//enrich chatlist with old entries
for (var count in chatlist){
chatlist[count].currentconversation.reverse();
if (chatlist[count].currentconversation[0].id_str!==chatlist[count].currentconversation[0].statusnet_conversation_id){
try{
Newsjs.oldchatfromdb(db,login.username,chatlist[count].currentconversation[0].statusnet_conversation_id,chatlist[count].currentconversation[0].id,allcontacts,function(oldpost,oldcount){
let completeChat=oldpost.currentconversation.concat(chatlist[count].currentconversation);
let newChat=completeChat[0];
newChat.currentconversation=[];
for (let c in completeChat){
if (completeChat[c].status_id!=newChat.status_id){
newChat["currentconversation"].push(completeChat[c])
}
}
newChat.newscount=oldcount+commentCount[count];
chatlistclean.push(newChat);
})
}catch(e){print(e)}
}
else{
let newChat=chatlist[count].currentconversation[0];
newChat["currentconversation"]=[];
for (let c in chatlist[count].currentconversation){
if (chatlist[count].currentconversation[c].status_id!=newChat.status_id){
newChat["currentconversation"].push(chatlist[count].currentconversation[c])
}
}
newChat.newscount=commentCount[count];
chatlistclean.push(newChat);
}
}
}
}
if (api=="/api/conversation/show"|| api=="/api/direct_messages/conversation"){
newslist.reverse();
root.conversation=newslist
}
else if (api=="/api/statuses/user_timeline" || api=="/api/users/show"){
root.contactposts=newslist
}
else if ((api!="/api/direct_messages/all")&&(api!="/api/friendica/notification")&&(api!="/api/direct_messages/new")&&(newstab.newstabstatus==="Conversations")){
showNews(chatlistclean);root.news=newslist
}
else {
showNews(newslist);root.news=newslist
};
//var newstabarray=["Conversations","Favorites","Timeline","DirectMessage","Replies"];
//if (newstabarray.indexOf(newstab.newstabstatus)>-1){
contacttimer.start()
//}
}
}
function cleanUser(user){
user.created_at=Date.parse(Newsjs.cleanDate(user.created_at));
var imagehelper1=user.profile_image_url.split("?");
var imagehelper2=imagehelper1[0].substring(imagehelper1[0].lastIndexOf("/")+1,imagehelper1[0].length);
var imagehelper3=login.imagestore+"contacts/"+user.screen_name+"-"+imagehelper2
if(filesystem.fileexist(imagehelper3)){user.profile_image=imagehelper3}else {user.profile_image=""}
return user
}
function updateView(viewtype,lastnews=0){
//messageSend.state="";
//newsBusy.running=true;
//downloadNotice.text="xhr start "+Date.now()
if (viewtype==="Conversations"){Newsjs.allchatsfromdb(db,login.username,function(temp){
newsStack.allchats=temp
})}
if ((osSettings.osType=="Android") && root.globaloptions.hasOwnProperty("syncinterval") && root.globaloptions.syncinterval !=null && root.globaloptions.syncinterval !=0){
//alarm.setAlarm(root.globaloptions.syncinterval);
setBackgroundsync()
}
xhr.setAccount(login);
switch(viewtype){
case "Conversations":
xhr.setApi("/api/statuses/friends_timeline");
xhr.setParam("since_id",lastnews);
xhr.setParam("count",50);
break;
case "Timeline":
xhr.setApi("/api/statuses/friends_timeline");
xhr.setParam("since_id",lastnews);
xhr.setParam("count",50)
break;
case "Search":
xhr.setApi("/api/search");
break;
case "Notifications":
xhr.setApi("/api/friendica/notification");
break;
case "Direct Messages":
xhr.setApi("/api/direct_messages/all");
break;
case "Public Timeline":
xhr.setApi("/api/statuses/public_timeline");
break;
case "Favorites":
xhr.setApi("/api/favorites");
break;
case "Replies":
xhr.setApi("/api/statuses/replies");
break;
default:
xhr.setApi("/api/statuses/friends_timeline");
xhr.setParam("since_id",lastnews);
xhr.setParam("count",50)
newstab.newstabstatus="Conversations";
}
xhr.get();
}
function showGroups(){
Helperjs.readData(db,"groups",login.username,function(groups){
var groupitems="";
for (var i=0;i<groups.length;i++){
groupitems=groupitems+"MenuItem{text:'"+groups[i].groupname+"'; onTriggered: {print('getGroupnews');Service.getGroupnews("+groups[i].gid+")}}"
}
var menuString="import QtQuick.Controls 6.3; import 'qrc:/js/service.js' as Service; Menu {"+groupitems+"}";
var grouplistObject=Qt.createQmlObject(menuString,newstabitem,"groupmenuOutput");
grouplistObject.popup()
})
}
function setBackgroundsync(){
Helperjs.readData(db,"globaloptions","",function(lastsync){
if((lastsync.length>0)&&((parseFloat(lastsync[0]["v"])+120)<(Date.now()/1000))){
alarm.setAlarm(root.globaloptions.syncinterval);
}
},"k","lastsync")
}
function getGroupnews(list){print("Liste "+list);
newstab.newstabstatus="Group news";
newsBusy.running=true;
xhr.setAccount(login);
xhr.setApi("/api/lists/statuses");
xhr.setParam("list_id",list)
xhr.get();
}

698
src/js/smiley.js Normal file
View file

@ -0,0 +1,698 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
var html=[//Smileys
'\u263A',
'\ud83d\ude00',
'\ud83d\ude02',
'\ud83d\ude06',
'\ud83d\ude07',
'\ud83d\ude08',
'\ud83d\ude09',
'\ud83d\ude0B',
'\ud83d\ude0D',
'\ud83d\ude0E',
'\ud83d\ude0F',
'\ud83d\ude10',
'\ud83d\ude15',
'\ud83d\ude18',
'\ud83d\ude1B',
'\ud83d\ude1C',
'\ud83d\ude1D',
'\ud83d\ude1E',
'\ud83d\ude1F',
'\ud83d\ude20',
'\ud83d\ude21',
'\ud83d\ude22',
'\ud83d\ude23',
'\ud83d\ude24',
'\ud83d\ude26',
'\ud83d\ude27',
'\ud83d\ude2C',
'\ud83d\ude2D',
'\ud83d\ude2E',
'\ud83d\ude2F',
'\ud83d\ude31',
'\ud83d\ude32',
'\ud83d\ude33',
'\ud83d\ude31',
'\ud83d\ude32',
'\ud83d\ude33',
'\ud83d\ude34',
'\ud83d\ude37',
'\ud83d\ude41',
'\ud83d\ude42',
'\ud83d\ude43',
'\ud83d\ude44',
'\ud83d\ude45',
'\ud83d\ude46',
'\ud83d\ude47',
'\ud83d\ude4B',
'\ud83d\ude4C',
'\ud83d\ude4D',
'\ud83d\ude4E',
'\ud83d\ude4F',
'\ud83e\udd2F',
'\uD83E\uDD17',
'\uD83E\uDD14',
'\uD83E\uDD73',
'\uD83E\uDD21',
'\uD83D\uDC4D',
'\uD83D\uDC94',
'\u2764\uFE0F',
'\uD83D\uDCA9',
'\uD83D\uDC2D',
'\uD83D\uDC2E',
'\uD83D\uDC31',
'\uD83D\uDCAF',
'\uD83C\uDF82',
'\uD83C\uDF89',
'\uD83C\uDF81',
'\uD83C\uDF08',
'\uD83D\uDC80',
'\u2639',
'\u263B',
//Weather
'\u2600',
'\u2601',
'\u263C',
'\u2614',
'\u2602',
'\u2603',
'\u2604',
'\u26C4',
'\u26C5',
'\u26C8',
//Leisure
'\u2615',
'\u26BD',
'\u26BE',
'\u26F1',
'\u26F2',
'\u26F3',
'\u26F4',
'\u26F5',
'\u26F7',
'\u26F8',
'\u26F9',
'\u26FA',
'\u26FD',
//Hand
'\u261C',
'\u261D',
'\u261E',
'\u261F',
'\u2620',
'\u2622',
'\u2623',
//Religion
'\u2626',
'\u262A',
'\u262C',
'\u262E',
'\u262F',
'\u26EA',
'\u26E9'
]
var core=[
{name:'&lt;3',url:
'qrc:///assets/smileys/core/smiley-heart.gif'},
{name:'&lt;/3',url:
'qrc:///assets/smileys/core/smiley-brokenheart.gif'},
{name:':-)',url:
'qrc:///assets/smileys/core/smiley-smile.gif'},
{name:';-)',url:
'qrc:///assets/smileys/core/smiley-wink.gif'},
{name:':-(',url:
'qrc:///assets/smileys/core/smiley-frown.gif'},
{name:':-P',url:
'qrc:///assets/smileys/core/smiley-tongue-out.gif'},
{name:':-X',url:
'qrc:///assets/smileys/core/smiley-kiss.gif'},
{name:':-D',url:
'qrc:///assets/smileys/core/smiley-laughing.gif'},
{name:':-O',url:
'qrc:///assets/smileys/core/smiley-surprised.gif'},
{name:'\\o/',url:
'qrc:///assets/smileys/core/smiley-thumbsup.gif'},
{name:'o.O',url:
'qrc:///assets/smileys/core/smiley-Oo.gif'},
{name:":'(",url:
'qrc:///assets/smileys/core/smiley-cry.gif'},
{name:":-!",url:
'qrc:///assets/smileys/core/smiley-foot-in-mouth.gif'},
{name:":-/",url:
'qrc:///assets/smileys/core/smiley-undecided.gif'},
{name:":-[",url:
'qrc:///assets/smileys/core/smiley-embarassed.gif'},
{name:"8-)",url:
'qrc:///assets/smileys/core/smiley-cool.gif'},
{name:':beer',url:
'qrc:///assets/smileys/core/beer_mug.gif'},
{name:':coffee',url:
'qrc:///assets/smileys/core/coffee.gif'},
{name:':facepalm',url:
'qrc:///assets/smileys/core/smiley-facepalm.gif'},
{name:':like',url:
'qrc:///assets/smileys/core/like.gif'},
{name:':dislike',url:
'qrc:///assets/smileys/core/dislike.gif'},
{name:'~friendica',url:
'qrc:///assets/smileys/core/friendica-16.png'},
{name:'red#',url:
'qrc:///assets/smileys/core/rm-16.png'}
]
var addon=[
{name:':bunnyflowers',url:
'qrc:///assets/smileys/animals/bunnyflowers.gif'},
{name:':chick',url:
'qrc:///assets/smileys/animals/chick.gif'},
{name:':bumblebee',url:
'qrc:///assets/smileys/animals/bee.gif'},
{name:':ladybird',url:
'qrc:///assets/smileys/animals/ladybird.gif'},
{name:':bigspider',url:
'qrc:///assets/smileys/animals/bigspider.gif' },
{name:':cat',url:
'qrc:///assets/smileys/animals/cat.gif'},
{name:':bunny',url:
'qrc:///assets/smileys/animals/bunny.gif' },
{name:':cow',url:
'qrc:///assets/smileys/animals/cow.gif' },
{name:':crab',url:
'qrc:///assets/smileys/animals/crab.gif' },
{name:':dolphin',url:
'qrc:///assets/smileys/animals/dolphin.gif' },
{name:':dragonfly',url:
'qrc:///assets/smileys/animals/dragonfly.gif' },
{name:':frog',url:
'qrc:///assets/smileys/animals/frog.gif'},
{name:':hamster',url:
'qrc:///assets/smileys/animals/hamster.gif' },
{name:':monkey',url:
'qrc:///assets/smileys/animals/monkey.gif' },
{name:':horse',url:
'qrc:///assets/smileys/animals/horse.gif' },
{name:':parrot',url:
'qrc:///assets/smileys/animals/parrot.gif' },
{name:':tux',url:
'qrc:///assets/smileys/animals/tux.gif' },
{name:':snail',url:
'qrc:///assets/smileys/animals/snail.gif' },
{name:':sheep',url:
'qrc:///assets/smileys/animals/sheep.gif' },
{name:':dog',url:
'qrc:///assets/smileys/animals/dog.gif'},
{name:':elephant',url:
'qrc:///assets/smileys/animals/elephant.gif' },
{name:':fish',url:
'qrc:///assets/smileys/animals/fish.gif' },
{name:':giraffe',url:
'qrc:///assets/smileys/animals/giraffe.gif' },
{name:':pig',url:
'qrc:///assets/smileys/animals/pig.gif'},
//Baby
{name:':baby',url:
'qrc:///assets/smileys/babies/baby.gif' },
{name:':babycot',url:
'qrc:///assets/smileys/babies/babycot.gif' },
{name:':pregnant',url:
'qrc:///assets/smileys/babies/pregnant.gif' },
{name:':stork',url:
'qrc:///assets/smileys/babies/stork.gif' },
//Confused
{name:':confused',url:
'qrc:///assets/smileys/confused/confused.gif' },
{name:':shrug',url:
'qrc:///assets/smileys/confused/shrug.gif' },
{name:':stupid',url:
'qrc:///assets/smileys/confused/stupid.gif' },
{name:':dazed',url:
'qrc:///assets/smileys/confused/dazed.gif' },
//Cool 'qrc:///assets/smileys
{name:':affro',url:
'qrc:///assets/smileys/cool/affro.gif'},
//Devil/Angel
{name:':angel',url:
'qrc:///assets/smileys/devilangel/angel.gif'},
{name:':cherub',url:
'qrc:///assets/smileys/devilangel/cherub.gif'},
{name:':devilangel',url:
'qrc:///assets/smileys/devilangel/blondedevil.gif' },
{name:':catdevil',url:
'qrc:///assets/smileys/devilangel/catdevil.gif'},
{name:':devillish',url:
'qrc:///assets/smileys/devilangel/devil.gif'},
{name:':daseesaw',url:
'qrc:///assets/smileys/devilangel/daseesaw.gif'},
{name:':turnevil',url:
'qrc:///assets/smileys/devilangel/turnevil.gif' },
{name:':saint',url:
'qrc:///assets/smileys/devilangel/saint.gif'},
{name:':graveside',url:
'qrc:///assets/smileys/devilangel/graveside.gif'},
//Unpleasent
{name:':toilet',url:
'qrc:///assets/smileys/disgust/toilet.gif'},
{name:':fartinbed',url:
'qrc:///assets/smileys/disgust/fartinbed.gif' },
{name:':fartblush',url:
'qrc:///assets/smileys/disgust/fartblush.gif' },
//Drinks
{name:':tea',url:
'qrc:///assets/smileys/drink/tea.gif' },
{name:':drool',url:
'qrc:///assets/smileys/drool/drool.gif'},
//Sad
{name:':crying',url:
'qrc:///assets/smileys/sad/crying.png'},
{name:':prisoner',url:
'qrc:///assets/smileys/sad/prisoner.gif' },
{name:':sigh',url:
'qrc:///assets/smileys/sad/sigh.gif'},
//Smoking - only one smiley in here, maybe it needs moving elsewhere?
{name:':smoking',url:
'qrc:///assets/smileys/smoking/smoking.gif'},
//Sport
{name:':basketball',url:
'qrc:///assets/smileys/sport/basketball.gif'},
{name:':bowling',url:
'qrc:///assets/smileys/sport/bowling.gif'},
{name:':cycling',url:
'qrc:///assets/smileys/sport/cycling.gif'},
{name:':darts',url:
'qrc:///assets/smileys/sport/darts.gif'},
{name:':fencing',url:
'qrc:///assets/smileys/sport/fencing.gif' },
{name:':juggling',url:
'qrc:///assets/smileys/sport/juggling.gif'},
{name:':skipping',url:
'qrc:///assets/smileys/sport/skipping.gif'},
{name:':archery',url:
'qrc:///assets/smileys/sport/archery.gif'},
{name:':surfing',url:
'qrc:///assets/smileys/sport/surfing.gif' },
{name:':snooker',url:
'qrc:///assets/smileys/sport/snooker.gif' },
{name:':horseriding',url:
'qrc:///assets/smileys/sport/horseriding.gif'},
//Love
{name:':iloveyou',url:
'qrc:///assets/smileys/love/iloveyou.gif'},
{name:':inlove',url:
'qrc:///assets/smileys/love/inlove.gif'},
{name:':~love',url:
'qrc:///assets/smileys/love/love.gif' },
{name:':lovebear',url:
'qrc:///assets/smileys/love/lovebear.gif'},
{name:':lovebed',url:
'qrc:///assets/smileys/love/lovebed.gif' },
{name:':loveheart',url:
'qrc:///assets/smileys/love/loveheart.gif' },
//Tired/Sleep
{name:':countsheep',url:
'qrc:///assets/smileys/tired/countsheep.gif' },
{name:':hammock',url:
'qrc:///assets/smileys/tired/hammock.gif'},
{name:':pillow',url:
'qrc:///assets/smileys/tired/pillow.gif' },
{name:':yawn',url:
'qrc:///assets/smileys/tired/yawn.gif'},
//Fight/Flame/Violent
{name:':2guns',url:
'qrc:///assets/smileys/fight/2guns.gif' },
{name:':alienfight',url:
'qrc:///assets/smileys/fight/alienfight.gif' },
{name:':army',url:
'qrc:///assets/smileys/fight/army.gif'},
{name:':arrowhead',url:
'qrc:///assets/smileys/fight/arrowhead.gif'},
{name:':bfg',url:
'qrc:///assets/smileys/fight/bfg.gif' },
{name:':bowman',url:
'qrc:///assets/smileys/fight/bowman.gif' },
{name:':chainsaw',url:
'qrc:///assets/smileys/fight/chainsaw.gif'},
{name:':crossbow',url:
'qrc:///assets/smileys/fight/crossbow.gif'},
{name:':crusader',url:
'qrc:///assets/smileys/fight/crusader.gif' },
{name:':dead',url:
'qrc:///assets/smileys/fight/dead.gif' },
{name:':hammersplat',url:
'qrc:///assets/smileys/fight/hammersplat.gif' },
{name:':lasergun',url:
'qrc:///assets/smileys/fight/lasergun.gif' },
{name:':machinegun',url:
'qrc:///assets/smileys/fight/machinegun.gif' },
{name:':acid',url:
'qrc:///assets/smileys/fight/acid.gif' },
//Fantasy - monsters and dragons fantasy. The other type of fantasy belongs in adult
{name:':alienmonster',url:
'qrc:///assets/smileys/fantasy/alienmonster.gif' },
{name:':barbarian',url:
'qrc:///assets/smileys/fantasy/barbarian.gif' },
{name:':dinosaur',url:
'qrc:///assets/smileys/fantasy/dinosaur.gif'},
{name:':dragon',url:
'qrc:///assets/smileys/fantasy/dragon.gif'},
{name:':draco',url:
'qrc:///assets/smileys/fantasy/dragonwhelp.gif'},
{name:':ghost',url:
'qrc:///assets/smileys/fantasy/ghost.gif'},
{name:':mummy',url:
'qrc:///assets/smileys/fantasy/mummy.gif'},
//Food
{name:':apple',url:
'qrc:///assets/smileys/food/apple.gif' },
{name:':broccoli',url:
'qrc:///assets/smileys/food/broccoli.gif' },
{name:':cake',url:
'qrc:///assets/smileys/food/cake.gif'},
{name:':carrot',url:
'qrc:///assets/smileys/food/carrot.gif' },
{name:':popcorn',url:
'qrc:///assets/smileys/food/popcorn.gif'},
{name:':tomato',url:
'qrc:///assets/smileys/food/tomato.gif'},
{name:':banana',url:
'qrc:///assets/smileys/food/banana.gif'},
{name:':cooking',url:
'qrc:///assets/smileys/food/cooking.gif'},
{name:':fryegg',url:
'qrc:///assets/smileys/food/fryegg.gif'},
{name:':birthdaycake',url:
'qrc:///assets/smileys/food/birthdaycake.gif'},
//Happy
{name:':cloud9',url:
'qrc:///assets/smileys/happy/cloud9.gif'},
{name:':tearsofjoy',url:
'qrc:///assets/smileys/happy/tearsofjoy.gif' },
//Repsect
{name:':bow',url:
'qrc:///assets/smileys/respect/bow.gif'},
{name:':bravo',url:
'qrc:///assets/smileys/respect/bravo.gif'},
{name:':hailking',url:
'qrc:///assets/smileys/respect/hailking.gif'},
{name:':number1',url:
'qrc:///assets/smileys/respect/number1.gif' },
//Laugh
{name:':hahaha',url:
'qrc:///assets/smileys/laugh/hahaha.gif'},
{name:':loltv',url:
'qrc:///assets/smileys/laugh/loltv.gif' },
{name:':rofl',url:
'qrc:///assets/smileys/laugh/rofl.gif'},
//Music
{name:':drums',url:
'qrc:///assets/smileys/music/drums.gif'},
{name:':guitar',url:
'qrc:///assets/smileys/music/guitar.gif'},
{name:':trumpet',url:
'qrc:///assets/smileys/music/trumpet.gif' },
//smileys that used to be in core
{name:':headbang',url:
'qrc:///assets/smileys/oldcore/headbang.gif'},
{name:':beard',url:
'qrc:///assets/smileys/oldcore/beard.png'},
{name:':whitebeard',url:
'qrc:///assets/smileys/oldcore/whitebeard.png'},
{name:':shaka',url:
'qrc:///assets/smileys/oldcore/shaka.gif'},
{name:':\\.../',url:
'qrc:///assets/smileys/oldcore/shaka.gif'},
{name:':\\ooo/',url:
'qrc:///assets/smileys/oldcore/shaka.gif' },
{name:':headdesk',url:
'qrc:///assets/smileys/oldcore/headbang.gif' },
//These two are still in core, so oldcore isn't strictly right, but we don't want too many directories
{name:':-d',url:
'qrc:///assets/smileys/oldcore/laughing.gif'},
{name:':-o',url:
'qrc:///assets/smileys/oldcore/surprised.gif' },
// Regex killers - stick these at the bottom so they appear at the end of the English and
// at the start of $OtherLanguage.
{name:':cool',url:
'qrc:///assets/smileys/cool/cool.gif' },
{name:':vomit',url:
'qrc:///assets/smileys/disgust/vomit.gif' },
{name:':golf',url:
'qrc:///assets/smileys/sport/golf.gif' },
{name:':football',url:
'qrc:///assets/smileys/sport/football.gif'},
{name:':tennis',url:
'qrc:///assets/smileys/sport/tennis.gif' },
{name:':alpha',url:
'qrc:///assets/smileys/fight/alpha.png' },
{name:':marine',url:
'qrc:///assets/smileys/fight/marine.gif' },
{name:':sabre',url:
'qrc:///assets/smileys/fight/sabre.gif' },
{name:':tank',url:
'qrc:///assets/smileys/fight/tank.gif' },
{name:':viking',url:
'qrc:///assets/smileys/fight/viking.gif' },
{name:':gangs',url:
'qrc:///assets/smileys/fight/gangs.gif' },
{name:':dj',url:
'qrc:///assets/smileys/music/dj.gif'},
{name:':elvis',url:
'qrc:///assets/smileys/music/elvis.gif'},
{name:':violin',url:
'qrc:///assets/smileys/music/violin.gif'},
]
var adult=[
{
name:'(o)(o) ',url:
'qrc:///assets/smileys/adult/tits.gif'},
{name:'(.)(.) ',url:
'qrc:///assets/smileys/adult/tits.gif'},
{name:':bong',url:
'qrc:///assets/smileys/adult/bong.gif'},
{name:':sperm',url:
'qrc:///assets/smileys/adult/sperm.gif'},
{name:':drunk',url:
'qrc:///assets/smileys/adult/drunk.gif'},
{name:':finger',url:
'qrc:///assets/smileys/adult/finger.gif'}
]

View file

@ -0,0 +1,73 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
Item {
id: calendarDay
width: root.fontFactor*osSettings.bigFontSize*2
height: root.fontFactor*osSettings.bigFontSize*2
property int dateInt: Math.floor(model.date.valueOf()/86400000)
Rectangle {
id: placeHolder
color: model.today?'lightblue':'transparent';
border.color: 'lightblue'
border.width: 2
antialiasing: true
anchors.fill:parent
radius: 0.5*mm
}
Text {
id:daytext
anchors.right: parent.right
anchors.margins: 0.5*mm
color:(model.month==monthgrid.month)?osSettings.primaryTextColor:osSettings.secondaryTextColor
wrapMode: Text.WrapAnywhere
text: model.day
font.bold: model.today
font.pointSize: 1.2*osSettings.systemFontSize
}
Rectangle {
id:eventRect
color:"grey"
anchors.margins: 0.5*mm
anchors.bottom: calendarDay.bottom
width: parent.width-mm
height: 0.5*osSettings.systemFontSize//mm
visible: eventdays.indexOf(dateInt)>-1
}
MouseArea {
anchors.fill: calendarDay
onClicked: {
rootstackView.push("qrc:/qml/calendarqml/EventList.qml",{"dayint": dateInt,"events":events});
}
}
}

View file

@ -0,0 +1,301 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
import QtQuick.Controls 6.3
//import Qt.labs.calendar 1.0
//import QtQuick.Layouts 1.3
import "qrc:/js/service.js" as Service
import "qrc:/js/helper.js" as Helperjs
import "qrc:/qml/calendarqml"
import "qrc:/qml/genericqml"
Rectangle {
id:calendarrectangle
// y:1
width:parent.width
height:parent.height
color: osSettings.backgroundColor
property date currentTime: new Date()
property int offsetTime: currentTime.getTimezoneOffset() * 60 * 1000
property var events:[]
property var eventdays:[]
function showEvents(friend){
if(friend=="backButton"){Service.eventsfromdb(db,login.username,function(eventArray,dayArray){
events=cleanEvents(eventArray);
eventdays=dayArray})
}
else if (friend!=""){
calendartab.calendartabstatus=friend.url.substring(friend.url.lastIndexOf("/")+1,friend.url.length)
Service.newRequestFriendsEvents(login,friend,calendartab,function(eventArray,dayArray){
events=cleanEvents(eventArray);
eventdays=dayArray})
}
else {calendartab.calendartabstatus="Events";
Service.eventsfromdb(db,login.username,function(eventArray,dayArray){
events=cleanEvents(eventArray);
eventdays=dayArray;
calBusy.running=false
var currentevents=events;
currentevents=events.filter(event=>(currentTime<=event.end));
for (var i=0; i<Math.min(5,currentevents.length);i++){
var liststate="";
eventModel.append({"event":currentevents[i],"eventstatus":liststate});
}
})
}
}
function cleanEvents(events){
for (var item in events){
events[item].start=events[item].start-offsetTime;
if(events[item].end>0){events[item].end=events[item].end-offsetTime};
}
return events
}
function createEvent(event){
rootstackView.push("qrc:/qml/calendarqml/EventCreate.qml",{"eventInformation": event})
}
BusyIndicator{
id: calBusy
anchors.horizontalCenter: calendarView.horizontalCenter
anchors.top:calendarView.top
anchors.topMargin: 2*mm
width:10*mm
height: 10*mm
running: false
}
ToolButton{
z:2
id:menuButton
anchors.right: parent.right
anchors.top: parent.top
//anchors.topMargin: friendsbar.height
visible: !wideScreen
icon.name: "application-menu"
icon.source: "qrc:/assets/icons/bars.svg"
icon.width: root.fontFactor*osSettings.bigFontSize
icon.height: root.fontFactor*osSettings.bigFontSize
onClicked:{
leftDrawerAndroid.visible?leftDrawerAndroid.close():leftDrawerAndroid.open()}
}
LeftDrawerLinux{
id:leftDrawer
property var newstabstatus: newstab.newstabstatus
visible: wideScreen&&rootstackView.depth<2
width: visible?root.fontFactor*osSettings.systemFontSize*15:0
height: root.height-bar.height
}
LeftDrawerAndroid{
id: leftDrawerAndroid
}
MButton{
id: updateEvents
anchors.top: parent.top
anchors.topMargin: 0.5*mm
anchors.right:calendartabstatusButton.left
anchors.rightMargin:mm
display: AbstractButton.IconOnly
text: qsTr("refresh")//"\uf021"
icon.name: "view-refresh-symbolic"
icon.source: "qrc:/assets/icons/refresh.svg"
// background: Rectangle{color: osSettings.dialogColor;
// radius: 0.5*mm}
onClicked: {
calBusy.running=true;
updatenews.setDatabase();
updatenews.login();
updatenews.setSyncAll(false);
updatenews.events();
}}
Connections{
target: updatenews
function onSuccess(api){
calBusy.running=false;
showEvents("")
}
}
Dialog {
id: deleteDialog
anchors.centerIn: parent
property int eventid:0
title: qsTr("Delete Event?")
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
onAccepted: {
xhr.setAccount(login);
xhr.setApi("/api/friendica/event_delete");
xhr.setParam("id",eventid);
xhr.post();
}
onRejected: {close()}
}
MButton{
id: createNewEvent
anchors.top: parent.top
anchors.topMargin: 0.5*mm
anchors.right:updateEvents.left
anchors.rightMargin:mm
width: 2*root.fontFactor*osSettings.bigFontSize;
display: AbstractButton.IconOnly
text: qsTr("add")
icon.name: "list-add"
icon.source: "qrc:/assets/icons/plus.svg"
// background: Rectangle{color: osSettings.dialogColor;
// radius: 0.5*mm}
onClicked: {
rootstackView.push("qrc:/qml/calendarqml/EventCreate.qml")
}
}
Connections{
target: xhr
function onSuccess(text,api){
if(api=="/api/friendica/event_create"){
calBusy.running=true;
updatenews.setDatabase();
updatenews.login();
updatenews.setSyncAll(false);
updatenews.events();
}
}
}
MButton{
id: calendartabstatusButton
anchors.top: parent.top
anchors.topMargin: 0.5*mm
anchors.right: parent.right
anchors.rightMargin:menuButton.width + mm
width: Math.max(6*root.fontFactor*osSettings.bigFontSize,implicitWidth)
text: calendartab.calendartabstatus=="Events"?qsTr("Events"):calendartabstatus
Menu {
id:calendartabmenu
width: 20*root.fontFactor*osSettings.systemFontSize
MenuItem {
text: qsTr("Own Calendar")
font.pointSize: osSettings.systemFontSize
onTriggered: {
calendartab.calendartabstatus="Events";
// calendartabstatusButton.text=qsTr("own Calendar");
showEvents("")}
}
}
onClicked: {calendartabmenu.popup()}
}
ListView{
id: calendarView
y:2*root.fontFactor*osSettings.bigFontSize//8*mm
width: wideScreen&&rootstackView.depth<2?parent.width-leftDrawer.width-mm:parent.width-mm//newstabitem.width/3*2:newstabitem.width
x: leftDrawer.width
height: 18*root.fontFactor*osSettings.bigFontSize //parent.height-9*mm
clip: true
snapMode: ListView.SnapOneItem
orientation: ListView.Horizontal
highlightRangeMode: ListView.StrictlyEnforceRange
model: CalendarModel {id:calendarModel
from: new Date()
to: new Date(new Date().valueOf()+93312000000)
}
delegate:
Item{
width:Math.min(23*root.fontFactor*osSettings.bigFontSize,calendarView.width)
height: calendarView.height
Text{
font.bold: true
width: parent.width-root.fontFactor*osSettings.bigFontSize
horizontalAlignment:Text.AlignHCenter
color: osSettings.primaryTextColor
text: model.year
font.pointSize: osSettings.systemFontSize
}
Text{y:1.5*root.fontFactor*osSettings.bigFontSize
width: parent.width-osSettings.bigFontSize
text: Qt.locale().standaloneMonthName(model.month)
color: osSettings.primaryTextColor
horizontalAlignment:Text.AlignHCenter
font.pointSize: osSettings.systemFontSize
}
DayOfWeekRow{y:3*root.fontFactor*osSettings.bigFontSize
width: parent.width-root.fontFactor*osSettings.bigFontSize
locale: monthgrid.locale
font.pointSize: osSettings.systemFontSize
}
MonthGrid {y:5*root.fontFactor*osSettings.bigFontSize
id: monthgrid
height: parent.height-5*root.fontFactor*osSettings.bigFontSize
width: parent.width-root.fontFactor*osSettings.bigFontSize
month: model.month
year: model.year
locale: Qt.locale()
delegate: CalendarDay{}
}
}
ScrollIndicator.horizontal: ScrollIndicator { }
Component.onCompleted: positionViewAtBeginning()
}
ListView {
id: eventlistView
y:20*root.fontFactor*osSettings.bigFontSize
x: leftDrawer.width
height: parent.height-20*root.fontFactor*osSettings.bigFontSize
width: wideScreen&&rootstackView.depth<2?parent.width-leftDrawer.width-mm:parent.width-mm
clip: true
model: eventModel
delegate: EventListItem{}
}
ListModel{
id: eventModel
}
Component.onCompleted: {
root.eventcreateSignal.connect(createEvent);
root.eventSignal.connect(showEvents);
if (calendartab.calendartabstatus=="Events"){showEvents("")}
}
}

View file

@ -0,0 +1,471 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
import QtQuick.Controls 6.3
//import QtQuick.Controls 1.4 as Oldcontrols
import "qrc:/js/service.js" as Service
import "qrc:/js/helper.js" as Helperjs
import "qrc:/qml/genericqml"
import "qrc:/qml/calendarqml"
Flickable{
id:eventCreateBox
property date startDate: new Date()
property var eventInformation: ({})
//anchors.fill: parent
contentWidth: eventRect.width; contentHeight: eventRect.height
function formatText(count, modelData) {
var data = count === 12 ? modelData + 1 : modelData;
return data.toString().length < 2 ? "0" + data : data;
}
boundsBehavior:Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar { }
Rectangle{
id: eventRect
width: root.width
height: textColumn.height + 6*root.fontFactor*osSettings.bigFontSize
color: osSettings.backgroundColor
MButton{
id:closeButton
anchors.top: parent.top
anchors.topMargin: 1*mm
anchors.right: parent.right
anchors.rightMargin: 1*mm
text: qsTr("Close")
display: AbstractButton.IconOnly
icon.name: "dialog-close"
icon.source: "qrc:/assets/icons/times-circle.svg"
onClicked:{rootstackView.pop()}
}
Label{
x: 0.5*root.fontFactor*osSettings.bigFontSize
y: 2*root.fontFactor*osSettings.bigFontSize
width: 3*root.fontFactor*osSettings.bigFontSize
height: root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
//verticalAlignment: TextInput.AlignBottom
color: osSettings.primaryTextColor
text:qsTr("Start")
}
TextField {
id: textStartDate
property string dateDay:(startDate.getDate()).toString().length<2?"0"+(startDate.getDate()):(startDate.getDate())
property string dateMonth: (startDate.getMonth()+1).toString().length<2?"0"+(startDate.getMonth()+1):(startDate.getMonth()+1)
x: 4*root.fontFactor*osSettings.bigFontSize
y: root.fontFactor*osSettings.bigFontSize
width: 5*root.fontFactor*osSettings.bigFontSize
height: 2.5*root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
horizontalAlignment: TextInput.AlignRight
text: dateDay+"-"+dateMonth+"-"+startDate.getFullYear()
inputMask: "99-99-9999"
validator: RegularExpressionValidator{regularExpression: /^([0-2\s]?[0-9\s]|3[0-1\s])-(0[0-9\s]|1[0-2\s])-([0-9\s][0-9\s][0-9\s][0-9\s])$ / }
font.bold: true
}
MButton {
id: textStartDateDropdown
x: 9.5*root.fontFactor*osSettings.bigFontSize
y: root.fontFactor*osSettings.bigFontSize
width: 2*root.fontFactor*osSettings.bigFontSize
height: 2*root.fontFactor*osSettings.bigFontSize
text: qsTr("Start date")
display: AbstractButton.IconOnly
icon.name: "expand"
icon.source: "qrc:/assets/icons/caret-down.svg"
onClicked:{
cal.visible=true;
cal.curSelection="start"
}
}
TextField {
id: textStartTime
x: 13*root.fontFactor*osSettings.bigFontSize
y: root.fontFactor*osSettings.bigFontSize
width: 3*root.fontFactor*osSettings.bigFontSize
height: 2.5*root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
inputMask: "99:99"
text: "00:00"
horizontalAlignment: TextInput.AlignRight
validator: RegularExpressionValidator{regularExpression: /^([0-1\s]?[0-9\s]|2[0-3\s]):([0-5\s][0-9\s])$ / }
font.bold: true
}
MButton {
id: textStartTimeDropdown
x: 16.5*root.fontFactor*osSettings.bigFontSize
y: root.fontFactor*osSettings.bigFontSize
width: 2*root.fontFactor*osSettings.bigFontSize
height: 2*root.fontFactor*osSettings.bigFontSize
text: qsTr("Start time")
display: AbstractButton.IconOnly
icon.name: "expand"
icon.source: "qrc:/assets/icons/caret-down.svg"
onClicked:{
onClicked: {timeTumbler.visible=true;timeTumbler.curSelection="start"}
}
}
Label{
x: 0.5*root.fontFactor*osSettings.bigFontSize
y: 4*root.fontFactor*osSettings.bigFontSize
width: 3*root.fontFactor*osSettings.bigFontSize
height: root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
color: osSettings.primaryTextColor
text:qsTr("End")
}
TextField {
id: textEndDate
x: 4*root.fontFactor*osSettings.bigFontSize
y: 3*root.fontFactor*osSettings.bigFontSize
width: 5*root.fontFactor*osSettings.bigFontSize
height: 2.5*root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
horizontalAlignment: TextInput.AlignRight
inputMask: "99-99-9999"
validator: RegularExpressionValidator{regularExpression: /^([0-2\s]?[0-9\s]|3[0-1\s])-(0[0-9\s]|1[0-2\s])-([0-9\s][0-9\s][0-9\s][0-9\s])$ / }
enabled: false
font.bold: true
}
MButton {
id: textEndDateDropdown
x: 9.5*root.fontFactor*osSettings.bigFontSize
y: 3*root.fontFactor*osSettings.bigFontSize
width: 2*root.fontFactor*osSettings.bigFontSize
height: 2*root.fontFactor*osSettings.bigFontSize
enabled: false
text: qsTr("End date")
display: AbstractButton.IconOnly
icon.name: "expand"
icon.source: "qrc:/assets/icons/caret-down.svg"
onClicked:{
cal.visible=true;
cal.curSelection="end"
}
}
TextField {
id: textEndTime
x: 13*root.fontFactor*osSettings.bigFontSize
y: 3*root.fontFactor*osSettings.bigFontSize
width: 3*root.fontFactor*osSettings.bigFontSize
height: 2.5*root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
enabled: false
horizontalAlignment: TextInput.AlignRight
inputMask: "99:99"
validator: RegularExpressionValidator{regularExpression: /^([0-1\s]?[0-9\s]|2[0-3\s]):([0-5\s][0-9\s])$ / }
font.bold: true
}
MButton {
id: textEndTimeDropdown
x: 16.5*root.fontFactor*osSettings.bigFontSize
y: 3*root.fontFactor*osSettings.bigFontSize
width: 2*root.fontFactor*osSettings.bigFontSize
height: 2*root.fontFactor*osSettings.bigFontSize
enabled: false
text: qsTr("End time")
display: AbstractButton.IconOnly
icon.name: "expand"
icon.source: "qrc:/assets/icons/caret-down.svg"
onClicked:{
onClicked: {timeTumbler.visible=true;timeTumbler.curSelection="end"}
}
}
Column{
id: textColumn
x: 4*root.fontFactor*osSettings.bigFontSize
y: 6*root.fontFactor*osSettings.bigFontSize
width: parent.width-7*root.fontFactor*osSettings.bigFontSize
ListView{
id: cal
visible: false
width: textColumn.width //12*root.fontFactor*osSettings.bigFontSize
height: 15*root.fontFactor*osSettings.bigFontSize
clip: true
snapMode: ListView.SnapOneItem
orientation: ListView.Horizontal
highlightRangeMode: ListView.StrictlyEnforceRange
property string curSelection: "start"
model: CalendarModel {id:calendarModel
from: new Date()
to: new Date(new Date().valueOf()+93312000000)
}
delegate:
Item{
width:Math.min(23*root.fontFactor*osSettings.bigFontSize,cal.width)
height: parent.height
Text{
font.bold: true
width: parent.width-root.fontFactor*osSettings.bigFontSize
horizontalAlignment:Text.AlignHCenter
color: osSettings.primaryTextColor
text: model.year
font.pointSize: osSettings.systemFontSize
}
Text{y:1.5*root.fontFactor*osSettings.bigFontSize
width: parent.width-osSettings.bigFontSize
text: Qt.locale().standaloneMonthName(model.month)
color: osSettings.primaryTextColor
horizontalAlignment:Text.AlignHCenter
font.pointSize: osSettings.systemFontSize
}
DayOfWeekRow{y:3*root.fontFactor*osSettings.bigFontSize
width: parent.width-root.fontFactor*osSettings.bigFontSize
locale: monthgrid.locale
font.pointSize: osSettings.systemFontSize
}
MonthGrid {y:5*root.fontFactor*osSettings.bigFontSize
id: monthgrid
height: parent.height-5*root.fontFactor*osSettings.bigFontSize
width: parent.width-root.fontFactor*osSettings.bigFontSize
month: model.month
year: model.year
locale: Qt.locale()
delegate: Button{
width: root.fontFactor*osSettings.bigFontSize*3
height: root.fontFactor*osSettings.bigFontSize*2
enabled: model.month==monthgrid.month
text: model.day
font.underline: model.today
font.pointSize: 1.2*osSettings.systemFontSize
highlighted: model.today
onClicked:{if (cal.curSelection=="start"){
textStartDate.text=Qt.formatDate(model.date, "dd-MM-yyyy");
}else{
textEndDate.text=Qt.formatDate(model.date, "dd-MM-yyyy");
}
cal.visible=false}}
}
}
ScrollIndicator.horizontal: ScrollIndicator { }
Component.onCompleted: positionViewAtBeginning()
}
Frame {
id: timeTumbler
width: 12*root.fontFactor*osSettings.bigFontSize
height: 10*root.fontFactor*osSettings.bigFontSize
visible: false
property string curSelection: "start"
Row {
Tumbler {
id: hoursTumbler
model: 24
delegate: tumblerDelegateComponent
currentIndex: 12
}
Tumbler {
id: minutesTumbler
model: 60
delegate: tumblerDelegateComponent
}
}
MButton {
id: timeInputfinished
width: 2*root.fontFactor*osSettings.bigFontSize
height: 2*root.fontFactor*osSettings.bigFontSize
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Accept time")
display: AbstractButton.IconOnly
icon.name: "checkbox"
icon.source: "qrc:/assets/icons/check.svg"
onClicked:{
if (timeTumbler.curSelection=="start"){
textStartTime.text=formatText(24,hoursTumbler.currentIndex)+":"+formatText(60,minutesTumbler.currentIndex);
}else{
textEndTime.text=formatText(24,hoursTumbler.currentIndex)+":"+formatText(60,minutesTumbler.currentIndex);
}
timeTumbler.visible=false
}
}
}
CheckBox{
id: checkNoEndTime
width: 12*root.fontFactor*osSettings.bigFontSize
height: 2.5*root.fontFactor*osSettings.bigFontSize
checked: true
font.pointSize: osSettings.systemFontSize
text: qsTr("no end")
onCheckedChanged: {
if(checked==true){
textEndDate.enabled=false;
textEndDateDropdown.enabled=false;
textEndTime.enabled=false;
textEndTimeDropdown.enabled=false;
textEndDate.text="";
textEndTime.text=""
}else{
textEndDate.enabled=true;
textEndDateDropdown.enabled=true;
textEndTime.enabled=true;
textEndTimeDropdown.enabled=true;
textEndDate.text=textStartDate.text;
textEndTime.text=textStartTime.text
}
}
}
TextField {
id: titleField
width: parent.width-root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
font.bold: true
placeholderText: qsTr("Title (required)")
}
Rectangle{
color: osSettings.backgroundColor
radius: 0.5*mm
width: parent.width-root.fontFactor*osSettings.bigFontSize
height:Math.max(bodyField.contentHeight+root.fontFactor*osSettings.bigFontSize,2.5*root.fontFactor*osSettings.bigFontSize)
TextArea {
id: bodyField
anchors.fill: parent
font.pointSize: osSettings.systemFontSize
font.family: "Noto Sans"
wrapMode: Text.Wrap
selectByMouse: true
placeholderText: qsTr("Event description (optional)")
textFormat: TextEdit.PlainText
text: eventInformation.hasOwnProperty("text")?eventInformation.text:""
onLinkActivated:{Qt.openUrlExternally(link)}
}
}
TextField {
id: locationField
width: parent.width-root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
placeholderText: qsTr("Location (optional)")
}
CheckBox{
id: chkbxPublish
width: 10*root.fontFactor*osSettings.bigFontSize
height: 2.5*root.fontFactor*osSettings.bigFontSize
checked: true
font.pointSize: osSettings.systemFontSize
text: qsTr("Publish event?")
}
BusyIndicator{
id: eventCreateBusy
anchors.horizontalCenter: eventRect.horizontalCenter
anchors.top: eventRect.top
anchors.topMargin: 2*root.fontFactor*osSettings.bigFontSize
width:3*root.fontFactor*osSettings.bigFontSize
height: 3*root.fontFactor*osSettings.bigFontSize
running: false
}
MButton{
id:createEventButton
text: qsTr("Create event")
width:textColumn.width
onClicked:{
let startdatetext=textStartDate.getText(0,textStartDate.length);
let startdate=new Date(startdatetext.substring(6,10)+"-"+startdatetext.substring(3,5)+"-"+startdatetext.substring(0,2)+"T"+textStartTime.text)
if (titleField.text==""){
Helperjs.showMessage(qsTr("Error"),qsTr("No event name supplied"),eventCreateBox)
}else{
let startdatetext=textStartDate.getText(0,textStartDate.length);
let startdate=new Date(startdatetext.substring(6,10)+"-"+startdatetext.substring(3,5)+"-"+startdatetext.substring(0,2)+"T"+textStartTime.text)
eventCreateBusy.running=true;
xhr.setAccount(login);
xhr.setApi("/api/friendica/event_create");
xhr.setParam("name", titleField.text);
xhr.setParam("start_time",startdate.toISOString())
if(!checkNoEndTime.checked){
let enddatetext=textEndDate.getText(0,textEndDate.length);
let enddate=new Date(enddatetext.substring(6,10)+"-"+enddatetext.substring(3,5)+"-"+enddatetext.substring(0,2)+"T"+textEndTime.text)
xhr.setParam("end_time",enddate.toISOString())
}
xhr.setParam("name",titleField.text)
if (bodyField.text!=""){xhr.setParam("desc",bodyField.text)}
if (locationField.text!=""){xhr.setParam("place",locationField.text)}
xhr.setParam("publish",chkbxPublish.checked)
xhr.post();
}
}
}
Connections{
target: xhr
function onSuccess(text,api){
if (api=="/api/friendica/event_create"){
updatenews.setDatabase();
updatenews.login();
updatenews.setSyncAll(false);
updatenews.events();
try{while(rootstackView.depth>1){rootstackView.pop()}}catch(e){}
}
}
function onError(text,api){
if (api=="/api/friendica/event_create"){
Helperjs.showMessage(qsTr("Error"),text,root)
}
}
}
}
Component {
id: tumblerDelegateComponent
Label {
text: formatText(Tumbler.tumbler.count, modelData)
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
color: osSettings.primaryTextColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pointSize: osSettings.systemFontSize
}
}
}
}

View file

@ -0,0 +1,109 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
import QtQuick.Controls 6.3
import "qrc:/js/service.js" as Service
import "qrc:/js/helper.js" as Helperjs
import "qrc:/qml/genericqml"
import "qrc:/qml/calendarqml"
Rectangle{
id:eventList
color: osSettings.backgroundColor
property var daylist:[]
property int dayint: 0
property var events:[]
MButton{
id:closeButton
anchors.top: parent.top
anchors.topMargin: 1*mm
anchors.right: parent.right
anchors.rightMargin: 1*mm
width: 2*root.fontFactor*osSettings.bigFontSize;
text: qsTr("Close")
display: AbstractButton.IconOnly
icon.name: "dialog-close"
icon.source: "qrc:/assets/icons/times-circle.svg"
onClicked:{rootstackView.pop()}
}
MButton{
id: createNewEvent
anchors.top: parent.top
anchors.topMargin: 1*mm
anchors.right:closeButton.left
anchors.rightMargin:mm
width: 2*root.fontFactor*osSettings.bigFontSize;
display: AbstractButton.IconOnly
icon.name: "list-add"
icon.source: "qrc:/assets/icons/plus.svg"
onClicked: {
rootstackView.push("qrc:/qml/calendarqml/EventCreate.qml",{"startDate": new Date(dayint*86400000)})
}
}
Dialog {
id: deleteDialog
anchors.centerIn: parent
property int eventid:0
title: qsTr("Delete Event?")
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
onAccepted: {
xhr.setAccount(login);
xhr.setApi("/api/friendica/event_delete");
xhr.setParam("id",eventid);
xhr.post();
}
onRejected: {close()}
}
ListView {
id: eventlistView
y:closeButton.height+2*mm
width: eventList.width-4*root.fontFactor*osSettings.bigFontSize
height: eventList.height-closeButton.height-root.fontFactor*osSettings.bigFontSize
clip: true
model: eventModel
delegate: EventListItem{}
}
ListModel{
id: eventModel
}
Component.onCompleted:{
var currentevents=events.filter(event=>(dayint>=event.startday)&&(dayint<=event.endday));
for (var i=0; i<currentevents.length;i++){
var liststate="";if(currentevents.length<2){liststate="large"};
eventModel.append({"event":currentevents[i],"eventstatus":liststate});
}
}
}

View file

@ -0,0 +1,125 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
import QtQuick.Controls 6.3
import QtQml 6.3
import "qrc:/js/service.js" as Service
import "qrc:/js/helper.js" as Helperjs
import "qrc:/qml/genericqml"
import "qrc:/qml/calendarqml"
Rectangle{
id:eventItem
property string status: eventstatus
property var currEvent: event
width:parent.width
height:Math.max(eventNameText.height+eventDetailsText.height,profileImage.height)+mm
//border.color: osSettings.backgroundDimColor
color: osSettings.backgroundColor
border.width: 1
radius: 0.5*mm
Image {
id:profileImage
source: ((event.eventOwner.profile_image!="") && (typeof(event.eventOwner.profile_image)=="string"))? "file://"+event.eventOwner.profile_image : event.eventOwner.profile_image_url
x:1
y:1
width: 7*mm
height: 7*mm
onStatusChanged: if (profileImage.status == Image.Error) {source="qrc:/assets/defaultcontact.jpg"}
}
Text {
id:eventNameText
x: 8*mm
width:parent.width-8*mm
height:contentHeight
color: osSettings.primaryTextColor
textFormat: Text.RichText
font.pointSize: osSettings.systemFontSize
text: new Date(event.start).toLocaleString(Qt.locale(),Locale.NarrowFormat)+ " - " +((event.end>0)&&(event.end!=null)?new Date(event.end).toLocaleString(Qt.locale(),Locale.NarrowFormat):"\u221E")+":<br>"+(status=="large"?"<b>"+event.title+"</b>":event.title)
wrapMode:Text.Wrap
}
Text {
id:eventDetailsText
x:8*mm
z:4
width: parent.width-8*mm
height: contentHeight
color: osSettings.primaryTextColor
textFormat: Text.RichText
text: status!="large"?"":Qt.atob(event.desc) + (event.location==""?"":"<br><br>"+qsTr("Location")+": "+event.location)
anchors.top: eventNameText.bottom
font.pointSize: osSettings.systemFontSize
wrapMode:Text.Wrap
onLinkActivated:{Qt.openUrlExternally(link)}
}
MouseArea{
anchors.fill: parent
MButton{
id: deleteEvent
anchors.top: parent.top
anchors.topMargin: 0.5*mm
anchors.right:parent.right
anchors.rightMargin:mm
width: 2*root.fontFactor*osSettings.bigFontSize;
icon.name: "user-trash-symbolic"
icon.source: "qrc:/assets/icons/trash.svg"
// background: Rectangle{color: osSettings.dialogColor;
// radius: 0.5*mm}
onClicked: {
deleteDialog.eventid=event.id
deleteDialog.open()
}
}
onClicked:{print (JSON.stringify(event))
if (status==""){
rootstackView.push("qrc:/qml/calendarqml/EventList.qml",{"dayint": event.startday, "events":[event]});
} else {rootstackView.pop()}
}
}
Connections{
target: xhr
function onSuccess(text,api){
if (api=="/api/friendica/event_delete"){
let obj=JSON.parse(text);
if(obj.status=="deleted"&&obj.id==event.id){
Helperjs.deleteData(db,"events",login.username, function(){
eventModel.remove(index);
},"id",obj.id)
}
}
}
}
}

View file

@ -0,0 +1,68 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
import QtQuick.Controls 6.3
Dialog {
id: rulesDialog
height: parent.height/2
width: parent.width
anchors.centerIn: parent
title: qsTr("Accept instance rules")
property string rules: ""
standardButtons: Dialog.Yes | Dialog.No
modal: true
onAccepted: {
username.visible=true;
ruleButton.visible=false;
confirmationOAuth.visible=true
}
onRejected: {close()}
ScrollView{
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
width: root.width-2*root.fontFactor*osSettings.bigFontSize
height:parent.height
clip:true
Text {
x:1; y:1
width: root.width-4*root.fontFactor*osSettings.bigFontSize
wrapMode: TextEdit.Wrap
color: osSettings.primaryTextColor
linkColor: osSettings.secondaryTextColor
textFormat: Text.PlainText
font.family: "Noto Sans"
font.pointSize: osSettings.systemFontSize
text: rules
}
}
}

View file

@ -0,0 +1,621 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
import QtCore 6.3
import QtQuick.Dialogs 6.3
import QtQuick.Controls 6.3
import QtQuick.Layouts
import QtQml.Models 6.3
import "qrc:/js/service.js" as Service
import "qrc:/js/helper.js" as Helperjs
import "qrc:/qml/configqml"
import "qrc:/qml/genericqml"
Page{
id:accountPage
width: root.width
height: root.height
property var users:[]
property var userdata: ({})
property string imagestoredir: ""
property var appdata: ({})
function setServericon(server){
if ((server!=null) && (server!="")){
xhr.setUrl(server);
xhr.setApi("/api/statusnet/config");
xhr.clearParams();
xhr.get();
}
}
function verify(userconfig){
Helperjs.friendicaRequest(userconfig,"/api/v1/accounts/verify_credentials",root,function(obj){
accountBusy.running=false;
try{var credentials=JSON.parse(obj);
if (credentials.hasOwnProperty('error')){print("error "+credentials.error);
Helperjs.showMessage(qsTr("Error"),qsTr("Wrong password or 2FA enabled!"),root)
}
else{
if (users.length==0){Service.setDefaultOptions(db);}
if (userconfig.hasOwnProperty("APIVersion")){userconfig.password=""}
if (imagestoredir==""){
imagestoredir=filesystem.homePath+"/"+credentials.username+"/";
userconfig.imagestore=imagestoredir
}
if(userconfig.imagestore == filesystem.homePath+"/"+credentials.username+"/")
{filesystem.makePath(filesystem.homePath+"/"+credentials.username);}
filesystem.Directory=imagestoredir;
filesystem.makeDir("contacts");
filesystem.makeDir("albums");
userconfig.accountId=credentials.id;
userconfig.username=credentials.username;
Service.storeConfig(db,userconfig);
Service.readConfig(db,function(userconfig){
Helperjs.readData(db,"config","",function(storedUsers){
storedUsers.sort(function(obj1, obj2) {
return obj1.isActive - obj2.isActive;
});
accountPage.users=storedUsers});
//reset values
login=userconfig;
news=[];
contactlist=[];
if(rootstackView.depth>1){rootstackSignal(0)}
newstypeSignal("refresh");
},"isActive",0);
Helperjs.showMessage(qsTr("Success"),qsTr("Name")+": "+credentials.display_name+"\nScreen Name: "+credentials.username,root)
rootstackView.pop()
}
}catch(e){Helperjs.showMessage(qsTr("Error"),qsTr("Wrong password or 2FA enabled!"+e),root)};
})
}
BusyIndicator{
id: accountBusy
anchors.centerIn: parent
width: 5*root.fontFactor*osSettings.bigFontSize
height: 5*root.fontFactor*osSettings.bigFontSize
running: false
}
ColumnLayout{
x: root.fontFactor*osSettings.bigFontSize
width: root.width - 2*mm
y: root.fontFactor*osSettings.bigFontSize
spacing: root.fontFactor*osSettings.bigFontSize
Row{
spacing:0.5*mm
height: userButton.height
width: parent.width
MButton{
id:userButton
text:qsTr("User")
font.pointSize: osSettings.bigFontSize
visible: users.length>0
onClicked:{
var useritems="";
for (var i=0;i<accountPage.users.length;i++){
useritems=useritems+"MenuItem{font.pointSize: osSettings.bigFontSize;width:accountPage.width*2/3; text:'"+accountPage.users[i].username+
"'; onTriggered: {Service.readConfig(db,function(obj){
userButton.text=obj.username;
servername.text=obj.server;
serverModel.insert(0,{text:obj.server})
accountPage.setServericon(obj.server);
username.text= obj.username;
password.text=Qt.atob(obj.password);
imagestore.text=obj.imagestore;
imagestoredir=obj.imagestore;
if( obj.isActive==0){userButton.font.bold='true'} else {userButton.font.bold='false'}
if(obj.password!=''){accountPage.state='password'}
else if (obj.token!=''){accountPage.state='oauth'}
},'username','"+ accountPage.users[i].username+"')}}"
}
var menuString="import QtQuick.Controls 6.3;import 'qrc:/js/service.js' as Service;"+
" Menu {width:8*root.fontFactor*osSettings.bigFontSize;"+useritems+"}";
var userlistObject=Qt.createQmlObject(menuString,accountPage,"usermenuOutput")
userlistObject.popup() }
}
MButton {
visible: users.length>0
text: "-"
font.pointSize: osSettings.bigFontSize
onClicked:{
var userconfig={server: servername.text, username: username.text, password: Qt.btoa(password.text)};
Service.readConfig(db,function(user){
if(userdata.token!=""){xhr.setUrl(servername.text);
xhr.setApi("/oauth/revoke");
xhr.clearParams();
xhr.setParam("client_id",user.client.client_id);
xhr.setParam("client_secret",user.client.client_secret);
xhr.setParam("token",user.token);
xhr.post();
}
},"username",username.text);
Service.deleteConfig(db,userconfig,function(){
filesystem.Directory=imagestore.text+"contacts";
filesystem.rmDir();
filesystem.Directory=imagestore.text+"albums";
filesystem.rmDir();
servername.text="https://";
servericon.visible=false;
servericon.source="";
username.text="";
password.text="";
imagestore.text="";
userButton.text=qsTr("User");
Helperjs.readData(db,"config","",function(storedUsers){
storedUsers.sort(function(obj1, obj2) {
return obj1.isActive - obj2.isActive;
})
accountPage.users=storedUsers;})
accountPage.state="new_oauth"
})
}}
MButton {
visible: users.length>0
text: "+"
font.pointSize: osSettings.bigFontSize
onClicked:{
servername.text="https://"
servericon.visible=false;
servericon.source="";
username.text=""
password.text=""
imagestore.text=""
userButton.text=qsTr("User")
accountPage.state="new_oauth"
}
}
MButton {
text: "?"
font.pointSize: osSettings.bigFontSize
onClicked:{
rootstackView.push("qrc:/qml/configqml/InfoBox.qml");
}
}
MButton {
text: qsTr("Method")
display: AbstractButton.IconOnly
icon.name: "expand"
icon.source: "qrc:/assets/icons/caret-down.svg"
font.pointSize: osSettings.bigFontSize
Menu {
id:authMethodMenu
width: 10*root.fontFactor*osSettings.systemFontSize
MenuItem {
text: qsTr("OAuth")
font.pointSize: osSettings.systemFontSize
font.bold:accountPage.state=="oauth"
onTriggered: {accountPage.state="oauth"}
}
MenuItem {
text: qsTr("Password")
font.pointSize: osSettings.systemFontSize
font.bold:accountPage.state=="password"
onTriggered: {accountPage.state="password"}
}
}
onClicked: {authMethodMenu.popup()}
}
MButton{
id:closeButton
visible: users.length>0
text: qsTr("Close")
display: AbstractButton.IconOnly
icon.name: "dialog-close"
icon.source: "qrc:/assets/icons/times-circle.svg"
font.pointSize: osSettings.bigFontSize
onClicked:{rootstackView.pop()}
}
}
Row{
spacing:0.5*mm
height: 3*root.fontFactor*osSettings.bigFontSize
width: parent.width
Image{
id:servericon
width:2.5*root.fontFactor*osSettings.bigFontSize; height: 2.5*root.fontFactor*osSettings.bigFontSize
visible: false
source:""
property var serverconfig:({})
MouseArea{
anchors.fill:parent
onClicked:{
let serverConfigString="import QtQuick 2.0; import QtQuick.Dialogs 6.3; MessageDialog{ visible: true; title:'Server';buttons: MessageDialog.Ok;text: 'Name: "+
servericon.serverconfig.site.name+"\nLanguage: "+servericon.serverconfig.site.language+
"\nEmail: "+servericon.serverconfig.site.email+"\nTimezone: "+servericon.serverconfig.site.timezone+"\nClosed: "+servericon.serverconfig.site.closed+
"\nText limit: "+servericon.serverconfig.site.textlimit+"\nShort Url length: "+servericon.serverconfig.site.shorturllength+
"\nFriendica version: "+servericon.serverconfig.site.friendica.FRIENDICA_VERSION+
"\nDB Update version: "+servericon.serverconfig.site.friendica.DB_UPDATE_VERSION+"'}";
var serverconfigObject=Qt.createQmlObject(serverConfigString,accountPage,"serverconfigOutput");
}
}
}
//FontLoader{id: fontAwesome; source: "qrc:/assets/fontawesome-webfont.ttf"}
MButton{
id:serverSearchButton
width: 3*root.fontFactor*osSettings.bigFontSize; height: 2.5*root.fontFactor*osSettings.bigFontSize
//text:"\uf002"
icon.name: "search"
font.pointSize: osSettings.bigFontSize
visible: servericon.visible?false:true
onClicked:{Qt.openUrlExternally(Qt.resolvedUrl("https://dir.friendica.social/servers"))}
}
// ComboBox{
// id: servername
// x: 4*root.fontFactor*osSettings.bigFontSize
// y: 3.5*root.fontFactor*osSettings.bigFontSize
// width: root.width-5*root.fontFactor*osSettings.bigFontSize
// height: 2.5*root.fontFactor*osSettings.bigFontSize//5*mm;
// font.pointSize: osSettings.systemFontSize
// editable:true
// model: serverModel
// onAccepted: {
// let cleanText =currentText;if(currentText==""){cleanText=editText}
// if((cleanText).substring(0,8) !=="https://"){
// cleanText="https://"+cleanText
// }
// if (find(cleanText) === -1) {
// serverModel.append({text: cleanText})
// currentIndex = find(cleanText)
// displayText=cleanText
// }
// if (cleanText!=""){accountPage.setServericon(cleanText)}
// }
// onFocusChanged: {
// if(focus==false){
// onAccepted()
// }
// }
// }
TextField {
id: servername
width: root.width-5*root.fontFactor*osSettings.bigFontSize
height: 2.5*root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.systemFontSize
text:"https://"
onFocusChanged:{
if (focus){servermenu.open()}
else{
if((servername.text).substring(0,11) =="https://http"){
servername.text= (servername.text).substring(8)
}
if (servername.text!="https://"){
accountPage.setServericon(servername.text)}
}
}
}
Menu {
id:servermenu
width: 13*root.fontFactor*osSettings.bigFontSize
Instantiator{
model:serverModel
MenuItem{
text: modelData
onTriggered: {servername.text=modelData}
}
onObjectAdded:{servermenu.insertItem(index,object)}
onObjectRemoved:{servermenu.removeItem(object)}
}
}
ListModel{id:serverModel
ListElement{text:"https://anonsys.net"}
ListElement{text:"https://asaps-sm.lafayettegroup.com"}
ListElement{text:"https://f.freinetz.ch"}
ListElement{text:"https://friendica.chilemasto.casa"}
ListElement{text:"https://friendica.eskimo.com"}
ListElement{text:"https://friendica.me"}
ListElement{text:"https://friendica.opensocial.space"}
ListElement{text:"https://friendica.utzer.de"}
ListElement{text:"https://friendica.vrije-mens.org"}
ListElement{text:"https://libranet.de"}
ListElement{text:"https://loma.ml"}
ListElement{text:"https://nerdica.net"}
ListElement{text:"https://nsfw.wnymathguy.com"}
ListElement{text:"https://opensocial.at"}
ListElement{text:"https://poliverso.org"}
ListElement{text:"https://social.isurf.ca"}
ListElement{text:"https://social.trom.tf"}
ListElement{text:"https://squeet.me"}
ListElement{text:"https://venera.social"}
}
}
MButton {
id: ruleButton
width: parent.width
visible: (osSettings.osType=="Android") && (userButton.text== qsTr("User"))
height: 2*root.fontFactor*osSettings.bigFontSize;
text: qsTr("Instance rules")
font.pointSize: osSettings.bigFontSize
onClicked:{
xhr.setUrl(servername.text);
xhr.setApi("/api/v1/instance/rules");
xhr.clearParams();
xhr.get();
}
}
TextField {
id: username
width: root.width-5*root.fontFactor*osSettings.bigFontSize
height: 2.5*root.fontFactor*osSettings.bigFontSize;
Layout.leftMargin: 3*root.fontFactor*osSettings.bigFontSize;
font.pointSize: osSettings.systemFontSize
visible: (osSettings.osType=="Android")?(text!= ""):true
placeholderText: qsTr("Nickname")
selectByMouse: true
onEditingFinished: {
if (username.text.indexOf('@')>-1){
Helperjs.showMessage(qsTr("Error"),qsTr("Nicknames containing @ symbol currently not supported"),accountPage)
}
imagestoredir=filesystem.homePath+"/"+username.text+"/"
}
}
TextField {
id: password
width: root.width-9*mm; height: 2.5*root.fontFactor*osSettings.bigFontSize;
Layout.leftMargin: 3*root.fontFactor*osSettings.bigFontSize;
font.pointSize: osSettings.systemFontSize
visible: (osSettings.osType=="Android")?(userButton.text!= qsTr("User")):true
selectByMouse: true
echoMode: TextInput.Password
placeholderText: qsTr("Password")
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhSensitiveData
}
Row{
spacing:0.5*mm
height: 3*root.fontFactor*osSettings.bigFontSize
width: parent.width
Label {
id: imagedirlabel
visible: imagestore.text!=""
text: qsTr("Image dir.")
font.pointSize: osSettings.systemFontSize
}
TextField {
id: imagestore
width: root.width-17*mm;
height: 2.5*root.fontFactor*osSettings.bigFontSize;
visible:imagestore.text!=""
font.pointSize: osSettings.systemFontSize
selectByMouse: true
text: ""
wrapMode: TextEdit.NoWrap
onTextChanged: imagestoredir=imagestore.text
}
MButton {
visible:imagestore.text!=""
text: "..."
font.pointSize: osSettings.bigFontSize
onClicked:{imagestoreDialog.open()}
}
FolderDialog {
id: imagestoreDialog
title: "Please choose a directory"
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
//selectFolder: true
onAccepted: {
var imagestoreString=imagestoreDialog.selectedFolder.toString();
imagestoreString=imagestoreString.replace(/^(file:\/{2})/,"")+"/"
imagestore.text=imagestoreString
}
}
}
MButton {
id:confirmationOAuth
width: parent.width
text: qsTr("Connect")
font.pointSize: osSettings.bigFontSize
visible: (osSettings.osType=="Android")?userButton.text!= qsTr("User"):true
onClicked:{
if (servername.text==""){Helperjs.showMessage(qsTr("Error"), qsTr("No server given!"),root)}
else{
xhr.setUrl(servername.text);
xhr.setApi("/api/v1/apps");
xhr.clearParams();
if (osSettings.osType=="Android"){
xhr.setParam("client_name","Friendiqa-Android");
} else {
xhr.setParam("client_name","Friendiqa-"+filesystem.hostname);
}
xhr.setParam("redirect_uris","http://127.0.0.1:1337/");
xhr.setParam("scopes","read write follow push");
xhr.setParam("website","https://friendiqa.ma-nic.de");
xhr.post();
}
}
}
Connections{
target: xhr
function onSuccess(text,api){
if(api=="/api/v1/instance/rules"){
let rulestext="";
let rulesarray=JSON.parse(text)
for (let rule in rulesarray){
rulestext=rulestext+rulesarray[rule].text+"\n"
}
var component = Qt.createComponent("qrc:/qml/configqml/AcceptRules.qml");
var rulesdialog = component.createObject(accountPage,{"rules": rulestext});
rulesdialog.open();
}
else if(api=="/api/statusnet/config"){
try{let serverdata = JSON.parse(text);
servericon.visible=true;
servericon.source=serverdata.site.logo;
servericon.serverconfig=serverdata;
}
catch(e){print(e)}
}
else if (api=="/api/v1/apps"){
let app=JSON.parse(text);
accountPage.appdata=app;
oauth2.setClientId(app.client_id);
oauth2.setClientSecret(app.client_secret);
oauth2.setServer(servername.text);
oauth2.grant();
}
}
function onError(text,api){
print(api + " Error "+ text)
}
}
Connections{
target: oauth2
function onSuccess(text){
var userconfig={server: servername.displayText, username:"", password:"", imagestore: imagestoredir,interval:"",token: text,client:Qt.btoa(JSON.stringify(appdata))}
verify(userconfig)
}
function onError(text){
Helperjs.showMessage(qsTr("Error"), qsTr("Couldn't connect to server"),root)
print ("oauth2 onerror "+text)
}
}
MButton {
id:confirmation
width: 10*root.fontFactor*osSettings.bigFontSize;
text: qsTr("Confirm")
font.pointSize: osSettings.bigFontSize
visible: false// (osSettings.osType=="Android")?userButton.text!= qsTr("User"):true
onClicked:{
accountBusy.running=true;
var userconfig={server: servername.displayText, username: username.text, password:Qt.btoa(password.text), imagestore:imagestoredir,interval:""};
var errormessage="";
if (servername.text==""){errormessage=qsTr("No server given! ")}
else if (username.text==""){errormessage+=qsTr("No nickname given! ")}
else if (password.text=="") {errormessage+=qsTr("No password given! ")}
else if (imagestoredir=="") {errormessage+=qsTr("No image directory given!")}
else {errormessage=""}
if (errormessage=="") {verify(userconfig)}
else {Helperjs.showMessage(qsTr("Error"), errormessage,root)}
}}
MButton {
id: setDefault
width: 10*root.fontFactor*osSettings.bigFontSize;
text: qsTr("Set as default")
font.pointSize: osSettings.bigFontSize
visible: false
onClicked:{
accountBusy.running=true;
let users=updatenews.getAccounts("username",username.text)
Service.storeConfig(db,users[0]);
Service.readConfig(db,function(userconfig){
//reset values
login=userconfig;
news=[];
contactlist=[];
rootstackSignal(0);
newstypeSignal("refresh");
},"isActive",0);
Helperjs.showMessage(qsTr("Success"),"Screen Name: "+users[0].username,root)
rootstackView.pop()
}
}
}
states: [
State {
name: "new_oauth"
PropertyChanges {target: username; visible: false }
PropertyChanges {target: password; visible: false}
PropertyChanges {target: ruleButton; visible: true}
},
State {
name:"oauth"
PropertyChanges {target: username; visible: false}
PropertyChanges {target: password; visible: false}
PropertyChanges {target: confirmationOAuth; visible: true}
PropertyChanges {target: setDefault; visible: true}
PropertyChanges {target: confirmation; visible: false}
},
State{
name:"password"
PropertyChanges {target: username; visible: true }
PropertyChanges {target: password; visible: true}
PropertyChanges {target: confirmation; visible: true}
PropertyChanges {target: confirmationOAuth; visible: false}
}
]
Component.onCompleted: { //print("filesystem.osType " +filesystem.osType)
try{Helperjs.readData(db,"config","",function(storedUsers){
storedUsers.sort(function(obj1, obj2) {
return obj1.isActive - obj2.isActive;
})
accountPage.users=storedUsers;
Service.readConfig(db,function(obj){
if (obj==null){
accountPage.state="new_oauth"
}
else{
userButton.text=obj.username;
servername.text=obj.server;
serverModel.insert(0,{text:obj.server})
accountPage.setServericon(obj.server);
username.text= obj.username;
password.text=Qt.atob(obj.password);
imagestore.text=obj.imagestore;
imagestoredir=obj.imagestore;
if( obj.isActive==0){userButton.font.bold='true'} else {userButton.font.bold='false'}
if(obj.password!=""){accountPage.state="password"}
else if (obj.token!=""){accountPage.state="oauth"}
else {accountPage.state="new_oauth"}
}
},"isActive",0)
})}
catch (e){//print("onCompleted" +users.count +e)
}
}
}

View file

@ -0,0 +1,256 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
//import QtQuick.Dialogs 1.2
import QtQuick.Controls 6.3
import "qrc:/js/service.js" as Service
import "qrc:/js/helper.js" as Helperjs
import "qrc:/qml/configqml"
import "qrc:/qml/genericqml"
Page{
width: parent.width
height: parent.height
ScrollView{
anchors.fill: parent
contentHeight: 40*root.fontFactor*osSettings.bigFontSize
contentWidth: root.width
clip:true
Label {
text: qsTr("News as")
font.pointSize:osSettings.systemFontSize
x: root.fontFactor*osSettings.bigFontSize; y: 2*root.fontFactor*osSettings.bigFontSize
}
Rectangle{
x: root.fontFactor*osSettings.bigFontSize; y: 4*root.fontFactor*osSettings.bigFontSize;
width: newsTypeField.contentWidth+2*mm; height: 2*root.fontFactor*osSettings.bigFontSize
color: osSettings.backgroundDimColor//"#F3F3F3"
radius: 0.5*mm
Label{
id: newsTypeField
anchors.fill: parent
font.pointSize:osSettings.bigFontSize
text:qsTr("Conversations")
}
MouseArea{
anchors.fill:parent
onClicked:newstypemenu.popup()
}
}
Menu {
id:newstypemenu
width:12*root.fontFactor*osSettings.bigFontSize
MenuItem {
font.pointSize: osSettings.bigFontSize
text: qsTr("Timeline")
onTriggered: {newsTypeField.text=qsTr("Timeline");
Service.updateglobaloptions(root.db,"newsViewType","Timeline");}
}
MenuItem {
font.pointSize: osSettings.bigFontSize
text: qsTr("Conversations")
onTriggered: {newsTypeField.text=qsTr("Conversations");
Service.updateglobaloptions(root.db,"newsViewType","Conversations");}
}
}
Label {
text: qsTr("Max. News")
font.pointSize: osSettings.systemFontSize
x: root.fontFactor*osSettings.bigFontSize; y:8*root.fontFactor*osSettings.bigFontSize
}
Slider{ id: maxNews
x:6*root.fontFactor*osSettings.bigFontSize; y: 10*root.fontFactor*osSettings.bigFontSize;
width: root.width/2;height:2*root.fontFactor*osSettings.bigFontSize
from: 0;to:2000; stepSize: 100
value: root.globaloptions.hasOwnProperty("max_news")?root.globaloptions.max_news:1000
}
Rectangle{
color: osSettings.backgroundDimColor
x: root.fontFactor*osSettings.bigFontSize; y: 10*root.fontFactor*osSettings.bigFontSize;
width: 4*root.fontFactor*osSettings.bigFontSize; height: 2*root.fontFactor*osSettings.bigFontSize;
radius: 0.5*mm
TextEdit{id:maxNewsText;
anchors.fill: parent
font.pointSize: osSettings.bigFontSize
verticalAlignment:TextEdit.AlignRight
color: osSettings.primaryTextColor
text:maxNews.value
selectByMouse: true
onTextChanged: {
Service.updateglobaloptions(root.db,"max_news",text);
}
}
}
CheckBox{
id: nsfwCheckbox
x: root.fontFactor*osSettings.bigFontSize
y: 14*root.fontFactor*osSettings.bigFontSize
font.pointSize: osSettings.bigFontSize
text: qsTr("Hide #nsfw?")
checked:(globaloptions["hide_nsfw"]==1)?true:false
onClicked: {
toggle();
if(nsfwCheckbox.checked==true){
Service.updateglobaloptions(root.db,"hide_nsfw",0);nsfwCheckbox.checked=false;
}
else{
Service.updateglobaloptions(root.db,"hide_nsfw",1);nsfwCheckbox.checked=true;
}
}
}
// CheckBox{
// id: darkmodeCheckbox
// tristate:true
// x: root.fontFactor*osSettings.bigFontSize
// y: 24*root.fontFactor*osSettings.bigFontSize
// font.pointSize: osSettings.bigFontSize
// text: qsTr("Dark Mode")
// checked:(globaloptions["view_darkmode"]==1)?true:false
// onClicked: {
// toggle();
// if(darkmodeCheckbox.checked==true){
// Service.updateglobaloptions(root.db,"view_darkmode",0);darkmodeCheckbox.checked=false;
// root.Material.theme=Material.Light
// }
// else{
// Service.updateglobaloptions(root.db,"view_darkmode",1);darkmodeCheckbox.checked=true;
// root.Material.theme=Material.Dark
// }
// }
// }
Column{
x: root.fontFactor*osSettings.bigFontSize
y: 18*root.fontFactor*osSettings.bigFontSize
Label{
text: qsTr("Default News Tabs")
font.pointSize: osSettings.systemFontSize
}
Repeater{id:newstypeRepeater
model: 4
delegate:ComboBox{
required property int index
model: ["Home", "Replies", "DirectMessages","Favorites","Public Timeline","Notifications"]
currentIndex: model.indexOf(globaloptions.defaultNewsTypes[index])
onCurrentTextChanged: {
if (currentText !== globaloptions.defaultNewsTypes[index]){
globaloptions.defaultNewsTypes.splice(index,1,currentText);
Service.updateglobaloptions(root.db,"defaultNewsTypes",globaloptions.defaultNewsTypes)
root.globaloptionsChanged()
}
}
}
}
}
Column{
visible: osSettings.osType=="Android"
x: root.fontFactor*osSettings.bigFontSize
y: 26*root.fontFactor*osSettings.bigFontSize
Label{
text: qsTr("Dark Mode")
font.pointSize: osSettings.systemFontSize}
RadioButton{
text: qsTr("System")
checked: (globaloptions["view_darkmode"]==0 || globaloptions["view_darkmode"]==undefined)?true:false
font.pointSize: osSettings.bigFontSize
onClicked: {
if(checked==true){
Service.updateglobaloptions(root.db,"view_darkmode",0);
root.Material.theme=Material.System
}
}
}
RadioButton{
text: qsTr("Dark")
checked: (globaloptions["view_darkmode"]==1)?true:false
font.pointSize: osSettings.bigFontSize
onClicked: {
if(checked==true){
Service.updateglobaloptions(root.db,"view_darkmode",1);
root.Material.theme=Material.Dark
}
}
}
RadioButton{
text: qsTr("Light")
checked: (globaloptions["view_darkmode"]==2)?true:false
font.pointSize: osSettings.bigFontSize
onClicked: {
if(checked==true){
Service.updateglobaloptions(root.db,"view_darkmode",2);
root.Material.theme=Material.Light
}
}
}
}
MButton {
anchors.right: parent.right; anchors.rightMargin: mm;
anchors.top: parent.top
anchors.topMargin: 3*root.fontFactor*osSettings.bigFontSize
//width: 2*root.fontFactor*osSettings.bigFontSize;
text: "?"
font.pointSize: osSettings.bigFontSize
onClicked:{
rootstackView.push("qrc:/qml/configqml/InfoBox.qml");
}
}
}
// MButton{
// id:closeButton
// // height: 2*root.fontFactor*osSettings.bigFontSize
// width: 2*root.fontFactor*osSettings.bigFontSize;
// anchors.top: parent.top
// anchors.topMargin:root.fontFactor*osSettings.bigFontSize
// anchors.right: parent.right
// anchors.rightMargin: 1*mm
// text: "\uf057"
// font.pointSize: osSettings.bigFontSize
// onClicked:{rootstackView.pop()}
// }
}

View file

@ -0,0 +1,129 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
import QtQuick.Layouts
import QtQuick.Controls 6.3
import "qrc:/js/service.js" as Service
import "qrc:/qml/configqml"
import "qrc:/qml/genericqml"
Page{
//anchors.fill: parent
width:root.width
height:root.height
TabBar {
id: configbar
width: parent.width-3*root.fontFactor*osSettings.bigFontSize//osSettings.osType=="Android"?parent.width-3*root.fontFactor*osSettings.bigFontSize:parent.width
height: 2*root.fontFactor*osSettings.bigFontSize
x: osSettings.osType=="Android"?2*osSettings.bigFontSize:0
//visible: !wideScreen
position:TabBar.Header
currentIndex: 0
TabButton {
text: qsTr("Appearance")
font.pointSize: osSettings.systemFontSize
height: 2*root.fontFactor*osSettings.bigFontSize
width:6*root.fontFactor*osSettings.bigFontSize
}
TabButton {
text: qsTr("Sync")
font.pointSize: osSettings.systemFontSize
height: 2*root.fontFactor*osSettings.bigFontSize
width:10*root.fontFactor*osSettings.bigFontSize
}
TabButton {
text: qsTr("Start")
visible:osSettings.osType=="Linux"
font.pointSize: osSettings.systemFontSize
height: 2*root.fontFactor*osSettings.bigFontSize
width:10*root.fontFactor*osSettings.bigFontSize
}
}
LeftDrawerLinux{
id:leftDrawer
visible: wideScreen&&rootstackView.depth<2
width: visible?osSettings.systemFontSize*15:0
height: root.height-bar.height
}
LeftDrawerAndroid{
id: leftDrawerAndroid
}
StackLayout{
id:configTabView
width: wideScreen&&rootstackView.depth<2?parent.width-leftDrawer.width-mm:parent.width-mm//newstabitem.width/3*2:newstabitem.width
x: leftDrawer.width
y: configbar.height
height: parent.height-configbar.height-mm
currentIndex: configbar.currentIndex
// onCurrentIndexChanged:{
// if (currentIndex==1){
// contactsSignal("")
// }
// else if (currentIndex==2){
// contactsSignal("")
// }
// else if (currentIndex==3){groupsSignal(root.login.username)}
// }
Loader{
id: appearanceLoader
source:(configTabView.currentIndex==0)? "qrc:/qml/configqml/ConfigAppearancePage.qml":""
}
Loader{
id: syncLoader
source:(configTabView.currentIndex==1)? "qrc:/qml/configqml/SyncConfig.qml":""
}
Loader{
id: startLoader
source:(configTabView.currentIndex==2)? "qrc:/qml/configqml/ConfigStartPage.qml":""
}
}
MButton{
id:closeButton
anchors.top: parent.top
anchors.topMargin:2*root.fontFactor*osSettings.bigFontSize
anchors.right: parent.right
anchors.rightMargin: 1*mm
text: qsTr("Close")
display: AbstractButton.IconOnly
icon.name: "dialog-close"
icon.source: "qrc:/assets/icons/times-circle.svg"
font.pointSize: osSettings.bigFontSize
onClicked:{rootstackView.pop()}
}
}

View file

@ -0,0 +1,80 @@
// This file is part of Friendiqa
// https://git.friendi.ca/lubuwest/Friendiqa
// Copyright (C) 2020 Marco R. <thomasschmidt45@gmx.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations including
// the two.
//
// You must obey the GNU General Public License in all respects for all
// of the code used other than OpenSSL. If you modify file(s) with this
// exception, you may extend this exception to your version of the
// file(s), but you are not obligated to do so. If you do not wish to do
// so, delete this exception statement from your version. If you delete
// this exception statement from all source files in the program, then
// also delete it here.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import QtQuick 6.3
import QtQuick.Controls 6.3
Page{
//anchors.fill: parent
width:root.width
height:root.height-6*root.fontFactor*osSettings.bigFontSize
CheckBox{
id: autostartCheckbox
x: mm
y: root.fontFactor*osSettings.bigFontSize
width: 10*root.fontFactor*osSettings.bigFontSize
checked:filesystem.isAutostart
//style: CheckBoxStyle {
text: qsTr("Autostart")
font.pointSize: osSettings.bigFontSize
onClicked: {
toggle();
if(autostartCheckbox.checked==true){
filesystem.setAutostart(false);
autostartCheckbox.checked=false;
}
else{
filesystem.setAutostart(true);
autostartCheckbox.checked=true;
}
}
}
// CheckBox{
// id: minimizeCheckbox
// x: mm
// y: 3*root.fontFactor*osSettings.bigFontSize
// width: 10*root.fontFactor*osSettings.bigFontSize
// enabled: autostartCheckbox.checked==true
// checked:(globaloptions["notify_"+adapter]==1)?true:false
// text: qsTr("Start Minimized")
// font.pointSize: osSettings.bigFontSize
// onClicked: {
// toggle();
// if(notifyCheckbox.checked==true){
// Service.updateglobaloptions(root.db,"notify_"+adapter,0);notifyCheckbox.checked=false;
// }
// else{
// Service.updateglobaloptions(root.db,"notify_"+adapter,1);notifyCheckbox.checked=true;
// }
// }
// }
}

Some files were not shown because too many files have changed in this diff Show more