diff --git a/examples/todoapp/todoapp/__init__.py b/examples/todoapp/todoapp/__init__.py index b813ab5..e88674b 100644 --- a/examples/todoapp/todoapp/__init__.py +++ b/examples/todoapp/todoapp/__init__.py @@ -79,12 +79,14 @@ class Layout(Component): \frame \frame padding=5 weight:y='2:10' weight:x='2:10' pos:grid=0,0 pos:sticky='nsew' \button command={back} image=img:@backward{width: 20} pos:grid=0,0 pos:sticky='w' bootstyle='dark outline' - !if is_login() - \label text='logged in!' pos:grid=1,0 + !if User.is_login() + \label text={f'logged in as {User.current().name}'} pos:grid=1,0 \button command={forward} image=img:@forward{width: 20} pos:grid=3,0 pos:sticky='e' bootstyle='dark outline' \frame:outlet pos:grid=0,1 """ + user = None + def __init__(self, app): self.app = app super().__init__() @@ -95,5 +97,4 @@ def back(self): def forward(self): self.app.forward() - def is_login(self): - return User.current() is None + User = User diff --git a/examples/todoapp/todoapp/admin.py b/examples/todoapp/todoapp/admin.py index cbf3a54..bea5c89 100644 --- a/examples/todoapp/todoapp/admin.py +++ b/examples/todoapp/todoapp/admin.py @@ -114,6 +114,10 @@ def create_todo(self, desc: str, done: bool = False): 'done': done, }) + @classmethod + def is_login(cls): + return cls.__current_user__ is not None + @dataclass @annotate diff --git a/examples/todoapp/todoapp/data.json b/examples/todoapp/todoapp/data.json index a42aadc..deb3b33 100644 --- a/examples/todoapp/todoapp/data.json +++ b/examples/todoapp/todoapp/data.json @@ -1,4 +1,10 @@ { - "users": [], + "users": [ + { + "name": "ken-morel", + "uuid": "9fd496a6-64a8-11ef-b107-28d244ed9304", + "password": "ama" + } + ], "todos": [] -} \ No newline at end of file +} diff --git a/examples/todoapp/todoapp/dictionaries/English.yml b/examples/todoapp/todoapp/dictionaries/English.yml index 5ce3ee3..06c8e1e 100644 --- a/examples/todoapp/todoapp/dictionaries/English.yml +++ b/examples/todoapp/todoapp/dictionaries/English.yml @@ -11,6 +11,7 @@ pages: It is still in development, and I will be happy to hear of you issues at https://github.com/ken-morel/taktk/issues next: Go to dashboard > + login: Login > todos: placeholder: Enter you todo her please mark-done: Mark as done @@ -20,6 +21,11 @@ pages: toggle: Toggle done remove: *todosRm edit: edit + signin: + label: + name: "Name: " + password: "Password: " + submit: Login > nav: back: < back next: next > diff --git a/examples/todoapp/todoapp/dictionaries/French.yml b/examples/todoapp/todoapp/dictionaries/French.yml index 7cd231d..dcec729 100644 --- a/examples/todoapp/todoapp/dictionaries/French.yml +++ b/examples/todoapp/todoapp/dictionaries/French.yml @@ -12,15 +12,21 @@ pages: de vos conseil or problemmes que vous rencotrerier en utilisant taktk sur https://github.com/ken-morel/taktk/issues next: Votre... vue d'enseble? > + login: Se connecter > todos: placeholder: Notez ici mark-done: Fait mark-undone: A faire - remove: retirer + remove: &todosRm retirer menu: toggle: Toggle done - remove: Remove + remove: *todosRm edit: edit + signin: + label: + name: "Nom: " + password: "Mot de passe: " + submit: Proceder > nav: back: < precedant next: suivant > diff --git a/examples/todoapp/todoapp/pages/__init__.py b/examples/todoapp/todoapp/pages/__init__.py index f9e2ac9..7422f7b 100644 --- a/examples/todoapp/todoapp/pages/__init__.py +++ b/examples/todoapp/todoapp/pages/__init__.py @@ -2,6 +2,7 @@ from . import todos from taktk.notification import Notification +from ..admin import User class Index(Component): @@ -9,7 +10,10 @@ class Index(Component): \frame padding=30 weight:x='1:5' \frame pos:grid=0,1 padding=5 pos:sticky='' \label text=[pages.index.welcome] pos:grid=0,0 font="{courier 10 bold}" - \ctk.button pos:grid=0,2 text=[pages.index.next] pos:sticky='se' command={gt_next} + !if User.is_login() + \ctk.button pos:grid=0,2 text=[pages.index.next] pos:sticky='se' command={gt_next} + !if not User.is_login() + \ctk.button pos:grid=0,2 text=[pages.index.login] pos:sticky='se' command={gt_login} """ def gt_next(self): @@ -17,6 +21,12 @@ def gt_next(self): taktk.application("todos") + def gt_login(self): + import taktk + taktk.application("sign", "signin", redirect='todos') + + User = User + def handle(): return Index() diff --git a/examples/todoapp/todoapp/pages/sign.py b/examples/todoapp/todoapp/pages/sign.py new file mode 100644 index 0000000..29cf00f --- /dev/null +++ b/examples/todoapp/todoapp/pages/sign.py @@ -0,0 +1,54 @@ +from taktk.component import Component +from taktk.notification import Notification +import taktk +from ..admin import User + + +class Signin(Component): + r""" + \frame:form weight:x='0: 1' weight:y='0: 1' padding=20 + \frame:form pos:grid=0,0 weight:x='0:1' + \frame pos:grid=0,0 padding=5 + \label text=[pages.signin.label.name] pos:grid=0,0 pos:sticky='w' + \entry text={{username}} pos:grid=1,0 width=50 pos:sticky='e' + \frame pos:grid=0,1 padding=5 + \label text=[pages.signin.label.password] pos:grid=0,0 pos:sticky='w' + \entry text={{password}} pos:grid=1,0 width=50 pos:sticky='e' + + \ctk.button command={signin} text=[pages.signin.submit] pos:grid=0,1 pos:sticky='se' + """ + + def __init__(self, redirect): + self.redirect = redirect + super().__init__() + + username = "" + password = "" + + def signin(self): + name = self['username'] + password = self['password'] + try: + user = User.login(name, password) + except User.DoesNotExist: + Notification( + "Todos", + "Wrong login: please verify credentials and re-enter", + bootstyle='danger', + source=None, + duration=10000, + ).show() + else: + Notification( + "Todos", + "Signin successful", + bootstyle='info', + source="signin-page", + duration=5000, + ).show() + taktk.application.view.url(self.redirect) + + + +def signin(redirect='todos'): + return Signin(redirect=redirect) diff --git a/src/taktk/application.py b/src/taktk/application.py index 45666dd..0e382b7 100644 --- a/src/taktk/application.py +++ b/src/taktk/application.py @@ -1,6 +1,7 @@ from ttkbootstrap import Window from .page import * from . import settings +import json class Application: @@ -63,8 +64,19 @@ def run(self, entry="/"): self.view.url(entry) self.root.mainloop() - def __call__(self, url: str): - self.view.url(url) + def __call__(self, module, function=None, /, **params): + from urllib.parse import quote + + path = module + id_ = None + if len(params) > 0: + path += "?" + "&".join([ + f"{quote(name)}={quote(json.dumps(value))}" + for name, value in params.items() + ]) + if function is not None: + path += f"#{function}" + self.view.url(path) def exit(self): self.root.destroy() diff --git a/src/taktk/component/parser.py b/src/taktk/component/parser.py index c881ae7..f87ca09 100644 --- a/src/taktk/component/parser.py +++ b/src/taktk/component/parser.py @@ -287,11 +287,12 @@ def next_if(_state: State) -> tuple[State, str, tuple[str, str]]: state = begin.copy() state += len("!if ") state |= skip_spaces(state) + b = state.copy() - nstate, condition = next_value(state) - state |= nstate + while state and state[...][0] != "\n": + state += 1 - return state, condition + return state, state.text[b:state] @annotate diff --git a/src/taktk/page.py b/src/taktk/page.py index 10543b2..f825840 100644 --- a/src/taktk/page.py +++ b/src/taktk/page.py @@ -1,4 +1,7 @@ from importlib import import_module +from urllib.parse import urlparse, parse_qsl +import json +# ParseResult(scheme='http', netloc='192.168.23.48', path='/todos/sign.php', params='', query='reason=3', fragment='out') class PageView: @@ -60,6 +63,10 @@ def __init__(self, package): self.package = package def run_command(self, cmd): - cmd = cmd.rstrip("/") + parsed = urlparse(cmd) + cmd = parsed.path.rstrip("/") path = [self.package.__package__] + list(filter(bool, cmd.split("/"))) - return import_module(".".join(path)).handle() + module = import_module(".".join(path)) + function = getattr(module, parsed.fragment or 'handle') + args = {k: json.loads(v) for k, v in parse_qsl(parsed.query)} + return function(**args)