-
Notifications
You must be signed in to change notification settings - Fork 140
feat: components v2 #1294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: components v2 #1294
Conversation
…t` for now to avoid shadowing
…omponent` more specific
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the idea behind suffixing the types with Input
?
I think it's a bit of a misnomer given only buttons/selects can be used for actual "input"
I'd say MessageComponentInput
=> MessageComponent
sounds better, wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could definitely rename it, it was mostly a decision made on a whim. c:
The [...]Input
part of the type name was meant to signify that it's moreso a MessageComponent
-like thing, ie. a single component, a list of action rows, a list of buttons and selects, etc etc, instead of only a single component like MessageComponent
might suggest(?)
The library internally transforms these shortcuts into the "list of action rows" structure the API would expect (for components v1, at least). But yea, I'll gladly rename these type aliases if we can come up with a more fitting name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just having it plural, like components: MessageComponents
would be good enough
Alternatively, components: abc.Sequence[MessageComponent(s)]
to better drive the fact it's always a sequence (unless we want to support singular components?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bnuuy
id: :class:`int` | ||
TODO | ||
|
||
.. versionadded:: 2.11 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this received even for interactive components? The docs say this is used to identify non-interactive ones 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just having it plural, like components: MessageComponents
would be good enough
Alternatively, components: abc.Sequence[MessageComponent(s)]
to better drive the fact it's always a sequence (unless we want to support singular components?)
""" | ||
|
||
# unused, but technically required by base type | ||
__repr_attributes__: Tuple[str, ...] = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iirc some types define this as ClassVar
and other do not, I think all should have it as ClassVar
for consistency
Edit: components.Component
uses __repr_info__: ClassVar[Tuple[str, ...]]
__repr_attributes__: Tuple[str, ...] = ( | |
__repr_attributes__: ClassVar[Tuple[str, ...]] = ( |
Whether the file is marked as a spoiler. Defaults to ``False``. | ||
""" | ||
|
||
__repr_attributes__: Tuple[str, ...] = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__repr_attributes__: Tuple[str, ...] = ( | |
__repr_attributes__: ClassVar[Tuple[str, ...]] = ( |
file: Any | ||
n/a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should work for existing Attachment
s, since really this only needs a url
|
||
__slots__: Tuple[str, ...] = ("accessory", "components") | ||
|
||
__repr_info__: ClassVar[Tuple[str, ...]] = __slots__ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, Component
has .__repr_info__
, but .ui.UIComponent
has .__repr_attributes__
. Is this desirable?
(I suggest we use the same class variable name everywhere)
|
||
|
||
# see ActionRowMessageComponent | ||
VALID_ACTION_ROW_MESSAGE_TYPES: Final = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say ..._MESSAGE_TYPES
conveys different to intended meaning
VALID_ACTION_ROW_MESSAGE_COMPONENTS
? VALID_ACTION_ROW_MESSAGE_COMPONENT_TYPES
?
Lengthy but less confusing
|
||
def _walk_internal(component: Component, seen: Set[Component]) -> Iterator[Component]: | ||
if component in seen: | ||
# prevent infinite recursion if anyone manages to nest a component in itself |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What it actually does is skipping a component that has already been referenced.
That's not exactly the same as recursion, as a component could be reused between different containers/rows
It is an error if the component has a custom_id/id set, but for non-interactive components with omitted id it's technically fine
|
||
if component_cls := COMPONENT_LOOKUP.get(component_type): | ||
return component_cls(data) # type: ignore | ||
else: | ||
assert_never(component_type) | ||
as_enum = try_enum(ComponentType, component_type) | ||
return Component._raw_construct(type=as_enum) # type: ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use a try - except KeyError - else
here, unknown types are rather exceptional
def _message_component_factory(data: MessageTopLevelComponentPayload) -> MessageTopLevelComponent: | ||
return _component_factory(data) # type: ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def _message_component_factory(data: MessageTopLevelComponentPayload) -> MessageTopLevelComponent: | |
return _component_factory(data) # type: ignore | |
if TYPE_CHECKING: | |
def _message_component_factory(data: MessageTopLevelComponentPayload) -> MessageTopLevelComponent: ... | |
else: | |
_message_component_factory = _component_factory |
Summary
First off, this isn't quite finished, as evidenced by the fact that this is marked as a draft PR and the numerous
TODO
s scattered across the code, but still very much usable in its current state (once the experiment lock gets removed for everyone).Most of the remaining changes are quality-of-life/DX improvements. I consider the interface fairly stable at this point, don't expect many further changes there.
It would probably make sense to wait a little longer with any reviews though, unless it's some glaring issue.
Description
discord/discord-api-docs@af1843d
This implements the new v2 components, including:
Section
: displays a thumbnail or button next to some textTextDisplay
: plaintext, but as a componentMediaGallery
: gallery/mosaic for up to 10 imagesFile
: non-image attachmentsSeparator
: vertical spacer/separatorContainer
: can be considered similar to anEmbed
These new components are all non-interactive and intended for layout only, they are quite a bit more flexible than the previous
content
/embeds
/attachments
/components
combo, and allow for much nicer looking/better structured messages.Scope
I feel like it's worth clarifying that this is about as straightforward as the interface could be, only building on the existing implementation/functionality for components. There may very well be better ways (in terms of DX) to implement these components (namely, builder pattern, yippee), but I've considered this out of scope from the start.
I've intentionally not implemented support for the new components in
View
s, given the intended scope of this PR and the complexities that somehow integrating them would bring, especially since all of the components are non-interactive.The goal with this PR was to get just about the most basic implementation going, any further work building on top of it will be in separate future PRs.
Example
code
(consider
MediaGalleryItem
unfinished, it is largely a placeholder implementation for now)Checklist
pdm lint
pdm pyright