|
| 1 | +# Frontend aspects |
| 2 | + |
| 3 | +## controller.py |
| 4 | + |
| 5 | +You can have your own frontend with a blueprint (flask blueprints are explained [here](https://realpython.com/flask-blueprint/)). If you only have one, it needs to have a `/` route in order to be linkable from the `choose your plugin` page. |
| 6 | +If you create your extension with a blueprint, it'll also create a controller for you which, simplified, looks like this: |
| 7 | +```python |
| 8 | +rubberduck_endpoint = ScratchpadService.blueprint |
| 9 | + |
| 10 | +def ext() -> ScratchpadService: |
| 11 | + ''' convenience for getting the extension-object''' |
| 12 | + return app.specter.ext["rubberduck"] |
| 13 | + |
| 14 | +def specter() -> Specter: |
| 15 | + ''' convenience for getting the specter-object''' |
| 16 | + return app.specter |
| 17 | + |
| 18 | + |
| 19 | +@rubberduck.route("/") |
| 20 | +@login_required |
| 21 | +@user_secret_decrypted_required |
| 22 | +def index(): |
| 23 | + return render_template( |
| 24 | + "rubberduck/index.jinja", |
| 25 | + ) |
| 26 | +[...] |
| 27 | +``` |
| 28 | + |
| 29 | +You can also have more than one blueprint. Define them like this in your service class: |
| 30 | +```python |
| 31 | + blueprint_modules = { |
| 32 | + "default" : "mynym.specterext.rubberduck.controller", |
| 33 | + "ui" : "mynym.specterext.rubberduck.controller_ui" |
| 34 | + } |
| 35 | +``` |
| 36 | + |
| 37 | +You have to have a default blueprint which has the above mentioned index page. |
| 38 | +In your controller, the endpoint needs to be specified like this: |
| 39 | + |
| 40 | +```python |
| 41 | +ui = RubberduckService.blueprints["ui"] |
| 42 | +``` |
| 43 | + |
| 44 | +## Templates and Static Resources |
| 45 | + |
| 46 | +The minimal url routes for `Service` selection and management. As usualy in Flask, `templates` and `static` resources are in their respective subfolders. Please note that there is an additional directory with the id of the extension which looks redundant at first. This is due to the way blueprints are loading templates and ensures that there are no naming collisions. Maybe at a later stage, this can be used to let plugins override other plugin's templates. |
| 47 | + |
| 48 | +## Modifying non-extension pages |
| 49 | + |
| 50 | +You might have an extension which wants to inject e.g. JavaScript code into each and every page of Specter Desktop. You can do that via overwriting one of the `inject_in_basejinja_*` methods in your service-class: |
| 51 | +```python |
| 52 | + @classmethod |
| 53 | + def inject_in_basejinja_head(cls): |
| 54 | + ''' e.g. rendering some snippet ''' |
| 55 | + return render_template("devhelp/html_inject_in_basejinja_head.jinja") |
| 56 | + |
| 57 | + @classmethod |
| 58 | + def inject_in_basejinja_body_top(cls): |
| 59 | + ''' or directly returning text ''' |
| 60 | + return "<script>console.log('Hello from body top')" |
| 61 | + |
| 62 | + @classmethod |
| 63 | + def inject_in_basejinja_body_bottom(cls): |
| 64 | + return "something here" |
| 65 | +``` |
| 66 | + |
| 67 | +For this to work, the extension needs to be activated for the user, though. |
| 68 | + |
| 69 | +### Extending dialogs |
| 70 | +You can extend the settings dialog or the wallet-dialog with your own templates. To do that, create a callback method in your service like: |
| 71 | + |
| 72 | +```python |
| 73 | +from cryptoadvance.specter.services import callbacks |
| 74 | +# [...] |
| 75 | + def callback_add_settingstabs(self): |
| 76 | + ''' Extending the settings tab with an own tab called "myexttitle" ''' |
| 77 | + return [{"title": "myexttitle", "endpoint":"myext_something"}] |
| 78 | + |
| 79 | + def callback_add_wallettabs(self): |
| 80 | + ''' Extending the wallets tab with an own tab called "mywalletdetails" ''' |
| 81 | + return [{"title": "mywalletdetails", "endpoint":"myext_mywalletdetails"}] |
| 82 | +``` |
| 83 | + |
| 84 | +In this case, this would add a tab called "myexttitle" and you're now supposed to provide an endpoint in your controller which might be called `myext_something` e.g. like this: |
| 85 | + |
| 86 | +```python |
| 87 | +@myext_endpoint.route("/settings_something", methods=["GET"]) |
| 88 | +def myext_something(): |
| 89 | + return render_template( |
| 90 | + "myext/some_settingspage.jinja", |
| 91 | + ext_settingstabs = app.specter.service_manager.execute_ext_callbacks( |
| 92 | + callbacks.add_settingstabs |
| 93 | + ) |
| 94 | + ) |
| 95 | +``` |
| 96 | + |
| 97 | +If you want to have an additional wallet tab, you would specify something like: |
| 98 | + |
| 99 | +```python |
| 100 | +@myext_endpoint.route("/wallet/<wallet_alias>/mywalletdetails", methods=["GET"]) |
| 101 | +def myext_mywalletdetails(wallet_alias): |
| 102 | + wallet: Wallet = app.specter.wallet_manager.get_by_alias(wallet_alias) |
| 103 | + return render_template( |
| 104 | + "myext/mywalletdetails.jinja", |
| 105 | + wallet_alias=wallet_alias, |
| 106 | + wallet=wallet, |
| 107 | + specter=app.specter, |
| 108 | + ext_wallettabs = app.specter.service_manager.execute_ext_callbacks( |
| 109 | + callbacks.add_wallettabs |
| 110 | + ) |
| 111 | + ) |
| 112 | +``` |
| 113 | + |
| 114 | +The `some_settingspage.jinja` should probably look exactly like all the other setting pages and you would do this like this: |
| 115 | + |
| 116 | +```jinja |
| 117 | +{% extends "base.jinja" %} |
| 118 | +{% block main %} |
| 119 | + <form action="?" method="POST" onsubmit="showPacman()"> |
| 120 | + <input type="hidden" class="csrf-token" name="csrf_token" value="{{ csrf_token() }}"/> |
| 121 | + <h1 id="title" class="settings-title">Settings</h1> |
| 122 | + {% from 'settings/components/settings_menu.jinja' import settings_menu %} |
| 123 | + {{ settings_menu('myext_something', current_user, setting_exts) }} |
| 124 | + <div class="card" style="margin: 20px auto;"> |
| 125 | + <h1>{{ _("Something something") }} </h1> |
| 126 | + </div> |
| 127 | + </form> |
| 128 | +{% endblock %} |
| 129 | +``` |
| 130 | + |
| 131 | + |
| 132 | + |
| 133 | + |
| 134 | +A reasonable `mywalletdetails.jinja` would look like this: |
| 135 | + |
| 136 | +```jinja |
| 137 | +{% extends "wallet/components/wallet_tab.jinja" %} |
| 138 | +{% set tab = 'my details' %} |
| 139 | +{% block content %} |
| 140 | + <br> |
| 141 | + <div class="center card" style="width: 610px; padding-top: 25px;"> |
| 142 | + Some content here for the wallet {{ wallet_alias }} |
| 143 | + </div> |
| 144 | +{% endblock %} |
| 145 | +``` |
| 146 | + |
| 147 | + |
| 148 | + |
0 commit comments