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