diff --git a/gotify_tray/database/default_settings.py b/gotify_tray/database/default_settings.py
index 6644428..e527ad3 100644
--- a/gotify_tray/database/default_settings.py
+++ b/gotify_tray/database/default_settings.py
@@ -4,4 +4,13 @@ DEFAULT_SETTINGS = {
"tray/notifications/duration_ms": 5000,
"tray/notifications/icon/show": True,
"watchdog/interval/s": 60,
+ "MessageWidget/image/size": 33,
+ "MessageWidget/font/title": "Noto Sans,17,-1,5,75,0,0,0,0,0,Bold",
+ "MessageWidget/font/date": "Noto Sans,11,-1,5,50,1,0,0,0,0,Italic",
+ "MessageWidget/font/content": "Noto Sans,11,-1,5,50,0,0,0,0,0,Regular",
+ "ApplicationItem/font": "Noto Sans,10,-1,5,50,0,0,0,0,0,Regular",
+ "MainWindow/font/application": "Noto Sans,13,-1,5,75,0,0,0,0,0,Bold",
+ "MainWindow/label/size": 25,
+ "MainWindow/button/size": 33,
+ "MainWindow/application/icon/size": 40,
}
diff --git a/gotify_tray/gui/MainApplication.py b/gotify_tray/gui/MainApplication.py
index 4741841..ff7d7a7 100644
--- a/gotify_tray/gui/MainApplication.py
+++ b/gotify_tray/gui/MainApplication.py
@@ -3,23 +3,35 @@ import logging
import os
import sys
import tempfile
-from typing import List
+from typing import List, Union
from gotify_tray import gotify
from gotify_tray.__version__ import __title__
from gotify_tray.database import Downloader, Settings
-from gotify_tray.tasks import GetApplicationsTask, ServerConnectionWatchdogTask
+from gotify_tray.tasks import (
+ DeleteApplicationMessagesTask,
+ DeleteAllMessagesTask,
+ DeleteMessageTask,
+ GetApplicationsTask,
+ GetApplicationMessagesTask,
+ GetMessagesTask,
+ ServerConnectionWatchdogTask,
+)
from gotify_tray.utils import verify_server
from PyQt6 import QtCore, QtGui, QtWidgets
from ..__version__ import __title__
-from .ApplicationModel import (
+from .models import (
+ ApplicationAllMessagesItem,
ApplicationItemDataRole,
ApplicationModel,
ApplicationModelItem,
+ MessagesModel,
+ MessagesModelItem,
+ MessageItemDataRole,
)
-from .SettingsDialog import SettingsDialog
-from .Tray import Tray
+from .widgets import MainWindow, SettingsDialog, Tray
+
settings = Settings("gotify-tray")
logger = logging.getLogger("gotify-tray")
@@ -58,7 +70,11 @@ class MainApplication(QtWidgets.QApplication):
self.downloader = Downloader()
+ self.messages_model = MessagesModel()
self.application_model = ApplicationModel()
+
+ self.main_window = MainWindow(self.application_model, self.messages_model)
+
self.refresh_applications()
self.tray = Tray()
@@ -77,44 +93,144 @@ class MainApplication(QtWidgets.QApplication):
self.watchdog.start()
def refresh_applications(self):
+ self.messages_model.clear()
self.application_model.clear()
- def get_applications_callback(
- applications: List[gotify.GotifyApplicationModel],
- ):
- for i, application in enumerate(applications):
- icon = QtGui.QIcon(
- self.downloader.get_filename(
- f"{self.gotify_client.url}/{application.image}"
- )
- )
- self.application_model.setItem(
- i, 0, ApplicationModelItem(application, icon),
- )
+ self.application_model.setItem(0, 0, ApplicationAllMessagesItem())
self.get_applications_task = GetApplicationsTask(self.gotify_client)
- self.get_applications_task.success.connect(get_applications_callback)
+ self.get_applications_task.success.connect(
+ self.get_applications_success_callback
+ )
+ self.get_applications_task.started.connect(
+ self.main_window.disable_applications
+ )
+ self.get_applications_task.finished.connect(
+ self.main_window.enable_applications
+ )
self.get_applications_task.start()
+ def get_applications_success_callback(
+ self, applications: List[gotify.GotifyApplicationModel],
+ ):
+ for i, application in enumerate(applications):
+ icon = QtGui.QIcon(
+ self.downloader.get_filename(
+ f"{self.gotify_client.url}/{application.image}"
+ )
+ )
+ self.application_model.setItem(
+ i + 1, 0, ApplicationModelItem(application, icon),
+ )
+
def listener_opened_callback(self):
+ self.main_window.set_active()
self.tray.set_icon_ok()
- # self.tray.actionReconnect.setEnabled(True)
def listener_closed_callback(self, close_status_code: int, close_msg: str):
+ self.main_window.set_connecting()
self.tray.set_icon_error()
if not self.shutting_down:
self.gotify_client.reconnect()
- def refresh_callback(self):
- # self.tray.actionReconnect.setDisabled(True)
+ def reconnect_callback(self):
if not self.gotify_client.is_listening():
self.gotify_client.listener.reset_wait_time()
else:
self.gotify_client.stop(reset_wait=True)
self.gotify_client.reconnect(increase_wait_time=False)
+ def insert_message(
+ self,
+ row: int,
+ message: gotify.GotifyMessageModel,
+ application: gotify.GotifyApplicationModel,
+ ):
+ message_item = MessagesModelItem(message)
+ self.messages_model.insertRow(row, message_item)
+ self.main_window.insert_message_widget(
+ message_item,
+ self.downloader.get_filename(
+ f"{self.gotify_client.url}/{application.image}"
+ ),
+ )
+
+ def application_selection_changed_callback(
+ self, item: Union[ApplicationModelItem, ApplicationAllMessagesItem]
+ ):
+ self.messages_model.clear()
+
+ if isinstance(item, ApplicationModelItem):
+
+ def get_application_messages_callback(
+ page: gotify.GotifyPagedMessagesModel,
+ ):
+ for i, message in enumerate(page.messages):
+ self.insert_message(
+ i, message, item.data(ApplicationItemDataRole.ApplicationRole),
+ )
+
+ self.get_application_messages_task = GetApplicationMessagesTask(
+ item.data(ApplicationItemDataRole.ApplicationRole).id,
+ self.gotify_client,
+ )
+ self.get_application_messages_task.success.connect(
+ get_application_messages_callback
+ )
+ self.get_application_messages_task.start()
+
+ elif isinstance(item, ApplicationAllMessagesItem):
+
+ def get_messages_callback(page: gotify.GotifyPagedMessagesModel):
+ for i, message in enumerate(page.messages):
+ if item := self.application_model.itemFromId(message.appid):
+ self.insert_message(
+ i,
+ message,
+ item.data(ApplicationItemDataRole.ApplicationRole),
+ )
+
+ self.get_messages_task = GetMessagesTask(self.gotify_client)
+ self.get_messages_task.success.connect(get_messages_callback)
+ self.get_messages_task.start()
+
+ def add_message_to_model(self, message: gotify.GotifyMessageModel):
+ if application_item := self.application_model.itemFromId(message.appid):
+ application_index = self.main_window.currentApplicationIndex()
+ if selected_application_item := self.application_model.itemFromIndex(
+ application_index
+ ):
+ if isinstance(selected_application_item, ApplicationModelItem):
+ # A single application is selected
+ if (
+ message.appid
+ == selected_application_item.data(
+ ApplicationItemDataRole.ApplicationRole
+ ).id
+ ):
+ self.insert_message(
+ 0,
+ message,
+ application_item.data(
+ ApplicationItemDataRole.ApplicationRole
+ ),
+ )
+ elif isinstance(selected_application_item, ApplicationAllMessagesItem):
+ # "All messages' is selected
+ self.insert_message(
+ 0,
+ message,
+ application_item.data(ApplicationItemDataRole.ApplicationRole),
+ )
+
def new_message_callback(self, message: gotify.GotifyMessageModel):
- if message.priority < settings.value("tray/notifications/priority", type=int):
+ self.add_message_to_model(message)
+
+ # Show a notification
+ if (
+ message.priority < settings.value("tray/notifications/priority", type=int)
+ or self.main_window.isActiveWindow()
+ ):
return
if settings.value("tray/notifications/icon/show", type=bool):
@@ -130,7 +246,6 @@ class MainApplication(QtWidgets.QApplication):
else:
icon = QtWidgets.QSystemTrayIcon.MessageIcon.Information
- # Show a notification
self.tray.showMessage(
message.title,
message.message,
@@ -138,6 +253,30 @@ class MainApplication(QtWidgets.QApplication):
msecs=settings.value("tray/notifications/duration_ms", type=int),
)
+ def delete_message_callback(self, message_item: MessagesModelItem):
+ self.delete_message_task = DeleteMessageTask(
+ message_item.data(MessageItemDataRole.MessageRole).id, self.gotify_client
+ )
+ self.messages_model.removeRow(message_item.row())
+ self.delete_message_task.start()
+
+ def delete_all_messages_callback(
+ self, item: Union[ApplicationModelItem, ApplicationAllMessagesItem]
+ ):
+ if isinstance(item, ApplicationModelItem):
+ self.delete_application_messages_task = DeleteApplicationMessagesTask(
+ item.data(ApplicationItemDataRole.ApplicationRole).id,
+ self.gotify_client,
+ )
+ self.delete_application_messages_task.start()
+ elif isinstance(item, ApplicationAllMessagesItem):
+ self.delete_all_messages_task = DeleteAllMessagesTask(self.gotify_client)
+ self.delete_all_messages_task.start()
+ else:
+ return
+
+ self.messages_model.clear()
+
def settings_callback(self):
settings_dialog = SettingsDialog()
accepted = settings_dialog.exec()
@@ -158,10 +297,26 @@ class MainApplication(QtWidgets.QApplication):
if r == QtWidgets.QMessageBox.StandardButton.Yes:
self.quit()
+ def tray_activated_callback(
+ self, reason: QtWidgets.QSystemTrayIcon.ActivationReason
+ ):
+ if reason == QtWidgets.QSystemTrayIcon.ActivationReason.Trigger:
+ self.main_window.bring_to_front()
+
def link_callbacks(self):
self.tray.actionQuit.triggered.connect(self.quit)
self.tray.actionSettings.triggered.connect(self.settings_callback)
- self.tray.actionReconnect.triggered.connect(self.refresh_callback)
+ self.tray.actionShowWindow.triggered.connect(self.main_window.bring_to_front)
+ self.tray.actionReconnect.triggered.connect(self.reconnect_callback)
+ self.tray.messageClicked.connect(self.main_window.bring_to_front)
+ self.tray.activated.connect(self.tray_activated_callback)
+
+ self.main_window.refresh.connect(self.refresh_applications)
+ self.main_window.delete_all.connect(self.delete_all_messages_callback)
+ self.main_window.application_selection_changed.connect(
+ self.application_selection_changed_callback
+ )
+ self.main_window.delete_message.connect(self.delete_message_callback)
self.watchdog.closed.connect(lambda: self.listener_closed_callback(None, None))
@@ -175,6 +330,8 @@ class MainApplication(QtWidgets.QApplication):
return self.lock_file.tryLock()
def quit(self) -> None:
+ self.main_window.store_state()
+
self.tray.hide()
self.lock_file.unlock()
diff --git a/gotify_tray/gui/__init__.py b/gotify_tray/gui/__init__.py
index 73e901d..a92bfaa 100644
--- a/gotify_tray/gui/__init__.py
+++ b/gotify_tray/gui/__init__.py
@@ -1,2 +1,2 @@
from .MainApplication import start_gui
-from .ServerInfoDialog import ServerInfoDialog
+from .widgets import ServerInfoDialog
diff --git a/gotify_tray/gui/designs/widget_main.py b/gotify_tray/gui/designs/widget_main.py
new file mode 100644
index 0000000..ef54132
--- /dev/null
+++ b/gotify_tray/gui/designs/widget_main.py
@@ -0,0 +1,77 @@
+# Form implementation generated from reading ui file 'gotify_tray/gui/designs\widget_main.ui'
+#
+# Created by: PyQt6 UI code generator 6.1.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MainWindow(object):
+ def setupUi(self, MainWindow):
+ MainWindow.setObjectName("MainWindow")
+ MainWindow.resize(809, 647)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
+ self.gridLayout.setContentsMargins(4, 4, 4, 4)
+ self.gridLayout.setObjectName("gridLayout")
+ self.splitter = QtWidgets.QSplitter(self.centralwidget)
+ self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal)
+ self.splitter.setObjectName("splitter")
+ self.listView_applications = QtWidgets.QListView(self.splitter)
+ self.listView_applications.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
+ self.listView_applications.setObjectName("listView_applications")
+ self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter)
+ self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.label_application = QtWidgets.QLabel(self.verticalLayoutWidget)
+ self.label_application.setObjectName("label_application")
+ self.horizontalLayout.addWidget(self.label_application)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
+ self.horizontalLayout.addItem(spacerItem1)
+ self.pb_refresh = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.pb_refresh.setText("")
+ self.pb_refresh.setObjectName("pb_refresh")
+ self.horizontalLayout.addWidget(self.pb_refresh)
+ self.pb_delete_all = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.pb_delete_all.setText("")
+ self.pb_delete_all.setObjectName("pb_delete_all")
+ self.horizontalLayout.addWidget(self.pb_delete_all)
+ self.verticalLayout_2.addLayout(self.horizontalLayout)
+ self.listView_messages = QtWidgets.QListView(self.verticalLayoutWidget)
+ self.listView_messages.setAutoScroll(True)
+ self.listView_messages.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
+ self.listView_messages.setObjectName("listView_messages")
+ self.verticalLayout_2.addWidget(self.listView_messages)
+ self.gridLayout.addWidget(self.splitter, 0, 0, 1, 1)
+ MainWindow.setCentralWidget(self.centralwidget)
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+ MainWindow.setTabOrder(self.listView_applications, self.listView_messages)
+ MainWindow.setTabOrder(self.listView_messages, self.pb_refresh)
+ MainWindow.setTabOrder(self.pb_refresh, self.pb_delete_all)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "Form"))
+ self.label_application.setText(_translate("MainWindow", "Application"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ MainWindow = QtWidgets.QMainWindow()
+ ui = Ui_MainWindow()
+ ui.setupUi(MainWindow)
+ MainWindow.show()
+ sys.exit(app.exec())
diff --git a/gotify_tray/gui/designs/widget_main.ui b/gotify_tray/gui/designs/widget_main.ui
new file mode 100644
index 0000000..6718d61
--- /dev/null
+++ b/gotify_tray/gui/designs/widget_main.ui
@@ -0,0 +1,118 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 809
+ 647
+
+
+
+ Form
+
+
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+
+
+
-
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Application
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+
+
+
+
+
+
+
+
+
+ listView_applications
+ listView_messages
+ pb_refresh
+ pb_delete_all
+
+
+
+
diff --git a/gotify_tray/gui/designs/widget_message.py b/gotify_tray/gui/designs/widget_message.py
new file mode 100644
index 0000000..2d8cf7e
--- /dev/null
+++ b/gotify_tray/gui/designs/widget_message.py
@@ -0,0 +1,94 @@
+# Form implementation generated from reading ui file 'gotify_tray/gui/designs\widget_message.ui'
+#
+# Created by: PyQt6 UI code generator 6.1.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(454, 122)
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetMinimumSize)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.frame = QtWidgets.QFrame(Form)
+ self.frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
+ self.frame.setObjectName("frame")
+ self.gridLayout_frame = QtWidgets.QGridLayout(self.frame)
+ self.gridLayout_frame.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetMinimumSize)
+ self.gridLayout_frame.setContentsMargins(-1, 0, -1, 0)
+ self.gridLayout_frame.setObjectName("gridLayout_frame")
+ self.label_title = QtWidgets.QLabel(self.frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Minimum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_title.sizePolicy().hasHeightForWidth())
+ self.label_title.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setPointSize(17)
+ font.setBold(False)
+ font.setWeight(50)
+ self.label_title.setFont(font)
+ self.label_title.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)
+ self.label_title.setObjectName("label_title")
+ self.gridLayout_frame.addWidget(self.label_title, 0, 1, 1, 1)
+ self.text_message = QtWidgets.QLabel(self.frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.text_message.sizePolicy().hasHeightForWidth())
+ self.text_message.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setPointSize(11)
+ self.text_message.setFont(font)
+ self.text_message.setWordWrap(True)
+ self.text_message.setOpenExternalLinks(True)
+ self.text_message.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)
+ self.text_message.setObjectName("text_message")
+ self.gridLayout_frame.addWidget(self.text_message, 3, 1, 1, 3)
+ self.label_date = QtWidgets.QLabel(self.frame)
+ font = QtGui.QFont()
+ font.setPointSize(11)
+ self.label_date.setFont(font)
+ self.label_date.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)
+ self.label_date.setObjectName("label_date")
+ self.gridLayout_frame.addWidget(self.label_date, 2, 1, 1, 1)
+ self.pb_delete = QtWidgets.QPushButton(self.frame)
+ self.pb_delete.setText("")
+ self.pb_delete.setFlat(True)
+ self.pb_delete.setObjectName("pb_delete")
+ self.gridLayout_frame.addWidget(self.pb_delete, 0, 3, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
+ self.gridLayout_frame.addItem(spacerItem, 0, 2, 1, 1)
+ self.label_image = QtWidgets.QLabel(self.frame)
+ self.label_image.setText("")
+ self.label_image.setObjectName("label_image")
+ self.gridLayout_frame.addWidget(self.label_image, 0, 0, 1, 1)
+ self.gridLayout.addWidget(self.frame, 0, 0, 1, 1)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.label_title.setText(_translate("Form", "Title"))
+ self.text_message.setText(_translate("Form", "TextLabel"))
+ self.label_date.setText(_translate("Form", "Date"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ Form = QtWidgets.QWidget()
+ ui = Ui_Form()
+ ui.setupUi(Form)
+ Form.show()
+ sys.exit(app.exec())
diff --git a/gotify_tray/gui/designs/widget_message.ui b/gotify_tray/gui/designs/widget_message.ui
new file mode 100644
index 0000000..718406f
--- /dev/null
+++ b/gotify_tray/gui/designs/widget_message.ui
@@ -0,0 +1,152 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 454
+ 122
+
+
+
+ Form
+
+
+
+ QLayout::SetMinimumSize
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ QLayout::SetMinimumSize
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 17
+ 50
+ false
+
+
+
+ Title
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 11
+
+
+
+ TextLabel
+
+
+ true
+
+
+ true
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+ -
+
+
+
+ 11
+
+
+
+ Date
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gotify_tray/gui/designs/widget_server.py b/gotify_tray/gui/designs/widget_server.py
index b2cba3f..faab5ea 100644
--- a/gotify_tray/gui/designs/widget_server.py
+++ b/gotify_tray/gui/designs/widget_server.py
@@ -1,6 +1,6 @@
# Form implementation generated from reading ui file 'gotify_tray/gui/designs\widget_server.ui'
#
-# Created by: PyQt6 UI code generator 6.2.1
+# Created by: PyQt6 UI code generator 6.1.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
@@ -46,8 +46,8 @@ class Ui_Dialog(object):
self.gridLayout.addWidget(self.label_server_info, 1, 1, 1, 2)
self.retranslateUi(Dialog)
- self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
- self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
diff --git a/gotify_tray/gui/designs/widget_settings.py b/gotify_tray/gui/designs/widget_settings.py
index c32e056..caad204 100644
--- a/gotify_tray/gui/designs/widget_settings.py
+++ b/gotify_tray/gui/designs/widget_settings.py
@@ -1,6 +1,6 @@
# Form implementation generated from reading ui file 'gotify_tray/gui/designs\widget_settings.ui'
#
-# Created by: PyQt6 UI code generator 6.2.1
+# Created by: PyQt6 UI code generator 6.1.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
@@ -80,8 +80,8 @@ class Ui_Dialog(object):
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(Dialog)
- self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
- self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.cb_icons_notification, self.spin_priority)
Dialog.setTabOrder(self.spin_priority, self.spin_duration)
diff --git a/gotify_tray/gui/images/refresh.svg b/gotify_tray/gui/images/refresh.svg
new file mode 100644
index 0000000..3257974
--- /dev/null
+++ b/gotify_tray/gui/images/refresh.svg
@@ -0,0 +1,45 @@
+
+
+
diff --git a/gotify_tray/gui/images/status_active.svg b/gotify_tray/gui/images/status_active.svg
new file mode 100644
index 0000000..7af0e8e
--- /dev/null
+++ b/gotify_tray/gui/images/status_active.svg
@@ -0,0 +1,49 @@
+
+
+
+
diff --git a/gotify_tray/gui/images/status_connecting.svg b/gotify_tray/gui/images/status_connecting.svg
new file mode 100644
index 0000000..8b1a364
--- /dev/null
+++ b/gotify_tray/gui/images/status_connecting.svg
@@ -0,0 +1,49 @@
+
+
+
+
diff --git a/gotify_tray/gui/images/status_error.svg b/gotify_tray/gui/images/status_error.svg
new file mode 100644
index 0000000..2f89ca1
--- /dev/null
+++ b/gotify_tray/gui/images/status_error.svg
@@ -0,0 +1,49 @@
+
+
+
+
diff --git a/gotify_tray/gui/images/status_inactive.svg b/gotify_tray/gui/images/status_inactive.svg
new file mode 100644
index 0000000..2d0eba9
--- /dev/null
+++ b/gotify_tray/gui/images/status_inactive.svg
@@ -0,0 +1,49 @@
+
+
+
+
diff --git a/gotify_tray/gui/images/trashcan.svg b/gotify_tray/gui/images/trashcan.svg
new file mode 100644
index 0000000..94b811f
--- /dev/null
+++ b/gotify_tray/gui/images/trashcan.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/gotify_tray/gui/ApplicationModel.py b/gotify_tray/gui/models/ApplicationModel.py
similarity index 67%
rename from gotify_tray/gui/ApplicationModel.py
rename to gotify_tray/gui/models/ApplicationModel.py
index e6435aa..716fe38 100644
--- a/gotify_tray/gui/ApplicationModel.py
+++ b/gotify_tray/gui/models/ApplicationModel.py
@@ -1,6 +1,6 @@
import enum
-from typing import Optional
+from typing import Optional, Union
from PyQt6 import QtCore, QtGui
from gotify_tray import gotify
from gotify_tray.database import Settings
@@ -20,12 +20,15 @@ class ApplicationModelItem(QtGui.QStandardItem):
application: gotify.GotifyApplicationModel,
icon: Optional[QtGui.QIcon] = None,
*args,
- **kwargs
+ **kwargs,
):
super(ApplicationModelItem, self).__init__(application.name)
self.setDropEnabled(False)
self.setData(application, ApplicationItemDataRole.ApplicationRole)
self.setData(icon, ApplicationItemDataRole.IconRole)
+ font = QtGui.QFont()
+ font.fromString(settings.value("ApplicationItem/font", type=str))
+ self.setFont(font)
if icon:
self.setIcon(icon)
@@ -36,6 +39,16 @@ class ApplicationModelItem(QtGui.QStandardItem):
)
+class ApplicationAllMessagesItem(QtGui.QStandardItem):
+ def __init__(self, *args, **kwargs):
+ super(ApplicationAllMessagesItem, self).__init__("ALL MESSAGES")
+ self.setDropEnabled(False)
+ self.setDragEnabled(False)
+ font = QtGui.QFont()
+ font.fromString(settings.value("ApplicationItem/font", type=str))
+ self.setFont(font)
+
+
class ApplicationModel(QtGui.QStandardItemModel):
def __init__(self):
super(ApplicationModel, self).__init__()
@@ -43,10 +56,17 @@ class ApplicationModel(QtGui.QStandardItemModel):
ApplicationModelItem(gotify.GotifyApplicationModel({"name": ""}), None)
)
- def setItem(self, row: int, column: int, item: ApplicationModelItem,) -> None:
+ def setItem(
+ self,
+ row: int,
+ column: int,
+ item: Union[ApplicationModelItem, ApplicationAllMessagesItem],
+ ) -> None:
super(ApplicationModel, self).setItem(row, column, item)
- def itemFromIndex(self, index: QtCore.QModelIndex) -> ApplicationModelItem:
+ def itemFromIndex(
+ self, index: QtCore.QModelIndex
+ ) -> Union[ApplicationModelItem, ApplicationAllMessagesItem]:
return super(ApplicationModel, self).itemFromIndex(index)
def itemFromId(self, appid: int) -> Optional[ApplicationModelItem]:
diff --git a/gotify_tray/gui/models/MessagesModel.py b/gotify_tray/gui/models/MessagesModel.py
new file mode 100644
index 0000000..b74d895
--- /dev/null
+++ b/gotify_tray/gui/models/MessagesModel.py
@@ -0,0 +1,23 @@
+import enum
+
+from typing import cast
+from PyQt6 import QtCore, QtGui
+from gotify_tray import gotify
+
+
+class MessageItemDataRole(enum.IntEnum):
+ MessageRole = QtCore.Qt.ItemDataRole.UserRole + 1
+
+
+class MessagesModelItem(QtGui.QStandardItem):
+ def __init__(self, message: gotify.GotifyMessageModel, *args, **kwargs):
+ super(MessagesModelItem, self).__init__()
+ self.setData(message, MessageItemDataRole.MessageRole)
+
+
+class MessagesModel(QtGui.QStandardItemModel):
+ def setItem(self, row: int, column: int, item: MessagesModelItem) -> None:
+ super(MessagesModel, self).setItem(row, column, item)
+
+ def itemFromIndex(self, index: QtCore.QModelIndex) -> MessagesModelItem:
+ return cast(MessagesModelItem, super(MessagesModel, self).itemFromIndex(index))
diff --git a/gotify_tray/gui/models/__init__.py b/gotify_tray/gui/models/__init__.py
new file mode 100644
index 0000000..e298d93
--- /dev/null
+++ b/gotify_tray/gui/models/__init__.py
@@ -0,0 +1,7 @@
+from .ApplicationModel import (
+ ApplicationAllMessagesItem,
+ ApplicationModelItem,
+ ApplicationModel,
+ ApplicationItemDataRole,
+)
+from .MessagesModel import MessagesModelItem, MessagesModel, MessageItemDataRole
diff --git a/gotify_tray/gui/widgets/MainWindow.py b/gotify_tray/gui/widgets/MainWindow.py
new file mode 100644
index 0000000..6f2fe13
--- /dev/null
+++ b/gotify_tray/gui/widgets/MainWindow.py
@@ -0,0 +1,154 @@
+from PyQt6 import QtCore, QtGui, QtWidgets
+from ..designs.widget_main import Ui_MainWindow
+from .StatusWidget import StatusWidget
+from ..models import (
+ ApplicationModel,
+ MessagesModel,
+ MessagesModelItem,
+)
+from . import MessageWidget
+
+from gotify_tray.__version__ import __title__
+from gotify_tray.database import Settings
+from gotify_tray.utils import get_abs_path
+
+
+settings = Settings("gotify-tray")
+
+
+class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
+ refresh = QtCore.pyqtSignal()
+ delete_all = QtCore.pyqtSignal(QtGui.QStandardItem)
+ delete_message = QtCore.pyqtSignal(MessagesModelItem)
+ application_selection_changed = QtCore.pyqtSignal(QtGui.QStandardItem)
+
+ def __init__(
+ self, application_model: ApplicationModel, messages_model: MessagesModel
+ ):
+ super(MainWindow, self).__init__()
+ self.setupUi(self)
+
+ self.setWindowTitle(__title__)
+
+ self.application_model = application_model
+ self.messages_model = messages_model
+
+ self.listView_applications.setModel(application_model)
+ self.listView_messages.setModel(messages_model)
+
+ # Do not expand the applications listview when resizing
+ self.splitter.setStretchFactor(0, 0)
+ self.splitter.setStretchFactor(1, 1)
+
+ self.status_widget = StatusWidget()
+ self.horizontalLayout.insertWidget(0, self.status_widget)
+
+ # Set button icons
+ self.pb_refresh.setIcon(
+ QtGui.QIcon(get_abs_path(f"gotify_tray/gui/images/refresh.svg"))
+ )
+ self.pb_delete_all.setIcon(
+ QtGui.QIcon(get_abs_path(f"gotify_tray/gui/images/trashcan.svg"))
+ )
+
+ # Resize the labels and icons
+ size = settings.value("MainWindow/label/size", type=int)
+ self.status_widget.setFixedSize(QtCore.QSize(size, size))
+
+ size = settings.value("MainWindow/button/size", type=int)
+ self.pb_refresh.setFixedSize(QtCore.QSize(size, size))
+ self.pb_delete_all.setFixedSize(QtCore.QSize(size, size))
+ self.pb_refresh.setIconSize(QtCore.QSize(0.7 * size, 0.7 * size))
+ self.pb_delete_all.setIconSize(QtCore.QSize(0.9 * size, 0.9 * size))
+
+ size = settings.value("MainWindow/application/icon/size", type=int)
+ self.listView_applications.setIconSize(QtCore.QSize(size, size))
+
+ font_title = QtGui.QFont()
+ font_title.fromString(settings.value("MainWindow/font/application", type=str))
+ self.label_application.setFont(font_title)
+
+ # Set tooltips
+ self.pb_refresh.setToolTip("Refresh")
+ self.pb_delete_all.setToolTip("Delete all messages")
+
+ self.restore_state()
+
+ self.link_callbacks()
+
+ def set_active(self):
+ self.status_widget.set_active()
+
+ def set_connecting(self):
+ self.status_widget.set_connecting()
+
+ def set_inactive(self):
+ self.status_widget.set_inactive()
+
+ def set_error(self):
+ self.status_widget.set_error()
+
+ def insert_message_widget(
+ self, message_item: MessagesModelItem, image_path: str = ""
+ ):
+ message_widget = MessageWidget(message_item, image_path=image_path)
+ self.listView_messages.setIndexWidget(
+ self.messages_model.indexFromItem(message_item), message_widget
+ )
+ message_widget.deletion_requested.connect(self.delete_message.emit)
+
+ def currentApplicationIndex(self) -> QtCore.QModelIndex:
+ return self.listView_applications.selectionModel().currentIndex()
+
+ def application_selection_changed_callback(
+ self, current: QtCore.QModelIndex, previous: QtCore.QModelIndex
+ ):
+ if item := self.application_model.itemFromIndex(current):
+ self.label_application.setText(item.text())
+ self.application_selection_changed.emit(item)
+
+ def delete_all_callback(self):
+ index = self.currentApplicationIndex()
+ if item := self.application_model.itemFromIndex(index):
+ self.delete_all.emit(item)
+
+ def disable_applications(self):
+ self.listView_applications.clearSelection()
+ self.listView_applications.setDisabled(True)
+
+ def enable_applications(self):
+ self.listView_applications.setEnabled(True)
+ self.listView_applications.setCurrentIndex(self.application_model.index(0, 0))
+
+ def bring_to_front(self):
+ self.ensurePolished()
+ self.setWindowState(
+ self.windowState() & ~QtCore.Qt.WindowState.WindowMinimized
+ | QtCore.Qt.WindowState.WindowActive
+ )
+ self.show()
+ self.activateWindow()
+
+ def link_callbacks(self):
+ self.pb_refresh.clicked.connect(self.refresh.emit)
+ self.pb_delete_all.clicked.connect(self.delete_all_callback)
+
+ self.listView_applications.selectionModel().currentChanged.connect(
+ self.application_selection_changed_callback
+ )
+
+ def store_state(self):
+ settings.setValue("MainWindow/geometry", self.saveGeometry())
+ settings.setValue("MainWindow/state", self.saveState())
+ settings.setValue("MainWindow/splitter", self.splitter.saveState())
+
+ def restore_state(self):
+ if geometry := settings.value("MainWindow/geometry", type=QtCore.QByteArray):
+ self.restoreGeometry(geometry)
+ if state := settings.value("MainWindow/state", type=QtCore.QByteArray):
+ self.restoreState(state)
+ if splitter := settings.value("MainWindow/splitter", type=QtCore.QByteArray):
+ self.splitter.restoreState(splitter)
+
+ def closeEvent(self, e: QtGui.QCloseEvent) -> None:
+ self.hide()
diff --git a/gotify_tray/gui/widgets/MessageWidget.py b/gotify_tray/gui/widgets/MessageWidget.py
new file mode 100644
index 0000000..d8400eb
--- /dev/null
+++ b/gotify_tray/gui/widgets/MessageWidget.py
@@ -0,0 +1,71 @@
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+from ..models.MessagesModel import MessageItemDataRole, MessagesModelItem
+from ..designs.widget_message import Ui_Form
+from gotify_tray.database import Settings
+from gotify_tray.utils import convert_links
+
+
+settings = Settings("gotify-tray")
+
+
+class MessageWidget(QtWidgets.QWidget, Ui_Form):
+ deletion_requested = QtCore.pyqtSignal(MessagesModelItem)
+
+ def __init__(self, message_item: MessagesModelItem, image_path: str = ""):
+ super(MessageWidget, self).__init__()
+ self.setupUi(self)
+ self.setAutoFillBackground(True)
+
+ self.message_item = message_item
+ message = message_item.data(MessageItemDataRole.MessageRole)
+
+ # Fonts
+ font_title = QtGui.QFont()
+ font_date = QtGui.QFont()
+ font_content = QtGui.QFont()
+
+ font_title.fromString(settings.value("MessageWidget/font/title", type=str))
+ font_date.fromString(settings.value("MessageWidget/font/date", type=str))
+ font_content.fromString(settings.value("MessageWidget/font/content", type=str))
+
+ self.label_title.setFont(font_title)
+ self.label_date.setFont(font_date)
+ self.text_message.setFont(font_content)
+
+ # Display message contents
+ self.label_title.setText(message.title)
+ self.label_date.setText(message.date.strftime("%Y-%m-%d, %H:%M"))
+
+ if markdown := message.get("extras", {}).get("client::display", {}).get("contentType") == "text/markdown":
+ self.text_message.setTextFormat(QtCore.Qt.TextFormat.MarkdownText)
+ self.text_message.setText(convert_links(message.message))
+
+ # Show the application icon
+ if image_path:
+ image_size = settings.value("MessageWidget/image/size", type=int)
+ self.label_image.setFixedSize(QtCore.QSize(image_size, image_size))
+ pixmap = QtGui.QPixmap(image_path).scaled(image_size, image_size, aspectRatioMode=QtCore.Qt.AspectRatioMode.KeepAspectRatioByExpanding)
+ self.label_image.setPixmap(pixmap)
+ else:
+ self.label_image.hide()
+
+ # Set MessagesModelItem's size hint based on the size of this widget
+ self.gridLayout_frame.setContentsMargins(10, 5, 10, 5)
+ self.gridLayout.setContentsMargins(5, 15, 5, 15)
+ self.adjustSize()
+ size_hint = self.message_item.sizeHint()
+ self.message_item.setSizeHint(
+ QtCore.QSize(
+ size_hint.width(),
+ self.height()
+ )
+ )
+
+ self.pb_delete.setIcon(QtGui.QIcon("gotify_tray/gui/images/trashcan.svg"))
+ self.pb_delete.setIconSize(QtCore.QSize(24, 24))
+
+ self.link_callbacks()
+
+ def link_callbacks(self):
+ self.pb_delete.clicked.connect(lambda: self.deletion_requested.emit(self.message_item))
\ No newline at end of file
diff --git a/gotify_tray/gui/ServerInfoDialog.py b/gotify_tray/gui/widgets/ServerInfoDialog.py
similarity index 98%
rename from gotify_tray/gui/ServerInfoDialog.py
rename to gotify_tray/gui/widgets/ServerInfoDialog.py
index 6871d67..3c30ac3 100644
--- a/gotify_tray/gui/ServerInfoDialog.py
+++ b/gotify_tray/gui/widgets/ServerInfoDialog.py
@@ -2,7 +2,7 @@ from gotify_tray.gotify.models import GotifyVersionModel
from gotify_tray.tasks import VerifyServerInfoTask
from PyQt6 import QtWidgets
-from .designs.widget_server import Ui_Dialog
+from ..designs.widget_server import Ui_Dialog
class ServerInfoDialog(QtWidgets.QDialog, Ui_Dialog):
diff --git a/gotify_tray/gui/SettingsDialog.py b/gotify_tray/gui/widgets/SettingsDialog.py
similarity index 98%
rename from gotify_tray/gui/SettingsDialog.py
rename to gotify_tray/gui/widgets/SettingsDialog.py
index 17e41ff..72b5150 100644
--- a/gotify_tray/gui/SettingsDialog.py
+++ b/gotify_tray/gui/widgets/SettingsDialog.py
@@ -5,7 +5,7 @@ from gotify_tray.database import Settings
from gotify_tray.utils import verify_server
from PyQt6 import QtCore, QtGui, QtWidgets
-from .designs.widget_settings import Ui_Dialog
+from ..designs.widget_settings import Ui_Dialog
logger = logging.getLogger("gotify-tray")
diff --git a/gotify_tray/gui/widgets/StatusWidget.py b/gotify_tray/gui/widgets/StatusWidget.py
new file mode 100644
index 0000000..25ff5ae
--- /dev/null
+++ b/gotify_tray/gui/widgets/StatusWidget.py
@@ -0,0 +1,34 @@
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+from gotify_tray.database import Settings
+from gotify_tray.utils import get_abs_path
+
+
+settings = Settings("gotify-tray")
+
+
+class StatusWidget(QtWidgets.QLabel):
+ def __init__(self):
+ super(StatusWidget, self).__init__()
+ self.setFixedSize(QtCore.QSize(20, 20))
+ self.setScaledContents(True)
+ self.set_connecting()
+
+ def set_status(self, image: str):
+ self.setPixmap(QtGui.QPixmap(get_abs_path(f"gotify_tray/gui/images/{image}")))
+
+ def set_active(self):
+ self.setToolTip("Listening for new messages")
+ self.set_status("status_active.svg")
+
+ def set_connecting(self):
+ self.setToolTip("Connecting...")
+ self.set_status("status_connecting.svg")
+
+ def set_inactive(self):
+ self.setToolTip("Listener inactive")
+ self.set_status("status_inactive.svg")
+
+ def set_error(self):
+ self.setToolTip("Listener error")
+ self.set_status("status_error.svg")
diff --git a/gotify_tray/gui/Tray.py b/gotify_tray/gui/widgets/Tray.py
similarity index 89%
rename from gotify_tray/gui/Tray.py
rename to gotify_tray/gui/widgets/Tray.py
index 4030166..54ca1a0 100644
--- a/gotify_tray/gui/Tray.py
+++ b/gotify_tray/gui/widgets/Tray.py
@@ -27,6 +27,11 @@ class Tray(QtWidgets.QSystemTrayIcon):
menu.addSeparator()
+ self.actionShowWindow = QtGui.QAction("Show Window", self)
+ menu.addAction(self.actionShowWindow)
+
+ menu.addSeparator()
+
self.actionReconnect = QtGui.QAction("Reconnect", self)
menu.addAction(self.actionReconnect)
diff --git a/gotify_tray/gui/widgets/__init__.py b/gotify_tray/gui/widgets/__init__.py
new file mode 100644
index 0000000..d43f65f
--- /dev/null
+++ b/gotify_tray/gui/widgets/__init__.py
@@ -0,0 +1,6 @@
+from .MessageWidget import MessageWidget
+from .MainWindow import MainWindow
+from .ServerInfoDialog import ServerInfoDialog
+from .SettingsDialog import SettingsDialog
+from .StatusWidget import StatusWidget
+from .Tray import Tray
diff --git a/gotify_tray/tasks.py b/gotify_tray/tasks.py
index 6143fd6..b74c81b 100644
--- a/gotify_tray/tasks.py
+++ b/gotify_tray/tasks.py
@@ -38,6 +38,32 @@ class BaseTask(QtCore.QThread):
self.running = False
+class DeleteMessageTask(BaseTask):
+ deleted = pyqtSignal(bool)
+
+ def __init__(self, message_id: int, gotify_client: gotify.GotifyClient):
+ super(DeleteMessageTask, self).__init__()
+ self.message_id = message_id
+ self.gotify_client = gotify_client
+
+ def task(self):
+ success = self.gotify_client.delete_message(self.message_id)
+ self.deleted.emit(success)
+
+
+class DeleteApplicationMessagesTask(BaseTask):
+ deleted = pyqtSignal(bool)
+
+ def __init__(self, appid: int, gotify_client: gotify.GotifyClient):
+ super(DeleteApplicationMessagesTask, self).__init__()
+ self.appid = appid
+ self.gotify_client = gotify_client
+
+ def task(self):
+ success = self.gotify_client.delete_application_messages(self.appid)
+ self.deleted.emit(success)
+
+
class DeleteAllMessagesTask(BaseTask):
deleted = pyqtSignal(bool)
@@ -66,6 +92,39 @@ class GetApplicationsTask(BaseTask):
self.success.emit(result)
+class GetApplicationMessagesTask(BaseTask):
+ success = pyqtSignal(gotify.GotifyPagedMessagesModel)
+ error = pyqtSignal(gotify.GotifyErrorModel)
+
+ def __init__(self, appid: int, gotify_client: gotify.GotifyClient):
+ super(GetApplicationMessagesTask, self).__init__()
+ self.appid = appid
+ self.gotify_client = gotify_client
+
+ def task(self):
+ result = self.gotify_client.get_application_messages(self.appid)
+ if isinstance(result, gotify.GotifyErrorModel):
+ self.error.emit(result)
+ else:
+ self.success.emit(result)
+
+
+class GetMessagesTask(BaseTask):
+ success = pyqtSignal(gotify.GotifyPagedMessagesModel)
+ error = pyqtSignal(gotify.GotifyErrorModel)
+
+ def __init__(self, gotify_client: gotify.GotifyClient):
+ super(GetMessagesTask, self).__init__()
+ self.gotify_client = gotify_client
+
+ def task(self):
+ result = self.gotify_client.get_messages()
+ if isinstance(result, gotify.GotifyErrorModel):
+ self.error.emit(result)
+ else:
+ self.success.emit(result)
+
+
class VerifyServerInfoTask(BaseTask):
success = pyqtSignal(GotifyVersionModel)
incorrect_token = pyqtSignal(GotifyVersionModel)
diff --git a/gotify_tray/utils.py b/gotify_tray/utils.py
index 12bbbba..46935ef 100644
--- a/gotify_tray/utils.py
+++ b/gotify_tray/utils.py
@@ -1,3 +1,9 @@
+import os
+import re
+
+from pathlib import Path
+
+
def verify_server(force_new: bool = False) -> bool:
from gotify_tray.gui import ServerInfoDialog
from gotify_tray.database import Settings
@@ -17,3 +23,26 @@ def verify_server(force_new: bool = False) -> bool:
return False
else:
return True
+
+
+def convert_links(text):
+ _link = re.compile(
+ r'(?:(https://|http://)|(www\.))(\S+\b/?)([!"#$%&\'()*+,\-./:;<=>?@[\\\]^_`{|}~]*)(\s|$)',
+ re.I,
+ )
+
+ def replace(match):
+ groups = match.groups()
+ protocol = groups[0] or "" # may be None
+ www_lead = groups[1] or "" # may be None
+ return '{0}{1}{2}{3}{4}'.format(
+ protocol, www_lead, *groups[2:]
+ )
+
+ return _link.sub(replace, text)
+
+
+def get_abs_path(s) -> str:
+ h = Path(__file__).parent.parent
+ p = Path(s)
+ return os.path.join(h, p).replace("\\", "/")