Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
relativisticelectron committed Jun 20, 2022
1 parent 2b8ac1f commit 68d9640
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 85 deletions.
52 changes: 23 additions & 29 deletions src/cryptoadvance/specter/notifications/notification_manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import logging

from cryptoadvance.specter import notifications

logger = logging.getLogger(__name__)

from .notifications import Notification
Expand Down Expand Up @@ -59,18 +61,16 @@ def show(self, notification):
If a target_ui of notification['target_uis'] is not is_available, then try with the next target_ui
"""
if notification["target_uis"] == ["internal_notification"]:
if notification.target_uis == ["internal_notification"]:
return

logger.debug(f"show {notification}")
ui_notification_of_user = self.get_ui_notification_of_user(
notification["user_id"]
)
ui_notification_of_user = self.get_ui_notification_of_user(notification.user_id)
logger.debug(f"ui_notification_of_user {ui_notification_of_user}")
broadcast_on_ui_notification = {
ui_notification
for ui_notification in ui_notification_of_user
if (ui_notification.name in notification["target_uis"])
if (ui_notification.name in notification.target_uis)
}

logger.debug(f"show {notification} on {broadcast_on_ui_notification}")
Expand Down Expand Up @@ -105,43 +105,43 @@ def treat_internal_message(self, internal_notification):
- handling callbacks (like on_close or on_show)
- messaging back that a target ui is unavailable (the notification is then also rebroadcasted)
"""
if "internal_notification" not in internal_notification["target_uis"]:
if "internal_notification" not in internal_notification.target_uis:
return internal_notification
logger.debug(f"treat_internal_message {internal_notification}")

referenced_notification = self.find_notification(
internal_notification["data"]["id"]
internal_notification.data["id"]
)

if (
internal_notification["title"]
internal_notification.title
== "notification_deactivate_target_ui_and_rebroadcast"
):
# deactivate target_ui and rebroadcast
logger.debug(
f'{internal_notification["data"]["target_ui"]} is unavailable, now deactivating this target_ui and rebroadcasting'
f'{internal_notification.data["target_ui"]} is unavailable, now deactivating this target_ui and rebroadcasting'
)
self.deactivate_target_ui(internal_notification["data"]["target_ui"])
self.deactivate_target_ui(internal_notification.data["target_ui"])
if not referenced_notification:
return
self.show(referenced_notification)

if internal_notification["title"] == "on_show":
if internal_notification.title == "on_show":
self.set_notification_shown(
referenced_notification["id"],
internal_notification["data"]["target_ui"],
referenced_notification.id,
internal_notification.data["target_ui"],
)

if internal_notification["title"] == "on_close":
if internal_notification.title == "on_close":
if not referenced_notification:
return

for ui_notification in self.ui_notifications:
# perhaps the target_ui was not available and it was displayed in another ui_notification. However still call the on_close of the original target_ui
if ui_notification.name == internal_notification["data"]["target_ui"]:
if ui_notification.name == internal_notification.data["target_ui"]:
ui_notification.on_close(
referenced_notification["id"],
internal_notification["data"]["target_ui"],
referenced_notification.id,
internal_notification.data["target_ui"],
)

def get_default_target_ui_name(self):
Expand All @@ -152,11 +152,8 @@ def get_all_target_ui_names(self):

def create_notification(self, title, user_id, **kwargs):
"""
The arguments are identical to Notification(....), e.g.
- title
- None
- body=None
- target_uis='default'
Creates a notification (which adds it to self.notifications) and also broadcasts it to ui_notifications.
kwargs are the optional arguments of Notification
"""
logger.debug(
f"Starting to ceated notification with title, **kwargs {title, kwargs}"
Expand All @@ -172,7 +169,7 @@ def create_notification(self, title, user_id, **kwargs):
logger.debug(f"Middle of creating notification {notification}")

# treat an internal (notification) message
if "internal_notification" in notification["target_uis"]:
if "internal_notification" in notification.target_uis:
# in case treat_internal_message returns a notification, then proceed with that
return self.treat_internal_message(notification)

Expand All @@ -187,19 +184,16 @@ def flash(self, message: str, user_id, category: str = "message"):

def create_and_show(self, title, user_id, **kwargs):
"""
The arguments are identical to Notification(....), e.g.
- title
- None
- body=None
- target_uis='default'
Creates a notification (which adds it to self.notifications) and also broadcasts it to ui_notifications.
kwargs are the optional arguments of Notification
"""
notification = self.create_notification(title, user_id, **kwargs)
if notification:
self.show(notification)

def find_notification(self, notification_id):
for notification in self.notifications:
if notification["id"] == notification_id:
if notification.id == notification_id:
return notification

def delete_notification(self, notification):
Expand Down
88 changes: 42 additions & 46 deletions src/cryptoadvance/specter/notifications/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class NotificationTypes:
exception = "exception"


class Notification(dict):
class Notification:
"""
A Notification is a datastructure to store title, body, ...
The field names should be ideally identical to https://notifications.spec.whatwg.org/#api
Expand All @@ -26,88 +26,86 @@ def __init__(
default_target_ui,
all_target_uis,
user_id,
target_uis="default",
notification_type=None,
body=None,
data=None,
target_uis="default",
**kwargs,
image=None,
icon=None,
timeout=None,
):
self["title"] = str(title)
self["date"] = datetime.datetime.now()
self["last_shown_date"] = dict() # structure {'target_ui' : date}
self["image"] = None
self["icon"] = None
self["body"] = body
self["data"] = data
self["timeout"] = None # [ms]
self["user_id"] = user_id
self.title = str(title)
self.user_id = user_id
self.date = datetime.datetime.now()
self.last_shown_date = dict() # structure {'target_ui' : date}

if not target_uis:
target_uis = "default"
self["target_uis"] = (
self.target_uis = (
{target_uis} if isinstance(target_uis, str) else set(target_uis)
)
self.cleanup_target_uis(default_target_ui, all_target_uis)

# clean up invalid NotificationTypes
self["type"] = (
self.notification_type = (
notification_type if notification_type else NotificationTypes.information
)
if self["type"] not in {
if self.notification_type not in {
NotificationTypes.debug,
NotificationTypes.information,
NotificationTypes.warning,
NotificationTypes.error,
NotificationTypes.exception,
}:
self["type"] = NotificationTypes.information

# take over all remeining kwargs
for key, value in kwargs.items():
self[key] = value
self.notification_type = NotificationTypes.information

self.cleanup_target_uis(default_target_ui, all_target_uis)
self.body = body
self.data = data
self.image = image
self.icon = icon
self.timeout = timeout # [ms]

# set id (dependent on all other properties, so must eb set last)
self["id"] = None
self.id = None
self._set_id()

def __str__(self):
return str(self.__dict__)

def _set_id(self):
reduced_dict = self.copy()
reduced_dict = self.__dict__.copy()
del reduced_dict["id"]
self["id"] = hashlib.sha256(str(self).encode()).hexdigest()
self.id = hashlib.sha256(str(reduced_dict).encode()).hexdigest()

def set_shown(self, target_ui, date=None):
self["last_shown_date"][target_ui] = date if date else datetime.datetime.now()
self.last_shown_date[target_ui] = date if date else datetime.datetime.now()
logger.debug(f"set_notification_shown {self}")

def cleanup_target_uis(self, default_target_ui, all_target_uis):
# clean up the notification['target_uis']
if "target_uis" not in self:
self["target_uis"] = set()

if "internal_notification" in self["target_uis"]:
if "internal_notification" in self.target_uis:
# no cleanup for internal_notification
return

if "all" in self["target_uis"]:
self["target_uis"] = all_target_uis
if "all" in self.target_uis:
target_uis = all_target_uis

# replace the "default" target_ui with the 0.th ui_notifications
if "default" in self["target_uis"]:
self["target_uis"].remove("default")
if "default" in self.target_uis:
self.target_uis.remove("default")
if default_target_ui:
self["target_uis"].add(default_target_ui)
self.target_uis.add(default_target_ui)

def to_js_notification(self):
"""
Returns a datastructure such that a Notification(result) can be called https://notifications.spec.whatwg.org/#api
The returned structure is:
{
"title": self["title"],
"id": self["id"],
"type": self["type"],
"timeout": self["timeout"],
"title": title,
"id": id,
"notification_type": notification_type,
"timeout": timeout,
"options": {
body = "",
image = None,
Expand All @@ -116,17 +114,15 @@ def to_js_notification(self):
"options" fields are optional, and can be looked up here: https://notifications.spec.whatwg.org/#dictdef-notificationoptions
"""
js_notification = {
"title": self["title"],
"id": self["id"],
"type": self["type"],
"timeout": self["timeout"],
"title": self.title,
"id": self.id,
"notification_type": self.notification_type,
"timeout": self.timeout,
"options": {},
}

for key, value in self.items():
if key in js_notification:
continue
if value is None:
for key, value in self.__dict__.items():
if key in js_notification or value is None:
continue
js_notification["options"][key] = value

Expand Down
14 changes: 7 additions & 7 deletions src/cryptoadvance/specter/notifications/ui_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self):
def show(self, notification):
if (
not self.is_available
or notification["type"] not in self.compatible_notification_types
or notification.notification_type not in self.compatible_notification_types
):
return
print(notification)
Expand All @@ -50,12 +50,12 @@ def __init__(self):
def show(self, notification):
if (
not self.is_available
or notification["type"] not in self.compatible_notification_types
or notification.notification_type not in self.compatible_notification_types
):
return
logger.info(
str(notification),
exc_info=notification["type"]
exc_info=notification.notification_type
in {NotificationTypes.error, NotificationTypes.exception},
)
notification.set_shown(self.name)
Expand All @@ -79,12 +79,12 @@ def __init__(self, user_id):
def show(self, notification):
if (
not self.is_available
or notification["type"] not in self.compatible_notification_types
or notification.notification_type not in self.compatible_notification_types
):
return
flash(
f"{notification['title']}\n{notification['body'] if notification['body'] else ''}",
notification["type"],
f"{notification.title}\n{notification.body if notification.body else ''}",
notification.notification_type,
)
notification.set_shown(self.name)
return True # successfully broadcasted
Expand All @@ -110,7 +110,7 @@ def show(self, notification):
"""
if (
not self.is_available
or notification["type"] not in self.compatible_notification_types
or notification.notification_type not in self.compatible_notification_types
):
return
self.js_notification_buffer.append(notification.to_js_notification())
Expand Down
4 changes: 4 additions & 0 deletions src/cryptoadvance/specter/server_endpoints/wallets_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from ..util.price_providers import get_price_at
from ..util.tx import decoderawtransaction
from embit.descriptor.checksum import add_checksum
from ..notifications.notifications import Notification
from ..notifications.ui_notifications import JSNotifications
from ..notifications.current_flask_user import flash

Expand Down Expand Up @@ -153,6 +154,8 @@ def create_notification():
'image' : image_url,
'icon' : icon,
}
The options are the optional arguments of Notification()
}
If a value is itself a list or dict (like target_uis) it has to be in a json format.
Expand All @@ -165,6 +168,7 @@ def create_notification():
)

options = json.loads(request.form.get("options", "{}"))

logger.debug(
f"wallets_endpoint_api create_notification with title {title} and options {options}"
)
Expand Down
6 changes: 3 additions & 3 deletions src/cryptoadvance/specter/templates/includes/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,11 @@ function js_message_box(js_notification){


function js_console(js_notification){
if (js_notification['type'] == 'error'){
if (js_notification['notification_type'] == 'error'){
console.error(js_notification)
} else if (js_notification['type'] == 'exception'){
} else if (js_notification['notification_type'] == 'exception'){
console.error(js_notification)
} else if (js_notification['type'] == 'warning'){
} else if (js_notification['notification_type'] == 'warning'){
console.warn(js_notification)
} else {
console.log(js_notification);
Expand Down

0 comments on commit 68d9640

Please sign in to comment.