Module userland.scripts.messages.details_modal

Message details screen

Classes

class DetailsModal (*args, reply_to: Message | None = None, **kwargs)

Message details screen

Initialize the screen.

Args

name
The name of the screen.
id
The ID of the screen in the DOM.
classes
The CSS classes for the screen.
Expand source code
class DetailsModal(ModalScreen):
    """Message details screen"""

    CSS = """
        DetailsModal {
            align: center middle;
            background: rgba(0, 0, 0, 0.5);
        }

        Label {
            margin-top: 1;
            width: 5;
        }

        Button {
            margin-top: 1;
            width: 50%;
        }

        Input {
            width: 53;
        }

        #save {
            margin-right: 1;
            margin-top: 1;
        }

        #wrapper {
            background: $primary-background;
            height: 12;
            padding: 1;
            width: 60;
        }
    """

    reply_to: Message | None
    response: str

    def __init__(self, *args, reply_to: Message | None = None, **kwargs):
        super().__init__(*args, **kwargs)
        self.reply_to = reply_to

    def compose(self):
        yield Vertical(
            Horizontal(
                Label("Title", shrink=True),
                Input(
                    (
                        f"Re: {self.reply_to.title.lstrip('Re: ')}"
                        if self.reply_to
                        else ""
                    ),
                    id="title",
                    max_length=MAX_TITLE_LENGTH,
                    validators=[
                        validation.Length(
                            1, failure_description="Title is required"
                        )
                    ],
                    validate_on=("changed", "submitted"),
                ),
            ),
            Horizontal(
                Label("Tags", shrink=True),
                Input(id="tags"),
            ),
            Horizontal(
                Button("Save", variant="success", name="save", id="save"),
                Button("Cancel", variant="error", name="cancel"),
            ),
            id="wrapper",
        )

    async def on_mount(self) -> None:
        if not self.reply_to:
            return

        tags = " ".join(
            [
                t.tag_name
                for t in await MessageTags.query.where(
                    MessageTags.message_id == self.reply_to.id
                ).gino.all()
            ]
        )
        inp: Input = self.get_widget_by_id("tags")  # type: ignore
        inp.value = tags

    async def submit(self) -> None:
        app: XthuluApp = self.app  # type: ignore
        title: Input = self.get_widget_by_id("title")  # type: ignore
        title_validator = title.validate(title.value)

        if title_validator and not title_validator.is_valid:
            return

        content = self.app._background_screens[-1].query_one(TextArea)
        tags: Input = self.get_widget_by_id("tags")  # type: ignore
        tags_validator = tags.validate(tags.value)

        if tags_validator and not tags_validator.is_valid:
            return

        all_tags = set(tags.value.split(" "))
        existing_tags = set(
            [
                t.name
                for t in await MessageTag.query.where(
                    MessageTag.name.in_(all_tags)
                ).gino.all()
            ]
        )
        nonexistent_tags = all_tags.difference(existing_tags)

        # create missing tags
        for t in nonexistent_tags:
            await MessageTag.create(name=t)

        # create message
        message = await Message.create(
            author_id=app.context.user.id,
            title=title.value,
            content=content.text,
            parent_id=self.reply_to.id if self.reply_to else None,
        )

        # link tags to message
        for t in all_tags:
            await MessageTags.create(message_id=message.id, tag_name=t)

        self.app.pop_screen()  # pop this modal
        self.app.pop_screen()  # pop the editor

    async def on_input_submitted(self, event: Input.Submitted) -> None:
        await self.submit()

    async def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.name == "cancel":
            self.app.pop_screen()  # pop this modal
            return

        await self.submit()

    async def key_escape(self, _):
        self.app.pop_screen()  # pop this modal

Ancestors

  • textual.screen.ModalScreen
  • textual.screen.Screen
  • typing.Generic
  • textual.widget.Widget
  • textual.dom.DOMNode
  • textual.message_pump.MessagePump

Class variables

var CSS
var can_focus
var can_focus_children
var reply_toMessage | None
var response : str

Methods

def compose(self)

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don't typically need to explicitly call this method.

Example

def compose(self) -> ComposeResult:
    yield Header()
    yield Label("Press the button below:")
    yield Button()
    yield Footer()
async def key_escape(self, _)
async def on_button_pressed(self, event: textual.widgets._button.Button.Pressed) ‑> None
async def on_input_submitted(self, event: textual.widgets._input.Input.Submitted) ‑> None
async def on_mount(self) ‑> None
async def submit(self) ‑> None