buyComponentDialogue.py (13371B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\cosmetics\ship\pages\store\buyComponentDialogue.py 2 import carbonui 3 import carbonui.text.styles 4 import eveformat 5 import eveicon 6 import uthread2 7 from carbonui import Align, AxisAlignment, ButtonStyle, ButtonVariant, Density, TextColor, uiconst, TextAlign, PickState 8 from carbonui.text.styles import TextHeader 9 from carbonui.button.group import ButtonGroup, ButtonSizeMode 10 from carbonui.control.button import Button 11 from carbonui.control.checkbox import Checkbox 12 from carbonui.control.singlelineedits.singleLineEditInteger import SingleLineEditInteger 13 from carbonui.control.window import Window 14 from carbonui.decorative.divider_line import DividerLine 15 from carbonui.primitives.container import Container 16 from carbonui.primitives.containerAutoSize import ContainerAutoSize 17 from carbonui.primitives.sprite import Sprite 18 from cosmetics.client.shipSkinComponentTradingSvc import get_ship_skin_component_trading_svc 19 from cosmetics.client.ships.skins.errors import get_listing_error_text 20 from cosmetics.common.ships.skins.util import Currency 21 from eve.client.script.ui import eveColor 22 from eve.client.script.ui.control.infoNotice import InfoNotice 23 from player_messaging.client.ui_message import message_player 24 from eve.client.script.ui.cosmetics.ship.controls.buyerFee import BuySKINBuyerFee 25 from eve.client.script.ui.cosmetics.ship.pages.cards.componentListingCard import ComponentListingCard 26 from eve.client.script.ui.cosmetics.ship.pages.store import storeSignals 27 from eve.client.script.ui.shared.neocom.wallet.buyMissingPlexDialog import BuyMissingPlexDialog 28 from localization import GetByLabel 29 from plex.client.api.repository import get_repository 30 from publicGateway.grpc.exceptions import GenericException 31 from stackless_response_router.exceptions import TimeoutException 32 33 class BuyComponentDialogue(Window): 34 default_windowID = 'BuyComponentDialogue' 35 default_iconNum = 'res:/UI/Texture/WindowIcons/paint_tool.png' 36 default_caption = GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/BuyElement') 37 default_fixedWidth = 515 38 default_isMinimizable = False 39 default_isStackable = False 40 default_isCollapseable = False 41 42 def __init__(self, listing, *args, **kwargs): 43 super(BuyComponentDialogue, self).__init__(*args, **kwargs) 44 self.listing = listing 45 self.buy_thread = None 46 self.construct_layout() 47 48 def Close(self, setClosed = False, *args, **kwargs): 49 try: 50 self.kill_threads() 51 finally: 52 super(BuyComponentDialogue, self).Close(setClosed, *args, **kwargs) 53 54 def kill_threads(self): 55 if self.buy_thread is not None: 56 self.buy_thread.kill() 57 self.buy_thread = None 58 59 def construct_content_cont(self): 60 return ContainerAutoSize(parent=self.sr.maincontainer, name='main', align=uiconst.TOTOP, padding=self.content_padding) 61 62 def construct_layout(self): 63 self.content.on_size_changed.connect(self.on_content_size_changed) 64 self.construct_card() 65 name = self.listing.name_with_quantity if hasattr(self.listing, 'name_with_quantity') else '' 66 carbonui.text.styles.TextHeader(parent=self.content, align=Align.TOTOP, text=name, textAlign=TextAlign.CENTER, padTop=-16) 67 DividerLine(parent=self.content, align=Align.TOTOP, padTop=32) 68 self.construct_buyer_fee() 69 self.construct_price() 70 self.construct_quantity() 71 DividerLine(parent=self.content, align=Align.TOTOP, padTop=16) 72 self.construct_activate_now_checkbox() 73 self.construct_item_delivery_notice() 74 self.construct_buttons() 75 self.update() 76 77 def construct_activate_now_checkbox(self): 78 self.activate_now_checkbox = Checkbox(name='activate_now_checkbox', parent=self.content, align=Align.TOTOP, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Store/ActivateNow'), hint=GetByLabel('UI/Personalization/ShipSkins/SKINR/Store/ActivateNowHint'), padTop=16, checked=True, callback=self.on_activate_now_checkbox) 79 80 def on_activate_now_checkbox(self, *args): 81 self.update_item_delivery_notice() 82 83 def on_content_size_changed(self, width, height): 84 _, height = self.GetWindowSizeForContentSize(height=height) 85 self.SetFixedHeight(height) 86 87 def construct_card(self): 88 card_container = ContainerAutoSize(name='card_container', parent=self.content, align=Align.TOTOP, padTop=16) 89 card = BuyComponentListingCard(name='card', parent=card_container, align=Align.CENTERTOP, listing=self.listing) 90 card._on_selected = lambda *args, **kwargs: None 91 card.is_selected = True 92 93 def construct_item_delivery_notice(self): 94 self.item_delivery_notice = InfoNotice(name='item_delivery_notice', parent=self.content, align=uiconst.TOTOP, labelText=GetByLabel('UI/Personalization/ShipSkins/SKINR/ItemDeliveryNotice'), padTop=16) 95 self.item_delivery_notice.noticeLabel.maxLines = None 96 97 def construct_price(self): 98 price_container = Container(name='price_container', parent=self.content, align=Align.TOTOP, height=32, padTop=16) 99 TextHeader(name='price_label', parent=price_container, align=Align.TOLEFT, text=GetByLabel('UI/Market/Marketbase/Price'), bold=True, padTop=4) 100 self.amount_label = TextHeader(name='amount_label', parent=price_container, align=Align.TORIGHT, state=uiconst.UI_NORMAL, bold=True, padTop=4) 101 icon_container = Container(name='icon_container', parent=price_container, align=Align.TORIGHT, width=16, padRight=4) 102 self.currency_icon = Sprite(name='currency_icon', parent=icon_container, align=Align.CENTER, pos=(0, 0, 16, 16)) 103 104 def construct_buyer_fee(self): 105 self.buyer_fee = BuySKINBuyerFee(name='buyer_fee', parent=self.content, align=Align.TOTOP, padTop=16, display=False) 106 107 def construct_quantity(self): 108 quantity_container = Container(name='quantity_container', parent=self.content, align=Align.TOTOP, height=32, padTop=8) 109 TextHeader(name='quantity_label', parent=quantity_container, align=Align.TOLEFT, text=GetByLabel('UI/Common/Quantity'), bold=True, padTop=4) 110 quantity_edit_max_value = self.listing.quantity 111 if quantity_edit_max_value < 0: 112 quantity_edit_max_value = SingleLineEditInteger.default_maxValue 113 self.quantity_edit = SingleLineEditInteger(name='quantity_edit', parent=quantity_container, align=Align.TORIGHT, density=Density.EXPANDED, minValue=1, maxValue=quantity_edit_max_value, setvalue=1, OnChange=self.on_input_change, width=125) 114 115 def on_input_change(self, *args): 116 self.update() 117 118 def construct_buttons(self): 119 button_group = ButtonGroup(name='button_group', parent=self.content, align=Align.TOTOP, button_alignment=AxisAlignment.CENTER, density=Density.EXPANDED, button_size_mode=ButtonSizeMode.STRETCH, padTop=16) 120 self.buy_button = Button(name='buy_button', label=GetByLabel('UI/Personalization/ShipSkins/SKINR/Store/Buy'), variant=ButtonVariant.PRIMARY, func=self.on_buy_button) 121 button_group.add_button(self.buy_button) 122 self.cancel_button = Button(name='cancel_button', label=GetByLabel('UI/Common/Buttons/Cancel'), variant=ButtonVariant.GHOST, func=self.on_cancel_button) 123 button_group.add_button(self.cancel_button) 124 125 def update(self): 126 self.update_buyer_fee() 127 self.update_amount_label() 128 self.update_currency_icon() 129 self.update_buttons() 130 self.update_item_delivery_notice() 131 132 def update_amount_label(self): 133 self.amount_label.text = eveformat.number(self.total_price) 134 if self.listing.currency == Currency.PLEX and self.has_enough_plex: 135 self.amount_label.color = TextColor.NORMAL 136 self.amount_label.hint = None 137 elif self.listing.currency == Currency.ISK and self.has_enough_isk: 138 self.amount_label.color = TextColor.NORMAL 139 self.amount_label.hint = None 140 else: 141 self.amount_label.color = TextColor.DANGER 142 self.amount_label.hint = GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/ListingErrors/InsufficientFunds') 143 144 def update_currency_icon(self): 145 self.currency_icon.hint = self.get_currency_hint() 146 self.currency_icon.texturePath = self.get_currency_icon() 147 self.currency_icon.color = self.get_currency_color() 148 149 def update_buttons(self): 150 self.buy_button.LoadTooltipPanel = self.load_buy_button_tooltip_panel 151 self.buy_button.style = self.get_buy_button_style() 152 153 def update_item_delivery_notice(self): 154 if hasattr(self, 'item_delivery_notice'): 155 self.item_delivery_notice.display = not self.activate_now_checkbox.checked 156 157 def update_buyer_fee(self): 158 pass 159 160 def has_sufficient_funds(self): 161 if self.listing.currency == Currency.ISK: 162 return self.has_enough_isk 163 elif self.listing.currency == Currency.PLEX: 164 return self.has_enough_plex 165 else: 166 return False 167 168 @property 169 def has_enough_isk(self): 170 return sm.GetService('wallet').GetWealth() >= self.get_required_amount() 171 172 @property 173 def has_enough_plex(self): 174 return get_repository().get_balance() >= self.get_required_amount() 175 176 def get_required_amount(self): 177 return self.listing.price * self.purchase_quantity 178 179 def load_buy_button_tooltip_panel(self, tooltip_panel, *args): 180 if self.has_sufficient_funds(): 181 return 182 tooltip_panel.columns = 1 183 tooltip_panel.LoadStandardSpacing() 184 tooltip_panel.AddLabelMedium(text=self.insufficient_funds_hint, wrapWidth=300, color=eveColor.DANGER_RED) 185 if self.listing.currency == Currency.PLEX and not self.has_enough_plex: 186 tooltip_panel.pickState = PickState.ON 187 button_group = ButtonGroup(parent=tooltip_panel, align=Align.TOTOP, button_size_mode=ButtonSizeMode.DYNAMIC, padTop=8) 188 Button(parent=button_group, label=GetByLabel('UI/Wallet/BuyMore'), func=self.on_buy_more_plex_button, texturePath=eveicon.plex, style=ButtonStyle.MONETIZATION) 189 190 @property 191 def insufficient_funds_hint(self): 192 currencies = [] 193 if self.listing.currency == Currency.ISK and not self.has_enough_isk: 194 currencies.append(GetByLabel('UI/Common/ISK')) 195 if self.listing.currency == Currency.PLEX and not self.has_enough_plex: 196 currencies.append(GetByLabel('UI/Common/PLEX')) 197 return u'{base} ({currencies})'.format(base=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/ListingErrors/InsufficientFunds'), currencies=u'/'.join(currencies)) 198 199 def on_buy_more_plex_button(self, *args): 200 buy_missing_plex_dialog = BuyMissingPlexDialog(required_amount=self.get_required_amount()) 201 buy_missing_plex_dialog.ShowModal() 202 self.SetModalResult(uiconst.ID_CANCEL) 203 204 def get_buy_button_style(self): 205 if self.is_allowed_to_purchase: 206 return ButtonStyle.NORMAL 207 return ButtonStyle.DANGER 208 209 @property 210 def is_allowed_to_purchase(self): 211 return self.has_sufficient_funds() 212 213 @property 214 def purchase_quantity(self): 215 return self.quantity_edit.GetValue() 216 217 def get_currency_hint(self): 218 if self.listing.currency == Currency.ISK: 219 return GetByLabel('UI/Common/ISK') 220 elif self.listing.currency == Currency.PLEX: 221 return GetByLabel('UI/Common/PLEX') 222 else: 223 return None 224 225 def get_currency_icon(self): 226 if self.listing.currency == Currency.ISK: 227 return eveicon.isk 228 elif self.listing.currency == Currency.PLEX: 229 return eveicon.plex 230 else: 231 return None 232 233 def get_currency_color(self): 234 if self.listing.currency == Currency.PLEX: 235 return eveColor.PLEX_YELLOW 236 return eveColor.WHITE 237 238 def on_buy_button(self, *args): 239 if self.is_allowed_to_purchase: 240 self.buy_thread = uthread2.StartTasklet(self._buy_async) 241 242 def _buy_async(self): 243 self.buy_button.busy = True 244 self.buy_button.disabled = True 245 try: 246 error = self.purchase_listing() 247 if error: 248 message_player(GetByLabel(get_listing_error_text(error))) 249 else: 250 self.on_buy_success() 251 except (GenericException, TimeoutException): 252 message_player(GetByLabel('UI/Common/CannotConnectToServer')) 253 finally: 254 self.buy_thread = None 255 self.buy_button.busy = False 256 self.buy_button.disabled = False 257 258 def on_buy_success(self): 259 storeSignals.on_component_listing_selected(None) 260 self.SetModalResult(uiconst.ID_OK) 261 262 def purchase_listing(self): 263 return get_ship_skin_component_trading_svc().purchase_listing(listing=self.listing, quantity=self.quantity, immediately_activate=self.activate_now_checkbox.checked) 264 265 def on_cancel_button(self, *args): 266 self.SetModalResult(uiconst.ID_CANCEL) 267 268 @property 269 def quantity(self): 270 return self.quantity_edit.GetValue() 271 272 @property 273 def total_price(self): 274 return self.listing.price * self.quantity 275 276 277 class BuyComponentListingCard(ComponentListingCard): 278 279 def get_component_name(self): 280 return ''