License WIP

This commit is contained in:
wheaney 2024-05-21 17:46:09 -07:00
parent 484b420e40
commit 2d867a83d0
14 changed files with 280 additions and 11 deletions

@ -1 +1 @@
Subproject commit b2cdc8cc5fc593ab04015cec08a8e97b103ffa63
Subproject commit 0f438e4d2d489745059d260fe55a553a038e6e0f

View File

@ -2,6 +2,9 @@
<gresources>
<gresource prefix="/com/xronlinux/BreezyDesktop">
<file preprocess="xml-stripblanks">gtk/connected-device.ui</file>
<file preprocess="xml-stripblanks">gtk/license-dialog.ui</file>
<file preprocess="xml-stripblanks">gtk/license-feature-row.ui</file>
<file preprocess="xml-stripblanks">gtk/license-tier-row.ui</file>
<file preprocess="xml-stripblanks">gtk/no-device.ui</file>
<file preprocess="xml-stripblanks">gtk/no-extension.ui</file>
<file preprocess="xml-stripblanks">gtk/shortcut-dialog.ui</file>

View File

@ -13,7 +13,7 @@
<property name="column-spacing">4</property>
<child>
<object class="GtkLabel" id="device_label">
<property name="label">VITURE One</property>
<property name="label"></property>
</object>
</child>
<child>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="LicenseDialog" parent="GtkDialog">
<property name="modal">1</property>
<property name="default_width">440</property>
<property name="default_height">200</property>
<child internal-child="content_area">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="AdwBanner" id="license_action_needed_banner">
<property name="revealed">0</property>
<property name="title" translatable="yes">Some features expire soon</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="vexpand">1</property>
<property name="label" translatable="yes">Tier Status</property>
</object>
</child>
<child>
<object class="GtkBox" id="tiers">
<property name="orientation">vertical</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="vexpand">1</property>
<property name="label" translatable="yes">Feature Availability</property>
</object>
</child>
<child>
<object class="GtkBox" id="features">
<property name="orientation">vertical</property>
</object>
</child>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar">
<property name="title-widget">
<object class="GtkLabel">
<property name="label" translatable="yes">License Details</property>
<property name="single-line-mode">1</property>
<property name="ellipsize">end</property>
<property name="width-chars">5</property>
<style>
<class name="title"/>
</style>
</object>
</property>
</object>
</child>
<child>
<object class="GtkEventControllerKey" id="event_controller" />
</child>
</template>
</interface>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="LicenseFeatureRow" parent="GtkGrid">
<property name="margin-start">16</property>
<property name="margin-end">16</property>
<property name="margin-top">16</property>
<property name="margin-bottom">16</property>
<property name="row-spacing">2</property>
<property name="column-spacing">2</property>
<child>
<object class="GtkLabel" id="feature_name">
<property name="vexpand">1</property>
</object>
</child>
<child>
<object class="GtkLabel" id="feature_status">
<property name="vexpand">1</property>
</object>
</child>
<child>
<object class="GtkLabel" id="feature_funds_needed_usd">
<property name="vexpand">1</property>
</object>
</child>
</template>
</interface>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="LicenseTierRow" parent="GtkGrid">
<property name="margin-start">16</property>
<property name="margin-end">16</property>
<property name="margin-top">16</property>
<property name="margin-bottom">16</property>
<property name="row-spacing">2</property>
<property name="column-spacing">2</property>
<child>
<object class="GtkLabel" id="tier_name">
<property name="vexpand">1</property>
</object>
</child>
<child>
<object class="GtkLabel" id="tier_status">
<property name="vexpand">1</property>
</object>
</child>
<child>
<object class="GtkLabel" id="tier_funds_needed_usd">
<property name="vexpand">1</property>
</object>
</child>
</template>
</interface>

View File

@ -26,9 +26,24 @@
</child>
</object>
</property>
<property name="child">
<object class="GtkBox" id="main_content">
<child>
<object class="AdwBanner" id="license_action_needed_banner">
<property name="revealed">0</property>
<property name="title" translatable="yes">Some features expire soon</property>
<property name="button-label" translatable="yes">Update your license</property>
</object>
</child>
</object>
</property>
</template>
<menu id="primary_menu">
<section>
<item>
<attribute name="label" translatable="yes">License Details</attribute>
<attribute name="action">app.license</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_About BreezyDesktop</attribute>
<attribute name="action">app.about</attribute>

26
ui/src/licensedialog.py Normal file
View File

@ -0,0 +1,26 @@
from gi.repository import Gtk
from .statemanager import StateManager
from .licensetierrow import LicenseTierRow
from .licensefeaturerow import LicenseFeatureRow
@Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/license-dialog.ui')
class LicenseDialog(Gtk.Dialog):
__gtype_name__ = 'LicenseDialog'
tiers = Gtk.Template.Child()
features = Gtk.Template.Child()
def __init__(self):
super(Gtk.Dialog, self).__init__()
self.init_template()
self.state_manager = StateManager.get_instance()
self._handle_license();
def _handle_license(self):
license_view = self.state_manager.state['ui_view']['license']
for tier_name, tier_details in license_view['tiers'].items():
self.tiers.append(LicenseTierRow(tier_name, tier_details))
for feature_name, feature_details in license_view['features'].items():
self.features.append(LicenseFeatureRow(feature_name, feature_details))

View File

@ -0,0 +1,9 @@
from gi.repository import Gtk
@Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/license-feature-row.ui')
class LicenseFeatureRow(Gtk.Grid):
__gtype_name__ = 'LicenseFeatureRow'
def __init__(self, feature_name, feature_details):
super(Gtk.Grid, self).__init__()
self.init_template()

64
ui/src/licensetierrow.py Normal file
View File

@ -0,0 +1,64 @@
from gi.repository import Gtk
tier_names = {
'supporter': 'Gaming Supporter Tier',
'subscriber': 'Productivity Tier',
'subscriber_pro': 'Productivity Pro Tier',
}
period_names = {
'monthly': 'Monthly renewal',
'yearly': 'Yearly renewal',
'lifetime': 'Lifetime access',
}
@Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/license-tier-row.ui')
class LicenseTierRow(Gtk.Grid):
__gtype_name__ = 'LicenseTierRow'
tier_name = Gtk.Template.Child()
tier_status = Gtk.Template.Child()
tier_funds_needed_usd = Gtk.Template.Child()
def __init__(self, tier, tier_details):
super(Gtk.Grid, self).__init__()
self.init_template()
self.tier_name.set_markup(f"<b>{tier_names[tier]}</b>")
active_period = tier_details.get('active_period')
funds_needed_in_seconds = tier_details.get('funds_needed_in_seconds')
status = 'Active' if active_period else 'Inactive'
details = ''
if active_period:
details += f"({period_names[active_period]})"
if funds_needed_in_seconds is not None and funds_needed_in_seconds > 0:
time_remaining = time_remaining_text(funds_needed_in_seconds)
if time_remaining: details += f", {time_remaining} remaining"
self.tier_status.set_markup(f" - <i>{status}</i> {details}")
funds_needed_markup = ''
first_period = True
for period, amount in tier_details['funds_needed_by_period'].items():
if not first_period:
funds_needed_markup += ', '
amount_text = f"${amount}USD" if amount > 0 else 'Already funded'
funds_needed_markup += f"{period}: {amount_text}"
first_period = False
self.tier_funds_needed_usd.set_markup(funds_needed_markup)
def time_remaining_text(seconds):
if not seconds:
return
if seconds < 60 * 60:
return 'less than an hour'
elif seconds / 60 * 60 < 24:
time_remaining = seconds / 60 * 60
return '1 hour' if time_remaining == 1 else f'{time_remaining} hours'
elif seconds / 24 * 60 * 60 < 30:
time_remaining = seconds / 24 * 60 * 60
return '1 day' if time_remaining == 1 else f'{time_remaining} days'
else:
return

View File

@ -25,6 +25,7 @@ gi.require_version('Adw', '1')
gi.require_version('Gio', '2.0')
from gi.repository import Adw, Gtk, Gio
from .licensedialog import LicenseDialog
from .statemanager import StateManager
from .window import BreezydesktopWindow
@ -36,6 +37,7 @@ class BreezydesktopApplication(Adw.Application):
flags=Gio.ApplicationFlags.DEFAULT_FLAGS)
self.create_action('quit', self.on_quit_action, ['<primary>q'])
self.create_action('about', self.on_about_action)
self.create_action('license', self.on_license_action)
def do_activate(self):
"""Called when the application is activated.
@ -61,6 +63,11 @@ class BreezydesktopApplication(Adw.Application):
copyright='© 2024 Wayne Heaney')
about.present()
def on_license_action(self, widget, _):
dialog = LicenseDialog()
dialog.set_transient_for(self.props.active_window)
dialog.present()
def create_action(self, name, callback, shortcuts=None):
"""Add an application action.

View File

@ -31,6 +31,9 @@ breezydesktop_sources = [
'__init__.py',
'connecteddevice.py',
'extensionsmanager.py',
'licensedialog.py',
'licensefeaturerow.py',
'licensetierrow.py',
'main.py',
'nodevice.py',
'noextension.py',

View File

@ -11,11 +11,13 @@ class Logger:
class StateManager(GObject.GObject):
__gsignals__ = {
'device-update': (GObject.SIGNAL_RUN_FIRST, None, (str,))
'device-update': (GObject.SIGNAL_RUN_FIRST, None, (str,)),
'license-action-needed': (GObject.SIGNAL_RUN_FIRST, None, (int,)),
}
__gproperties__ = {
'follow-mode': (bool, 'Follow Mode', 'Whether the follow mode is enabled', False, GObject.ParamFlags.READWRITE)
'follow-mode': (bool, 'Follow Mode', 'Whether the follow mode is enabled', False, GObject.ParamFlags.READWRITE),
'license-action-needed-date': (int, 'License Action Needed Date', 'The date, in seconds, when the license action was needed', 0, 1024000, 0, GObject.ParamFlags.READWRITE),
}
_instance = None
@ -44,6 +46,8 @@ class StateManager(GObject.GObject):
GObject.GObject.__init__(self)
self.ipc = XRDriverIPC.get_instance()
self.connected_device_name = None
self.license_action_needed = False
self.license_action_needed_seconds = 0
self.start()
@ -61,7 +65,16 @@ class StateManager(GObject.GObject):
self.connected_device_name = new_device_name
self.emit('device-update', self.connected_device_name)
license_view = self.state['ui_view']['license']
action_needed_seconds = license_view.get('action_needed_seconds')
action_needed = action_needed_seconds is not None
if (action_needed != self.license_action_needed):
self.license_action_needed = action_needed
self.license_action_needed_seconds = action_needed_seconds
self.emit('license-action-needed', action_needed_seconds)
self.set_property('follow-mode', self.state.get('breezy_desktop_smooth_follow_enabled'))
self.set_property('license-action-needed-date', self.license_action_needed_seconds)
if self.running: threading.Timer(1.0, self._refresh_state).start()

View File

@ -28,28 +28,43 @@ from .noextension import NoExtension
class BreezydesktopWindow(Gtk.ApplicationWindow):
__gtype_name__ = 'BreezydesktopWindow'
main_content = Gtk.Template.Child()
license_action_needed_banner = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.state_manager = StateManager.get_instance()
self.state_manager.connect('device-update', self._handle_device_update)
self.state_manager.connect('license-action-needed', self._handle_device_update)
self.connected_device = ConnectedDevice()
self.no_device = NoDevice()
self.no_extension = NoExtension()
self._handle_device_update(self.state_manager, StateManager.device_name(self.state_manager.state))
self.license_action_needed_banner.connect('button-clicked', self._on_license_action_needed_button_clicked)
self._handle_device_update(self.state_manager, None)
self.connect("destroy", self._on_window_destroy)
def _handle_device_update(self, state_manager, connected_device_name):
def _handle_device_update(self, state_manager, val):
for child in self.main_content:
self.main_content.remove(child)
if state_manager.license_action_needed:
self.main_content.append(self.no_license)
if not ExtensionsManager.get_instance().is_installed():
self.set_child(self.no_extension)
elif connected_device_name:
self.set_child(self.connected_device)
self.connected_device.set_device_name(connected_device_name)
self.main_content.append(self.no_extension)
elif state_manager.connected_device_name:
self.main_content.append(self.connected_device)
self.connected_device.set_device_name(state_manager.connected_device_name)
else:
self.set_child(self.no_device)
self.main_content.append(self.no_device)
def _on_license_action_needed_button_clicked(self, widget):
self.state_manager.ipc.show_license()
def _on_window_destroy(self, widget):
self.state_manager.disconnect_by_func(self._handle_device_update)