from __future__ import annotations
import copy
import datetime
import urllib.parse
import uuid
import warnings
from collections.abc import Iterable
from typing import Callable, Literal, Optional, TypeVar, Union
from toasts_winrt.windows.ui.notifications import ToastDismissedEventArgs, ToastFailedEventArgs
from .events import ToastActivatedEventArgs
from .toast_audio import ToastAudio
from .wrappers import (
ToastButton,
ToastDisplayImage,
ToastDuration,
ToastInputSelectionBox,
ToastInputTextBox,
ToastProgressBar,
ToastScenario,
ToastSystemButton,
)
ToastInput = TypeVar("ToastInput", ToastInputTextBox, ToastInputSelectionBox)
[docs]class Toast:
audio: Optional[ToastAudio]
"""The custom audio configuration for the toast"""
duration: Literal[ToastDuration.Default, ToastDuration.Long, ToastDuration.Short]
""":class:`~windows_toasts.wrappers.ToastDuration`, be it the default, short, or long"""
expiration_time: Optional[datetime.datetime]
"""The time for the toast to expire on in the action center. If it is on-screen, nothing will happen"""
group: Optional[str]
"""A generic identifier, where you can assign groups like "wallPosts", "messages", "friendRequests", etc."""
scenario: ToastScenario
"""Scenario for the toast"""
suppress_popup: bool
"""Whether to suppress the toast popup and relegate it immediately to the action center"""
timestamp: Optional[datetime.datetime]
"""A custom timestamp. If you don't provide one, Windows uses the time that your notification was sent"""
progress_bar: Optional[ToastProgressBar]
"""An adjustable progress bar for the toast"""
on_activated: Optional[Callable[[ToastActivatedEventArgs], None]]
"""Callable to execute when the toast is clicked if basic, or a button is clicked if interactable"""
on_dismissed: Optional[Callable[[ToastDismissedEventArgs], None]]
"""Callable to execute when the toast is dismissed (X is clicked or times out) if interactable"""
on_failed: Optional[Callable[[ToastFailedEventArgs], None]]
"""Callable to execute when the toast fails to display"""
actions: list[Union[ToastButton, ToastSystemButton]]
"""List of buttons to include. Implemented through :func:`AddAction`"""
images: list[ToastDisplayImage]
"""See :func:`AddImage`"""
inputs: list[ToastInput]
"""Text/selection input boxes"""
text_fields: list[Optional[str]]
"""Various text fields"""
tag: str
"""uuid of a tag for the toast"""
updates: int
"""Number of times the toast has been updated; mostly for internal use"""
_launch_action: Optional[str]
"""Protocol to launch when the toast is clicked"""
[docs] def __init__(
self,
text_fields: Union[list[Optional[str]], tuple[Optional[str]], set[Optional[str]]] = None,
audio: Optional[ToastAudio] = None,
duration: ToastDuration = ToastDuration.Default,
expiration_time: Optional[datetime.datetime] = None,
group: Optional[str] = None,
launch_action: Optional[str] = None,
progress_bar: Optional[ToastProgressBar] = None,
scenario: ToastScenario = ToastScenario.Default,
suppress_popup: bool = False,
timestamp: Optional[datetime.datetime] = None,
on_activated: Optional[Callable[[ToastActivatedEventArgs], None]] = None,
on_dismissed: Optional[Callable[[ToastDismissedEventArgs], None]] = None,
on_failed: Optional[Callable[[ToastFailedEventArgs], None]] = None,
actions: Iterable[Union[ToastButton, ToastSystemButton]] = (),
images: Iterable[ToastDisplayImage] = (),
inputs: Iterable[ToastInput] = (),
) -> None:
"""
Initialise a toast
:param actions: Iterable of actions to add; see :meth:`AddAction`
:type actions: Iterable[Union[ToastButton, ToastSystemButton]]
:param images: See :meth:`AddImage`
:type images: Iterable[ToastDisplayImage]
:param inputs: See :meth:`AddInput`
:type inputs: Iterable[ToastInput]
"""
self.audio = audio
self.duration = duration
self.scenario = scenario
self.progress_bar = progress_bar
self.timestamp = timestamp
self.group = group
self.expiration_time = expiration_time
self.suppress_popup = suppress_popup
self.launch_action = launch_action
self.actions = []
self.images = []
self.inputs = []
self.text_fields = [] if text_fields is None else list(text_fields)
for action in actions:
self.AddAction(action)
for image in images:
self.AddImage(image)
for toast_input in inputs:
self.AddInput(toast_input)
self.on_activated = on_activated
self.on_dismissed = on_dismissed
self.on_failed = on_failed
self.tag = str(uuid.uuid4())
self.updates = 0
def __eq__(self, other):
if isinstance(other, Toast):
return other.tag == self.tag
return False
def __repr__(self):
kws = [f"{key}={value!r}" for key, value in self.__dict__.items()]
return "{}({})".format(type(self).__name__, ", ".join(kws))
[docs] def AddAction(self, action: Union[ToastButton, ToastSystemButton]) -> None:
"""
Add an action to the action list. For example, if you're setting up a reminder,
you would use 'action=remindlater&date=2020-01-20' as arguments. Maximum of five.
:type action: Union[ToastButton, ToastSystemButton]
"""
if len(self.actions) + len(self.inputs) >= 5:
warnings.warn(
f"Cannot add action '{action.content}', you've already reached the maximum of five actions + inputs"
)
return
self.actions.append(action)
[docs] def AddImage(self, image: ToastDisplayImage) -> None:
"""
Adds an the image that will be displayed on the toast.
If using WindowsToaster, a maximum of two (one as the logo and one hero) images will work.
:param image: :class:`ToastDisplayImage` to display in the toast
"""
self.images.append(image)
@property
def launch_action(self) -> Optional[str]:
"""Protocol to launch when the toast is clicked"""
return self._launch_action
@launch_action.setter
def launch_action(self, value: Optional[str]):
if value is None:
self._launch_action = None
else:
if not urllib.parse.urlparse(value).scheme:
warnings.warn(f"Ensure your launch action of {value} is of a proper protocol")
self._launch_action = value
[docs] def clone(self) -> Toast:
"""
Clone the current toast and return the new one
:return: A deep copy of the toast
:rtype: Toast
"""
newToast = copy.deepcopy(self)
newToast.tag = str(uuid.uuid4())
return newToast