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

Window for sending info to the developer.

SendInfoWindow( modal: bool = False, legacy_code_mode: bool = False, origin_widget: _bauiv1.Widget | None = None)
 21    def __init__(
 22        self,
 23        modal: bool = False,
 24        legacy_code_mode: bool = False,
 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        if origin_widget is not None:
 31            self._transition_out = 'out_scale'
 32            scale_origin = origin_widget.get_screen_space_center()
 33            transition = 'in_scale'
 34        else:
 35            self._transition_out = 'out_right'
 36            scale_origin = None
 37            transition = 'in_right'
 38
 39        width = 450 if legacy_code_mode else 600
 40        height = 200 if legacy_code_mode else 300
 41
 42        self._modal = modal
 43        self._r = 'promoCodeWindow'
 44
 45        assert bui.app.classic is not None
 46        uiscale = bui.app.ui_v1.uiscale
 47        super().__init__(
 48            root_widget=bui.containerwidget(
 49                size=(width, height),
 50                transition=transition,
 51                toolbar_visibility='menu_minimal_no_back',
 52                scale_origin_stack_offset=scale_origin,
 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        )
 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                    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                    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=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        )
Inherited Members
bauiv1._uitypes.Window
get_root_widget