bauiv1lib.sendinfo
UI functionality for entering promo codes.
1# Released under the MIT License. See LICENSE for details. 2# 3"""UI functionality for entering promo codes.""" 4 5from __future__ import annotations 6 7import time 8import logging 9from typing import TYPE_CHECKING 10 11import bauiv1 as bui 12 13if TYPE_CHECKING: 14 from typing import Any 15 16 17class SendInfoWindow(bui.MainWindow): 18 """Window for sending info to the developer.""" 19 20 def __init__( 21 self, 22 modal: bool = False, 23 legacy_code_mode: bool = False, 24 transition: str | None = 'in_scale', 25 origin_widget: bui.Widget | None = None, 26 ): 27 self._legacy_code_mode = legacy_code_mode 28 29 # Need to wrangle our own transition-out in modal mode. 30 if origin_widget is not None: 31 self._transition_out = 'out_scale' 32 else: 33 self._transition_out = 'out_right' 34 35 width = 450 if legacy_code_mode else 600 36 height = 200 if legacy_code_mode else 300 37 38 self._modal = modal 39 self._r = 'promoCodeWindow' 40 41 assert bui.app.classic is not None 42 uiscale = bui.app.ui_v1.uiscale 43 super().__init__( 44 root_widget=bui.containerwidget( 45 size=(width, height), 46 toolbar_visibility=( 47 'menu_minimal_no_back' 48 if uiscale is bui.UIScale.SMALL or modal 49 else 'menu_full' 50 ), 51 scale=( 52 2.0 53 if uiscale is bui.UIScale.SMALL 54 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 55 ), 56 ), 57 transition=transition, 58 origin_widget=origin_widget, 59 ) 60 61 btn = bui.buttonwidget( 62 parent=self._root_widget, 63 scale=0.5, 64 position=(40, height - 40), 65 size=(60, 60), 66 label='', 67 on_activate_call=self._do_back, 68 autoselect=True, 69 color=(0.55, 0.5, 0.6), 70 icon=bui.gettexture('crossOut'), 71 iconscale=1.2, 72 ) 73 74 v = height - 74 75 76 if legacy_code_mode: 77 v -= 20 78 else: 79 v -= 20 80 bui.textwidget( 81 parent=self._root_widget, 82 text=bui.Lstr(resource='sendInfoDescriptionText'), 83 maxwidth=width * 0.9, 84 position=(width * 0.5, v), 85 color=(0.7, 0.7, 0.7, 1.0), 86 size=(0, 0), 87 scale=0.8, 88 h_align='center', 89 v_align='center', 90 ) 91 v -= 20 92 93 # bui.textwidget( 94 # parent=self._root_widget, 95 # text=bui.Lstr( 96 # resource='supportEmailText', 97 # subs=[('${EMAIL}', 'support@froemling.net')], 98 # ), 99 # maxwidth=width * 0.9, 100 # position=(width * 0.5, v), 101 # color=(0.7, 0.7, 0.7, 1.0), 102 # size=(0, 0), 103 # scale=0.65, 104 # h_align='center', 105 # v_align='center', 106 # ) 107 v -= 80 108 109 bui.textwidget( 110 parent=self._root_widget, 111 text=bui.Lstr( 112 resource=( 113 f'{self._r}.codeText' 114 if legacy_code_mode 115 else 'descriptionText' 116 ) 117 ), 118 position=(22, v), 119 color=(0.8, 0.8, 0.8, 1.0), 120 size=(90, 30), 121 h_align='right', 122 maxwidth=100, 123 ) 124 v -= 8 125 126 self._text_field = bui.textwidget( 127 parent=self._root_widget, 128 position=(125, v), 129 size=(280 if legacy_code_mode else 380, 46), 130 text='', 131 h_align='left', 132 v_align='center', 133 max_chars=64, 134 color=(0.9, 0.9, 0.9, 1.0), 135 description=bui.Lstr( 136 resource=( 137 f'{self._r}.codeText' 138 if legacy_code_mode 139 else 'descriptionText' 140 ) 141 ), 142 editable=True, 143 padding=4, 144 on_return_press_call=self._activate_enter_button, 145 ) 146 bui.widget(edit=btn, down_widget=self._text_field) 147 148 v -= 79 149 b_width = 200 150 self._enter_button = btn2 = bui.buttonwidget( 151 parent=self._root_widget, 152 position=(width * 0.5 - b_width * 0.5, v), 153 size=(b_width, 60), 154 scale=1.0, 155 label=bui.Lstr( 156 resource='submitText', fallback_resource=f'{self._r}.enterText' 157 ), 158 on_activate_call=self._do_enter, 159 ) 160 bui.containerwidget( 161 edit=self._root_widget, 162 cancel_button=btn, 163 start_button=btn2, 164 selected_child=self._text_field, 165 ) 166 167 def _do_back(self) -> None: 168 # pylint: disable=cyclic-import 169 170 if not self._modal: 171 self.main_window_back() 172 return 173 174 # Handle modal case: 175 176 # no-op if our underlying widget is dead or on its way out. 177 if not self._root_widget or self._root_widget.transitioning_out: 178 return 179 180 bui.containerwidget( 181 edit=self._root_widget, transition=self._transition_out 182 ) 183 184 def _activate_enter_button(self) -> None: 185 self._enter_button.activate() 186 187 def _do_enter(self) -> None: 188 # pylint: disable=cyclic-import 189 # from bauiv1lib.settings.advanced import AdvancedSettingsWindow 190 191 plus = bui.app.plus 192 assert plus is not None 193 194 description: Any = bui.textwidget(query=self._text_field) 195 assert isinstance(description, str) 196 197 if self._modal: 198 # no-op if our underlying widget is dead or on its way out. 199 if not self._root_widget or self._root_widget.transitioning_out: 200 return 201 bui.containerwidget( 202 edit=self._root_widget, transition=self._transition_out 203 ) 204 else: 205 # no-op if we're not in control. 206 if not self.main_window_has_control(): 207 return 208 self.main_window_back() 209 210 # Used for things like unlocking shared playlists or linking 211 # accounts: talk directly to V1 server via transactions. 212 if self._legacy_code_mode: 213 if plus.get_v1_account_state() != 'signed_in': 214 bui.screenmessage( 215 bui.Lstr(resource='notSignedInErrorText'), color=(1, 0, 0) 216 ) 217 bui.getsound('error').play() 218 else: 219 plus.add_v1_account_transaction( 220 { 221 'type': 'PROMO_CODE', 222 'expire_time': time.time() + 5, 223 'code': description, 224 } 225 ) 226 plus.run_v1_account_transactions() 227 else: 228 bui.app.create_async_task(_send_info(description)) 229 230 231async def _send_info(description: str) -> None: 232 from bacommon.cloud import SendInfoMessage 233 234 plus = bui.app.plus 235 assert plus is not None 236 237 try: 238 # Don't allow *anything* if our V2 transport connection isn't up. 239 if not plus.cloud.connected: 240 bui.screenmessage( 241 bui.Lstr(resource='internal.unavailableNoConnectionText'), 242 color=(1, 0, 0), 243 ) 244 bui.getsound('error').play() 245 return 246 247 # Ship to V2 server, with or without account info. 248 if plus.accounts.primary is not None: 249 with plus.accounts.primary: 250 response = await plus.cloud.send_message_async( 251 SendInfoMessage(description) 252 ) 253 else: 254 response = await plus.cloud.send_message_async( 255 SendInfoMessage(description) 256 ) 257 258 # Support simple message printing from v2 server. 259 if response.message is not None: 260 bui.screenmessage(response.message, color=(0, 1, 0)) 261 262 # If V2 handled it, we're done. 263 if response.handled: 264 return 265 266 # Ok; V2 didn't handle it. Try V1 if we're signed in there. 267 if plus.get_v1_account_state() != 'signed_in': 268 bui.screenmessage( 269 bui.Lstr(resource='notSignedInErrorText'), color=(1, 0, 0) 270 ) 271 bui.getsound('error').play() 272 return 273 274 # Push it along to v1 as an old style code. Allow v2 response to 275 # sub in its own code. 276 plus.add_v1_account_transaction( 277 { 278 'type': 'PROMO_CODE', 279 'expire_time': time.time() + 5, 280 'code': ( 281 description 282 if response.legacy_code is None 283 else response.legacy_code 284 ), 285 } 286 ) 287 plus.run_v1_account_transactions() 288 except Exception: 289 logging.exception('Error sending promo code.') 290 bui.screenmessage('Error sending code (see log).', color=(1, 0, 0)) 291 bui.getsound('error').play()
class
SendInfoWindow(bauiv1._uitypes.MainWindow):
18class SendInfoWindow(bui.MainWindow): 19 """Window for sending info to the developer.""" 20 21 def __init__( 22 self, 23 modal: bool = False, 24 legacy_code_mode: bool = False, 25 transition: str | None = 'in_scale', 26 origin_widget: bui.Widget | None = None, 27 ): 28 self._legacy_code_mode = legacy_code_mode 29 30 # Need to wrangle our own transition-out in modal mode. 31 if origin_widget is not None: 32 self._transition_out = 'out_scale' 33 else: 34 self._transition_out = 'out_right' 35 36 width = 450 if legacy_code_mode else 600 37 height = 200 if legacy_code_mode else 300 38 39 self._modal = modal 40 self._r = 'promoCodeWindow' 41 42 assert bui.app.classic is not None 43 uiscale = bui.app.ui_v1.uiscale 44 super().__init__( 45 root_widget=bui.containerwidget( 46 size=(width, height), 47 toolbar_visibility=( 48 'menu_minimal_no_back' 49 if uiscale is bui.UIScale.SMALL or modal 50 else 'menu_full' 51 ), 52 scale=( 53 2.0 54 if uiscale is bui.UIScale.SMALL 55 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 56 ), 57 ), 58 transition=transition, 59 origin_widget=origin_widget, 60 ) 61 62 btn = bui.buttonwidget( 63 parent=self._root_widget, 64 scale=0.5, 65 position=(40, height - 40), 66 size=(60, 60), 67 label='', 68 on_activate_call=self._do_back, 69 autoselect=True, 70 color=(0.55, 0.5, 0.6), 71 icon=bui.gettexture('crossOut'), 72 iconscale=1.2, 73 ) 74 75 v = height - 74 76 77 if legacy_code_mode: 78 v -= 20 79 else: 80 v -= 20 81 bui.textwidget( 82 parent=self._root_widget, 83 text=bui.Lstr(resource='sendInfoDescriptionText'), 84 maxwidth=width * 0.9, 85 position=(width * 0.5, v), 86 color=(0.7, 0.7, 0.7, 1.0), 87 size=(0, 0), 88 scale=0.8, 89 h_align='center', 90 v_align='center', 91 ) 92 v -= 20 93 94 # bui.textwidget( 95 # parent=self._root_widget, 96 # text=bui.Lstr( 97 # resource='supportEmailText', 98 # subs=[('${EMAIL}', 'support@froemling.net')], 99 # ), 100 # maxwidth=width * 0.9, 101 # position=(width * 0.5, v), 102 # color=(0.7, 0.7, 0.7, 1.0), 103 # size=(0, 0), 104 # scale=0.65, 105 # h_align='center', 106 # v_align='center', 107 # ) 108 v -= 80 109 110 bui.textwidget( 111 parent=self._root_widget, 112 text=bui.Lstr( 113 resource=( 114 f'{self._r}.codeText' 115 if legacy_code_mode 116 else 'descriptionText' 117 ) 118 ), 119 position=(22, v), 120 color=(0.8, 0.8, 0.8, 1.0), 121 size=(90, 30), 122 h_align='right', 123 maxwidth=100, 124 ) 125 v -= 8 126 127 self._text_field = bui.textwidget( 128 parent=self._root_widget, 129 position=(125, v), 130 size=(280 if legacy_code_mode else 380, 46), 131 text='', 132 h_align='left', 133 v_align='center', 134 max_chars=64, 135 color=(0.9, 0.9, 0.9, 1.0), 136 description=bui.Lstr( 137 resource=( 138 f'{self._r}.codeText' 139 if legacy_code_mode 140 else 'descriptionText' 141 ) 142 ), 143 editable=True, 144 padding=4, 145 on_return_press_call=self._activate_enter_button, 146 ) 147 bui.widget(edit=btn, down_widget=self._text_field) 148 149 v -= 79 150 b_width = 200 151 self._enter_button = btn2 = bui.buttonwidget( 152 parent=self._root_widget, 153 position=(width * 0.5 - b_width * 0.5, v), 154 size=(b_width, 60), 155 scale=1.0, 156 label=bui.Lstr( 157 resource='submitText', fallback_resource=f'{self._r}.enterText' 158 ), 159 on_activate_call=self._do_enter, 160 ) 161 bui.containerwidget( 162 edit=self._root_widget, 163 cancel_button=btn, 164 start_button=btn2, 165 selected_child=self._text_field, 166 ) 167 168 def _do_back(self) -> None: 169 # pylint: disable=cyclic-import 170 171 if not self._modal: 172 self.main_window_back() 173 return 174 175 # Handle modal case: 176 177 # no-op if our underlying widget is dead or on its way out. 178 if not self._root_widget or self._root_widget.transitioning_out: 179 return 180 181 bui.containerwidget( 182 edit=self._root_widget, transition=self._transition_out 183 ) 184 185 def _activate_enter_button(self) -> None: 186 self._enter_button.activate() 187 188 def _do_enter(self) -> None: 189 # pylint: disable=cyclic-import 190 # from bauiv1lib.settings.advanced import AdvancedSettingsWindow 191 192 plus = bui.app.plus 193 assert plus is not None 194 195 description: Any = bui.textwidget(query=self._text_field) 196 assert isinstance(description, str) 197 198 if self._modal: 199 # no-op if our underlying widget is dead or on its way out. 200 if not self._root_widget or self._root_widget.transitioning_out: 201 return 202 bui.containerwidget( 203 edit=self._root_widget, transition=self._transition_out 204 ) 205 else: 206 # no-op if we're not in control. 207 if not self.main_window_has_control(): 208 return 209 self.main_window_back() 210 211 # Used for things like unlocking shared playlists or linking 212 # accounts: talk directly to V1 server via transactions. 213 if self._legacy_code_mode: 214 if plus.get_v1_account_state() != 'signed_in': 215 bui.screenmessage( 216 bui.Lstr(resource='notSignedInErrorText'), color=(1, 0, 0) 217 ) 218 bui.getsound('error').play() 219 else: 220 plus.add_v1_account_transaction( 221 { 222 'type': 'PROMO_CODE', 223 'expire_time': time.time() + 5, 224 'code': description, 225 } 226 ) 227 plus.run_v1_account_transactions() 228 else: 229 bui.app.create_async_task(_send_info(description))
Window for sending info to the developer.
SendInfoWindow( modal: bool = False, legacy_code_mode: bool = False, transition: str | None = 'in_scale', origin_widget: _bauiv1.Widget | None = None)
21 def __init__( 22 self, 23 modal: bool = False, 24 legacy_code_mode: bool = False, 25 transition: str | None = 'in_scale', 26 origin_widget: bui.Widget | None = None, 27 ): 28 self._legacy_code_mode = legacy_code_mode 29 30 # Need to wrangle our own transition-out in modal mode. 31 if origin_widget is not None: 32 self._transition_out = 'out_scale' 33 else: 34 self._transition_out = 'out_right' 35 36 width = 450 if legacy_code_mode else 600 37 height = 200 if legacy_code_mode else 300 38 39 self._modal = modal 40 self._r = 'promoCodeWindow' 41 42 assert bui.app.classic is not None 43 uiscale = bui.app.ui_v1.uiscale 44 super().__init__( 45 root_widget=bui.containerwidget( 46 size=(width, height), 47 toolbar_visibility=( 48 'menu_minimal_no_back' 49 if uiscale is bui.UIScale.SMALL or modal 50 else 'menu_full' 51 ), 52 scale=( 53 2.0 54 if uiscale is bui.UIScale.SMALL 55 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 56 ), 57 ), 58 transition=transition, 59 origin_widget=origin_widget, 60 ) 61 62 btn = bui.buttonwidget( 63 parent=self._root_widget, 64 scale=0.5, 65 position=(40, height - 40), 66 size=(60, 60), 67 label='', 68 on_activate_call=self._do_back, 69 autoselect=True, 70 color=(0.55, 0.5, 0.6), 71 icon=bui.gettexture('crossOut'), 72 iconscale=1.2, 73 ) 74 75 v = height - 74 76 77 if legacy_code_mode: 78 v -= 20 79 else: 80 v -= 20 81 bui.textwidget( 82 parent=self._root_widget, 83 text=bui.Lstr(resource='sendInfoDescriptionText'), 84 maxwidth=width * 0.9, 85 position=(width * 0.5, v), 86 color=(0.7, 0.7, 0.7, 1.0), 87 size=(0, 0), 88 scale=0.8, 89 h_align='center', 90 v_align='center', 91 ) 92 v -= 20 93 94 # bui.textwidget( 95 # parent=self._root_widget, 96 # text=bui.Lstr( 97 # resource='supportEmailText', 98 # subs=[('${EMAIL}', 'support@froemling.net')], 99 # ), 100 # maxwidth=width * 0.9, 101 # position=(width * 0.5, v), 102 # color=(0.7, 0.7, 0.7, 1.0), 103 # size=(0, 0), 104 # scale=0.65, 105 # h_align='center', 106 # v_align='center', 107 # ) 108 v -= 80 109 110 bui.textwidget( 111 parent=self._root_widget, 112 text=bui.Lstr( 113 resource=( 114 f'{self._r}.codeText' 115 if legacy_code_mode 116 else 'descriptionText' 117 ) 118 ), 119 position=(22, v), 120 color=(0.8, 0.8, 0.8, 1.0), 121 size=(90, 30), 122 h_align='right', 123 maxwidth=100, 124 ) 125 v -= 8 126 127 self._text_field = bui.textwidget( 128 parent=self._root_widget, 129 position=(125, v), 130 size=(280 if legacy_code_mode else 380, 46), 131 text='', 132 h_align='left', 133 v_align='center', 134 max_chars=64, 135 color=(0.9, 0.9, 0.9, 1.0), 136 description=bui.Lstr( 137 resource=( 138 f'{self._r}.codeText' 139 if legacy_code_mode 140 else 'descriptionText' 141 ) 142 ), 143 editable=True, 144 padding=4, 145 on_return_press_call=self._activate_enter_button, 146 ) 147 bui.widget(edit=btn, down_widget=self._text_field) 148 149 v -= 79 150 b_width = 200 151 self._enter_button = btn2 = bui.buttonwidget( 152 parent=self._root_widget, 153 position=(width * 0.5 - b_width * 0.5, v), 154 size=(b_width, 60), 155 scale=1.0, 156 label=bui.Lstr( 157 resource='submitText', fallback_resource=f'{self._r}.enterText' 158 ), 159 on_activate_call=self._do_enter, 160 ) 161 bui.containerwidget( 162 edit=self._root_widget, 163 cancel_button=btn, 164 start_button=btn2, 165 selected_child=self._text_field, 166 )
Create a MainWindow given a root widget and transition info.
Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.
Inherited Members
- bauiv1._uitypes.MainWindow
- main_window_back_state
- main_window_is_top_level
- main_window_close
- main_window_has_control
- main_window_back
- main_window_replace
- on_main_window_close
- get_main_window_state
- bauiv1._uitypes.Window
- get_root_widget