Apple Like Notificiation Centre
This commit is contained in:
1 parent
91ed27a04b
commit
0f811def01
3 files changed
+639
-102
No files matched your search
@@ -67,12 +67,12 @@ class ModusControlCenter(Window):
|
||||
|
||||
self.add_keybinding("Escape", self.hide_controlcenter)
|
||||
|
||||
self.network_service.connect("wifi-device-added", self.on_network_ready)
|
||||
volume = 100
|
||||
wlan = modus_service.sc("wlan-changed", self.wlan_changed)
|
||||
bluetooth = modus_service.sc("bluetooth-changed", self.bluetooth_changed)
|
||||
music = modus_service.sc("music-changed", self.audio_changed)
|
||||
|
||||
self.network_service.connect("wifi-device-added", self.on_network_ready)
|
||||
# Store signal connections for cleanup
|
||||
self._signal_connections.extend(
|
||||
[
|
||||
|
||||
@@ -1,13 +1,282 @@
|
||||
from fabric.widgets.centerbox import CenterBox
|
||||
from loguru import logger
|
||||
from collections import defaultdict
|
||||
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from modules.notification.notification import NotificationWidget, cleanup_notification_image_cache, cleanup_all_notification_caches, cleanup_notification_specific_caches
|
||||
from modules.notification.notification import (
|
||||
NotificationWidget,
|
||||
cleanup_notification_image_cache,
|
||||
cleanup_all_notification_caches,
|
||||
cleanup_notification_specific_caches,
|
||||
)
|
||||
from services.modus import notification_service
|
||||
from widgets.wayland import WaylandWindow as Window
|
||||
from fabric.widgets.image import Image
|
||||
from fabric.widgets.eventbox import EventBox
|
||||
from fabric.widgets.revealer import Revealer
|
||||
|
||||
|
||||
class ExpandableNotificationGroup(Box):
|
||||
def __init__(self, app_name, notifications, **kwargs):
|
||||
super().__init__(
|
||||
name="notification-group", orientation="v", spacing=0, **kwargs
|
||||
)
|
||||
|
||||
self.app_name = app_name
|
||||
self.notifications = notifications
|
||||
self.is_expanded = False
|
||||
|
||||
# Create collapsed state (shows only latest notification)
|
||||
self.create_collapsed_state()
|
||||
|
||||
def create_collapsed_state(self):
|
||||
from widgets.custom_image import CustomImage
|
||||
|
||||
latest_notification = self.notifications[0] # Most recent notification
|
||||
|
||||
# Create clickable event box
|
||||
self.collapsed_eventbox = EventBox(
|
||||
events=["button-press-event"],
|
||||
)
|
||||
self.collapsed_eventbox.connect("button-press-event", self.on_clicked)
|
||||
|
||||
# Only create stacked effect if we have multiple notifications
|
||||
num_notifications = len(self.notifications)
|
||||
|
||||
if num_notifications == 1:
|
||||
# Single notification - no stacking needed
|
||||
single_notification = Box(
|
||||
name="single-notification-content",
|
||||
spacing=8,
|
||||
children=[
|
||||
Box(
|
||||
name="notification-image",
|
||||
children=CustomImage(
|
||||
pixbuf=self._get_notification_pixbuf(
|
||||
latest_notification._notification
|
||||
)
|
||||
),
|
||||
),
|
||||
Box(
|
||||
name="notification-text",
|
||||
orientation="v",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
children=[
|
||||
Box(
|
||||
name="notification-summary-box",
|
||||
orientation="h",
|
||||
children=[
|
||||
Label(
|
||||
name="notification-summary",
|
||||
markup=f"<b>{self.app_name}</b>",
|
||||
h_align="start",
|
||||
ellipsization="end",
|
||||
),
|
||||
],
|
||||
),
|
||||
Label(
|
||||
name="notification-body",
|
||||
markup=latest_notification._notification.summary.replace(
|
||||
"\n", " "
|
||||
),
|
||||
h_align="start",
|
||||
ellipsization="end",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
self.collapsed_eventbox.add(single_notification)
|
||||
else:
|
||||
# Multiple notifications - create stacked effect
|
||||
# Create container for the entire stack
|
||||
stack_container = Box(
|
||||
name="notification-stack-container",
|
||||
orientation="v",
|
||||
spacing=0,
|
||||
)
|
||||
|
||||
# Add bottom shadow layer first (deepest)
|
||||
if num_notifications >= 3:
|
||||
bottom_shadow = Box(
|
||||
name="stack-shadow-bottom",
|
||||
# style="min-height: 16px; margin-left: 20px; margin-right: -4px; margin-bottom: -8px;",
|
||||
)
|
||||
stack_container.add(bottom_shadow)
|
||||
|
||||
# Add middle shadow layer
|
||||
if num_notifications >= 2:
|
||||
middle_shadow = Box(
|
||||
name="stack-shadow-middle",
|
||||
# style="min-height: 20px; margin-left: 10px; margin-right: -2px; margin-bottom: -12px;",
|
||||
)
|
||||
stack_container.add(middle_shadow)
|
||||
|
||||
# Add the main notification content on top
|
||||
main_notification = Box(
|
||||
name="stack-main-notification",
|
||||
spacing=8,
|
||||
children=[
|
||||
Box(
|
||||
name="notification-image",
|
||||
children=CustomImage(
|
||||
pixbuf=self._get_notification_pixbuf(
|
||||
latest_notification._notification
|
||||
)
|
||||
),
|
||||
),
|
||||
Box(
|
||||
name="notification-text",
|
||||
orientation="v",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
children=[
|
||||
Box(
|
||||
name="notification-summary-box",
|
||||
orientation="h",
|
||||
children=[
|
||||
Label(
|
||||
name="notification-summary",
|
||||
markup=f"<b>{self.app_name}</b>",
|
||||
h_align="start",
|
||||
ellipsization="end",
|
||||
),
|
||||
],
|
||||
),
|
||||
Label(
|
||||
name="notification-body",
|
||||
markup=latest_notification._notification.summary.replace(
|
||||
"\n", " "
|
||||
),
|
||||
h_align="start",
|
||||
ellipsization="end",
|
||||
),
|
||||
],
|
||||
),
|
||||
Box(
|
||||
name="notification-count",
|
||||
children=[
|
||||
Label(
|
||||
name="notification-count-label",
|
||||
label=f"{len(self.notifications)}",
|
||||
h_align="end",
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
stack_container.add(main_notification)
|
||||
self.collapsed_eventbox.add(stack_container)
|
||||
|
||||
self.add(self.collapsed_eventbox)
|
||||
|
||||
# Create expanded state (hidden initially)
|
||||
self.create_expanded_state()
|
||||
|
||||
def create_expanded_state(self):
|
||||
self.expanded_box = Box(
|
||||
name="notification-group-expanded",
|
||||
orientation="v",
|
||||
spacing=5,
|
||||
visible=False,
|
||||
)
|
||||
|
||||
# Header with app name and controls
|
||||
|
||||
# App name and show less button
|
||||
header_content = Box(
|
||||
orientation="h",
|
||||
h_expand=True,
|
||||
children=[
|
||||
Label(
|
||||
name="notification-group-title",
|
||||
markup=f"<b>{self.app_name}</b>",
|
||||
h_align="start",
|
||||
h_expand=True,
|
||||
),
|
||||
Button(
|
||||
name="notification-show-less",
|
||||
label="Show less",
|
||||
on_clicked=self.collapse,
|
||||
h_align="end",
|
||||
),
|
||||
Button(
|
||||
name="notification-close-all",
|
||||
label="×",
|
||||
on_clicked=self.close_all,
|
||||
h_align="end",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
self.expanded_box.add(header_content)
|
||||
|
||||
# Individual notifications
|
||||
for notification in self.notifications:
|
||||
notification_widget = NotificationCenterWidget(notification=notification)
|
||||
self.expanded_box.add(notification_widget)
|
||||
|
||||
self.add(self.expanded_box)
|
||||
|
||||
def _get_notification_pixbuf(self, notification):
|
||||
# Use the same logic as NotificationWidget
|
||||
try:
|
||||
if hasattr(notification, "image_pixbuf") and notification.image_pixbuf:
|
||||
return notification.image_pixbuf.scale_simple(
|
||||
35, 35, 2
|
||||
) # GdkPixbuf.InterpType.BILINEAR = 2
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fallback to app icon
|
||||
try:
|
||||
from modules.notification.notification import cache_notification_icon
|
||||
|
||||
cached_app_icon = cache_notification_icon(notification.app_icon, (35, 35))
|
||||
if cached_app_icon:
|
||||
return cached_app_icon
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Ultimate fallback
|
||||
from modules.notification.notification import get_fallback_notification_icon
|
||||
|
||||
return get_fallback_notification_icon((35, 35))
|
||||
|
||||
def on_clicked(self, widget, event):
|
||||
if event.button == 1: # Left click
|
||||
self.expand()
|
||||
return True
|
||||
|
||||
def expand(self, *args):
|
||||
self.is_expanded = True
|
||||
self.collapsed_eventbox.set_visible(False)
|
||||
self.expanded_box.set_visible(True)
|
||||
|
||||
def collapse(self, *args):
|
||||
self.is_expanded = False
|
||||
self.collapsed_eventbox.set_visible(True)
|
||||
self.expanded_box.set_visible(False)
|
||||
|
||||
def close_all(self, *args):
|
||||
# Close all notifications in this group
|
||||
for notification in self.notifications:
|
||||
try:
|
||||
cleanup_notification_specific_caches(
|
||||
app_icon_source=getattr(notification, "app_icon_source", None),
|
||||
notification_image_cache_key=getattr(
|
||||
notification, "notification_image_cache_key", None
|
||||
),
|
||||
)
|
||||
notification_service.remove_cached_notification(notification.cache_id)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Error removing notification {notification.cache_id}: {e}"
|
||||
)
|
||||
|
||||
|
||||
class NotificationCenterWidget(NotificationWidget):
|
||||
@@ -15,13 +284,17 @@ class NotificationCenterWidget(NotificationWidget):
|
||||
self.notification_id = notification.cache_id
|
||||
|
||||
super().__init__(
|
||||
notification._notification, timeout_ms=0, show_close_button=True, **kwargs
|
||||
notification._notification,
|
||||
timeout_ms=0,
|
||||
show_close_button=True,
|
||||
name="notification-centre-notifs",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def create_content(self, notification):
|
||||
# Create our custom close button for notification center
|
||||
from widgets.custom_image import CustomImage
|
||||
|
||||
|
||||
self.close_button = Button(
|
||||
name="notif-close-button",
|
||||
image=CustomImage(icon_name="close-symbolic", icon_size=18),
|
||||
@@ -34,11 +307,11 @@ class NotificationCenterWidget(NotificationWidget):
|
||||
self.close_button.connect(
|
||||
"leave-notify-event", lambda *_: self.unhover_button(self.close_button)
|
||||
)
|
||||
|
||||
|
||||
# Create the content box manually with our custom close button
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.label import Label
|
||||
|
||||
|
||||
return Box(
|
||||
name="notification-content",
|
||||
spacing=8,
|
||||
@@ -100,11 +373,15 @@ class NotificationCenterWidget(NotificationWidget):
|
||||
try:
|
||||
# Clean up this notification's cached icons AND images
|
||||
cleanup_notification_specific_caches(
|
||||
app_icon_source=getattr(self, 'app_icon_source', None),
|
||||
notification_image_cache_key=getattr(self, 'notification_image_cache_key', None)
|
||||
app_icon_source=getattr(self, "app_icon_source", None),
|
||||
notification_image_cache_key=getattr(
|
||||
self, "notification_image_cache_key", None
|
||||
),
|
||||
)
|
||||
logger.debug(f"Cleaned up all caches for notification center: {self.notification_id}")
|
||||
|
||||
logger.debug(
|
||||
f"Cleaned up all caches for notification center: {self.notification_id}"
|
||||
)
|
||||
|
||||
notification_service.remove_cached_notification(self.notification_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Error removing notification {self.notification_id}: {e}")
|
||||
@@ -135,6 +412,10 @@ class NotificationCenter(Window):
|
||||
NOTIFICATION_CENTER_WIDTH = 410
|
||||
self.set_size_request(NOTIFICATION_CENTER_WIDTH, 600)
|
||||
|
||||
# Group notifications by app name
|
||||
self.notification_groups = defaultdict(list)
|
||||
self.group_widgets = {}
|
||||
|
||||
notification_service.connect(
|
||||
"cached-notification-added", self.on_notification_added
|
||||
)
|
||||
@@ -150,27 +431,10 @@ class NotificationCenter(Window):
|
||||
name="noti-center-box",
|
||||
)
|
||||
|
||||
header = Box(
|
||||
orientation="h",
|
||||
spacing=10,
|
||||
style="margin-bottom: 10px;",
|
||||
)
|
||||
|
||||
header.add(
|
||||
Label(
|
||||
label="Notification Center",
|
||||
h_align="start",
|
||||
h_expand=True,
|
||||
style="font-size: 16px; font-weight: bold; color: #ffffff;",
|
||||
)
|
||||
)
|
||||
|
||||
main_box.add(header)
|
||||
|
||||
self.scrolled = ScrolledWindow(h_expand=True, v_expand=True)
|
||||
self.scrolled = ScrolledWindow(h_expand=False, v_expand=False)
|
||||
self.notifications_box = Box(
|
||||
v_expand=True,
|
||||
h_expand=True,
|
||||
v_expand=False,
|
||||
h_expand=False,
|
||||
style="margin: 1px 0px 1px 1px;",
|
||||
orientation="v",
|
||||
spacing=5,
|
||||
@@ -178,84 +442,154 @@ class NotificationCenter(Window):
|
||||
self.scrolled.add(self.notifications_box)
|
||||
main_box.add(self.scrolled)
|
||||
|
||||
# No notifications label
|
||||
self.not_found_label = Label(
|
||||
label="No notifications",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
style="color: #888888; font-style: italic; margin: 20px;",
|
||||
visible=(notification_service.count == 0),
|
||||
)
|
||||
main_box.add(self.not_found_label)
|
||||
# # No notifications label
|
||||
# self.not_found_label = Label(
|
||||
# label="No notifications",
|
||||
# h_align="center",
|
||||
# v_align="center",
|
||||
# h_expand=True,
|
||||
# v_expand=True,
|
||||
# style="color: #888888; font-style: italic; margin: 20px;",
|
||||
# visible=(notification_service.count == 0),
|
||||
# )
|
||||
# main_box.add(self.not_found_label)
|
||||
|
||||
self.clear_all_button = Button(
|
||||
name="noti-clear-button",
|
||||
label="Clear All",
|
||||
label="Clear",
|
||||
on_clicked=self.clear_all_notifications,
|
||||
visible=(notification_service.count > 0),
|
||||
)
|
||||
main_box.add(self.clear_all_button)
|
||||
self.button_centre_box = CenterBox(
|
||||
center_children=[self.clear_all_button],
|
||||
)
|
||||
main_box.add(self.button_centre_box)
|
||||
|
||||
self.children = main_box
|
||||
|
||||
# Load existing notifications
|
||||
for cached_notification in notification_service.cached_notifications:
|
||||
self.notifications_box.add(
|
||||
NotificationCenterWidget(notification=cached_notification)
|
||||
)
|
||||
# Load existing notifications and group them
|
||||
self._rebuild_notification_groups()
|
||||
|
||||
self.add_keybinding("Escape", self._on_escape_pressed)
|
||||
self.connect("destroy", self._on_destroy)
|
||||
|
||||
def _rebuild_notification_groups(self):
|
||||
"""Rebuild notification groups from scratch"""
|
||||
# Clear existing groups
|
||||
self.notification_groups.clear()
|
||||
self.group_widgets.clear()
|
||||
|
||||
# Clear notifications box
|
||||
for child in self.notifications_box.get_children():
|
||||
child.destroy()
|
||||
|
||||
# Group notifications by app name
|
||||
for cached_notification in notification_service.cached_notifications:
|
||||
app_name = cached_notification._notification.app_name
|
||||
self.notification_groups[app_name].append(cached_notification)
|
||||
|
||||
# Create group widgets
|
||||
for app_name, notifications in self.notification_groups.items():
|
||||
# Sort notifications by timestamp (newest first)
|
||||
notifications.sort(key=lambda n: getattr(n, "timestamp", 0), reverse=True)
|
||||
|
||||
group_widget = ExpandableNotificationGroup(app_name, notifications)
|
||||
self.group_widgets[app_name] = group_widget
|
||||
self.notifications_box.add(group_widget)
|
||||
|
||||
def on_notification_added(self, service, cached_notification):
|
||||
try:
|
||||
notification_widget = NotificationCenterWidget(
|
||||
notification=cached_notification
|
||||
)
|
||||
# Insert at the beginning to show newest first
|
||||
self.notifications_box.pack_start(notification_widget, False, False, 0)
|
||||
notification_widget.show_all()
|
||||
logger.debug(
|
||||
f"Added notification widget for {cached_notification.app_name}"
|
||||
)
|
||||
app_name = cached_notification._notification.app_name
|
||||
|
||||
# Add to groups
|
||||
self.notification_groups[app_name].insert(
|
||||
0, cached_notification
|
||||
) # Insert at beginning (newest first)
|
||||
|
||||
# Update or create group widget
|
||||
if app_name in self.group_widgets:
|
||||
# Update existing group
|
||||
group_widget = self.group_widgets[app_name]
|
||||
group_widget.notifications = self.notification_groups[app_name]
|
||||
# Refresh the group widget
|
||||
self._refresh_group_widget(group_widget)
|
||||
else:
|
||||
# Create new group widget
|
||||
group_widget = ExpandableNotificationGroup(
|
||||
app_name, self.notification_groups[app_name]
|
||||
)
|
||||
self.group_widgets[app_name] = group_widget
|
||||
self.notifications_box.pack_start(group_widget, False, False, 0)
|
||||
group_widget.show_all()
|
||||
|
||||
logger.debug(f"Added notification to group {app_name}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding notification widget: {e}")
|
||||
logger.error(f"Error adding notification to group: {e}")
|
||||
|
||||
def _refresh_group_widget(self, group_widget):
|
||||
"""Refresh a group widget's content"""
|
||||
try:
|
||||
# Remove existing children
|
||||
for child in group_widget.get_children():
|
||||
group_widget.remove(child)
|
||||
|
||||
# Recreate content
|
||||
group_widget.create_collapsed_state()
|
||||
group_widget.show_all()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error refreshing group widget: {e}")
|
||||
|
||||
def on_notification_removed(self, service, cached_notification):
|
||||
try:
|
||||
# Find and remove the corresponding widget with cache cleanup
|
||||
for child in self.notifications_box.get_children():
|
||||
if (
|
||||
hasattr(child, "notification_id")
|
||||
and child.notification_id == cached_notification.cache_id
|
||||
):
|
||||
# Clean up the widget's cached icons AND images before destroying
|
||||
cleanup_notification_specific_caches(
|
||||
app_icon_source=getattr(child, 'app_icon_source', None),
|
||||
notification_image_cache_key=getattr(child, 'notification_image_cache_key', None)
|
||||
)
|
||||
logger.debug(f"Cleaned up all caches for removed notification: {child.notification_id}")
|
||||
|
||||
child.destroy()
|
||||
break
|
||||
logger.debug(
|
||||
f"Removed notification widget and all caches for {cached_notification.app_name}"
|
||||
app_name = cached_notification._notification.app_name
|
||||
|
||||
# Remove from groups
|
||||
if app_name in self.notification_groups:
|
||||
self.notification_groups[app_name] = [
|
||||
n
|
||||
for n in self.notification_groups[app_name]
|
||||
if n.cache_id != cached_notification.cache_id
|
||||
]
|
||||
|
||||
# If no more notifications for this app, remove group widget
|
||||
if not self.notification_groups[app_name]:
|
||||
if app_name in self.group_widgets:
|
||||
group_widget = self.group_widgets[app_name]
|
||||
group_widget.destroy()
|
||||
del self.group_widgets[app_name]
|
||||
del self.notification_groups[app_name]
|
||||
else:
|
||||
# Update existing group widget
|
||||
group_widget = self.group_widgets[app_name]
|
||||
group_widget.notifications = self.notification_groups[app_name]
|
||||
self._refresh_group_widget(group_widget)
|
||||
|
||||
# Clean up caches
|
||||
cleanup_notification_specific_caches(
|
||||
app_icon_source=getattr(cached_notification, "app_icon_source", None),
|
||||
notification_image_cache_key=getattr(
|
||||
cached_notification, "notification_image_cache_key", None
|
||||
),
|
||||
)
|
||||
|
||||
logger.debug(f"Removed notification from group {app_name}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error removing notification widget: {e}")
|
||||
logger.error(f"Error removing notification from group: {e}")
|
||||
|
||||
def on_clear_all(self, service):
|
||||
try:
|
||||
# Clear all remaining cached notification images AND icons
|
||||
# Individual notifications clean up their own images, this handles any remaining
|
||||
# Clear all groups
|
||||
self.notification_groups.clear()
|
||||
self.group_widgets.clear()
|
||||
|
||||
# Clear all remaining cached notification images AND icons
|
||||
cleanup_all_notification_caches()
|
||||
for child in self.notifications_box.get_children():
|
||||
child.destroy()
|
||||
logger.debug("Cleared all notification widgets and remaining cached images")
|
||||
logger.debug("Cleared all notification groups and remaining cached images")
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing notification widgets: {e}")
|
||||
logger.error(f"Error clearing notification groups: {e}")
|
||||
|
||||
def on_count_changed(self, service, count=None):
|
||||
current_count = notification_service.count
|
||||
@@ -264,8 +598,11 @@ class NotificationCenter(Window):
|
||||
self.scrolled.set_visible(current_count > 0)
|
||||
|
||||
def clear_all_notifications(self, *_):
|
||||
# Clear all groups
|
||||
self.notification_groups.clear()
|
||||
self.group_widgets.clear()
|
||||
|
||||
# Clear all remaining cached notification images AND icons when clear all is clicked
|
||||
# Individual notifications clean up their own images, this handles any remaining
|
||||
cleanup_all_notification_caches() # Clear ALL caches (icons + images)
|
||||
notification_service.clear_all_cached_notifications()
|
||||
if hasattr(self, "mousecapture"):
|
||||
|
||||
+221
-21
@@ -1,26 +1,182 @@
|
||||
#noti-center-box {
|
||||
background-color: alpha(#fff, 0.09);
|
||||
background-color: transparent;
|
||||
|
||||
border: 1px solid alpha(#111, 0.3);
|
||||
/* border: 1px solid alpha(#111, 0.3); */
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-right: 10px;
|
||||
/* padding: 20px; */
|
||||
/* margin: 6px; */
|
||||
}
|
||||
|
||||
#noti-clear-button {
|
||||
min-width: 10px;
|
||||
min-height: 40px;
|
||||
border-radius: 10px;
|
||||
#noti-clear-button label {
|
||||
/* min-width: 10px; */
|
||||
background-color: alpha(#202828, 0.8);
|
||||
min-height: 25px;
|
||||
min-width: 70px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
font-family: "SF Pro Rounded";
|
||||
border: 1.5px solid #525155;
|
||||
border-radius: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#noti-clear-button {
|
||||
background-color: alpha(#888, 0.09);
|
||||
}
|
||||
#noti-clear-button:hover {
|
||||
background-color: alpha(#fff, 0.09);
|
||||
}
|
||||
|
||||
/* Expandable notification group styles */
|
||||
#notification-group {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* Single notification (no stacking) */
|
||||
#single-notification-content {
|
||||
background-color: alpha(#202828, 0.8);
|
||||
border: 1.5px solid #525155;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
#single-notification-content:hover {
|
||||
background-color: alpha(#999, 0.9);
|
||||
border: 1.5px solid #525155;
|
||||
}
|
||||
|
||||
/* Stacked notification container */
|
||||
#notification-stack-container {
|
||||
background-color: transparent;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
/* Bottom shadow layer (deepest) - offset to show stacking from below */
|
||||
#stack-shadow-bottom {
|
||||
background-color: alpha(#202828, 0.9);
|
||||
border: 1.5px solid #525155;
|
||||
border-radius: 12px;
|
||||
opacity: 1;
|
||||
min-height: 16px;
|
||||
margin-left: 20px;
|
||||
margin-right: -4px;
|
||||
margin-bottom: -8px;
|
||||
}
|
||||
|
||||
/* Middle shadow layer */
|
||||
#stack-shadow-middle {
|
||||
background-color: alpha(#202828, 0.9);
|
||||
border: 1.5px solid #525155;
|
||||
border-radius: 12px;
|
||||
opacity: 1;
|
||||
min-height: 20px;
|
||||
margin-left: 10px;
|
||||
margin-right: -2px;
|
||||
margin-bottom: -12px;
|
||||
}
|
||||
|
||||
/* Main notification content (top layer) */
|
||||
#stack-main-notification {
|
||||
background-color: alpha(#202828, 1);
|
||||
border: 1.5px solid #525155;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* Hover effects for stacked notifications */
|
||||
#notification-stack-container:hover #stack-main-notification {
|
||||
background-color: alpha(#525155, 0.1);
|
||||
border: 1px solid #525155;
|
||||
}
|
||||
|
||||
#notification-stack-container:hover #stack-shadow-middle {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#notification-stack-container:hover #stack-shadow-bottom {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Collapsed notification state */
|
||||
#notification-content-collapsed {
|
||||
background-color: alpha(#000, 0.05);
|
||||
border: 1px solid #525155;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
margin: 2px 0;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
#notification-content-collapsed:hover {
|
||||
background-color: alpha(#525155, 0.1);
|
||||
border: 1px solid #525155;
|
||||
}
|
||||
|
||||
/* Notification count badge */
|
||||
#notification-count-label {
|
||||
background-color: alpha(#fff, 0.2);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 10px;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
/* Expanded notification group header */
|
||||
#notification-group-header {
|
||||
background-color: alpha(#000, 0.05);
|
||||
border: 1px solid #525155;
|
||||
border-radius: 12px 12px 0 0;
|
||||
padding: 8px 12px;
|
||||
margin: 2px 0 0 0;
|
||||
}
|
||||
|
||||
#notification-group-title {
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#notification-show-less {
|
||||
background-color: transparent;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid alpha(#fff, 0.2);
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
font-size: 11px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
#notification-show-less:hover {
|
||||
background-color: alpha(#fff, 0.1);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
#notification-close-all {
|
||||
background-color: transparent;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid alpha(#fff, 0.2);
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
#notification-close-all:hover {
|
||||
background-color: alpha(#ff5555, 0.2);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
border-color: alpha(#ff5555, 0.4);
|
||||
}
|
||||
|
||||
/* Expanded notification list */
|
||||
#notification-group-expanded {
|
||||
border: 1px solid #525155;
|
||||
border-top: none;
|
||||
border-radius: 0 0 12px 12px;
|
||||
/* background-color: alpha(#000, 0.02); */
|
||||
}
|
||||
|
||||
/* Stacked notification group styles */
|
||||
.stacked-notification-group {
|
||||
margin: 4px 0;
|
||||
@@ -30,11 +186,8 @@
|
||||
}
|
||||
|
||||
.stacked-notification-item {
|
||||
background-color: alpha(#000, 0.4);
|
||||
border: 0.5px solid alpha(#111, 0.4);
|
||||
box-shadow:
|
||||
inset 0 0 200px 0 alpha(#111, 0.5),
|
||||
inset 0 0 0 0.5px alpha(#aaa, 0.4);
|
||||
background-color: alpha(#000, 0.05);
|
||||
border: 1px solid #525155;
|
||||
border-radius: 12px;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
@@ -50,11 +203,8 @@
|
||||
}
|
||||
|
||||
#stacked-notification-item:hover {
|
||||
background-color: alpha(#111, 0.5);
|
||||
box-shadow:
|
||||
inset 0 0 200px 0 alpha(#111, 0.5),
|
||||
inset 0 0 0 0.5px alpha(#aaa, 0.4),
|
||||
0 4px 12px alpha(#000, 0.3);
|
||||
background-color: alpha(#525155, 0.1);
|
||||
border: 1px solid #525155;
|
||||
}
|
||||
|
||||
/* Stack count indicator */
|
||||
@@ -72,7 +222,57 @@
|
||||
|
||||
/* Make expanded notifications clickable with subtle hover effect */
|
||||
#expanded-notification-list:hover {
|
||||
background-color: alpha(#111, 0.1);
|
||||
background-color: alpha(#111, 0.9);
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
#notification-group-title {
|
||||
font-family: "SF Pro Rounded";
|
||||
font-size: 19px;
|
||||
margin: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
#notification-show-less {
|
||||
font-family: "SF Pro Rounded";
|
||||
background-color: alpha(#111, 0.9);
|
||||
font-size: 12px;
|
||||
margin: 10px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
#notification-close-all {
|
||||
margin: 10px 10px 10px 0;
|
||||
background-color: alpha(#111, 0.9);
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
#notif-close-button {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#notification-group-expanded {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#notification-content {
|
||||
background-color: transparent;
|
||||
}
|
||||
#notification-count-label {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
font-family: "SF Pro Rounded";
|
||||
font-size: 13px;
|
||||
}
|
||||
#notification-centre-notifs {
|
||||
background-color: alpha(#1c2328, 0.8);
|
||||
min-height: 40px;
|
||||
padding: 10px;
|
||||
border: 1.5px solid #525155;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
#notification-centre-notifs:last-child {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
Reference in new issue
Block a user