Commit Graph

289 Commits

Author SHA1 Message Date
Carlos Garnacho
bc3b890e75 keyboard: Only reset to the default level on unlocked latched levels
Reportedly, non-alphanumeric key levels were able to stick by
happenstance, and let the user press multiple keys until explicitly
switching to a different mode. Reportedly, this broke, switching to
the default level after the first key press on the additional levels.

Since we have this information in the OSK key models (each level has
a "mode" field to either default/latched/locked), retrieve this
information for them for each level, and only reset to the default
level if on one of those latched levels, and the relevant key was not
locked through long-press.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5763
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2945>
2023-09-08 19:42:38 +00:00
Carlos Garnacho
08f3ad3835 keyboard: Reset latched levels on string commit on all situations
The _commitAction() paths have early returns, which made resetting the
latched mode inconsistent depending on the paths taken to commit the
string. This made latched modes not return to normal on e.g. Shell
entries.

Make this happen outside the function, and after the only calling
point, so that the level is correctly reset on all situations.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2945>
2023-09-08 19:42:38 +00:00
Florian Müllner
a42f7c2384 cleanup: Use type-safe comparisons
We have been using type-safe comparisons in new code for quite a while
now, however old code has only been adapted slowly.

Change all the remaining bits to get rid of another legacy style
difference.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2866>
2023-08-09 15:10:38 +00:00
Florian Müllner
9a3913d4a0 cleanup: Use consistent 4-space indent
This is another bit where we've made good progress, and just need
a final push to complete the transition.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2866>
2023-08-09 15:10:37 +00:00
Florian Müllner
071f92cfb6 cleanup: Remove spaces in object literals
We only adopted this style relatively recently, so there's a bit
more to adjust. Still, it's manageable and another step towards
getting rid of the legacy style.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2866>
2023-08-09 15:10:37 +00:00
Evan Welsh
a751e213f6 js: Port to modules
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1499>
2023-08-06 13:02:49 +02:00
Evan Welsh
1e9b906cbc js: Split gi imports to be on new lines to prepare for ES modules
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2794>
2023-06-21 04:50:33 -07:00
Roshan-R
63904a09bf keyboard: Change osk menu entry to control-center keyboard pane
Currenty, the osk settings popup has the menu entry of "Region and
Language settings" which should be changed to the "Keyboard" pane
instead as input source configuration is done there.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6134

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2554>
2022-11-24 13:49:55 +01:00
Carlos Garnacho
a33e3eaf22 keyboard: Ensure completion is disabled when OSK is destroyed
If the OSK is destroyed while visible without being close()'d first,
the completion mode might remain turned on. Ensure it is turned off
on OSK destruction so that typing-booster has no chance to remain
turned on.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2543>
2022-11-16 18:06:27 +00:00
Carlos Garnacho
52253625b8 keyboard: Disconnect from InputMethod::terminal-mode-changed on destruction
The OSK may be created and destroyed, while it connects to signals from the
longer-lived InputMethod object. This makes these signals linger and issue
copious warnings while trying to change the extended keymap and new ratio
on a defunct OSK.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6085
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2543>
2022-11-16 18:06:27 +00:00
Carlos Garnacho
a0352ba312 keyboard: Bind emoji pager visibility to swipeTracker enabled state
The swipeTracker may connect gestures on the stage, so we might end up
in the odd situation that the swipeTracker is handling events for an
invisible actor. Avoid doing this.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2543>
2022-11-16 18:06:27 +00:00
Carlos Garnacho
7e4f7c7e5b keyboard: Explicitly destroy swipeTracker after emoji panel destruction
Even though the emoji panel may be destroyed with the OSK, the swipeTracker
that is set up to navigate between pages is left lingering, and handling
events for some gestures in the stage. This results in warnings like:

  JS ERROR: TypeError: this._panel is null
  set delta@resource:///org/gnome/shell/ui/keyboard.js:720:9
  _onSwipeUpdate@resource:///org/gnome/shell/ui/keyboard.js:750:22
  _updateGesture@resource:///org/gnome/shell/ui/swipeTracker.js:670:14
  vfunc_gesture_progress@resource:///org/gnome/shell/ui/swipeTracker.js:288>

on touch interaction after the OSK was shown and dismissed. In order to fix
this, issue explicit destruction of the swipeTracker when the emoji pager
actor is destroyed.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2543>
2022-11-16 18:06:27 +00:00
Carlos Garnacho
b2f2266199 keyboard: Synchronize input method state after toggling completion
Make the input method synchronize its state after the engine changes
resulting from completion being toggled.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2512>
2022-10-26 10:00:37 +00:00
Carlos Garnacho
76090c14eb inputMethod: Check return value when letting IM handle virtual keys
When propagating keys from the OSK, we usually feed these directly to
the IBusInputContext and let the IM handle the effects of this virtual
key event (which may also include feeding a key event back to us).

But these functions may also return a FALSE value if the key was "let
through" by the IM, which means the ball is in our yard again, and
we are responsible of letting this event get to its destination.

If that happens, just fall through, so the string is committed to
the client as an UTF-8 string, or propagated through keyboard events.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5930
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2508>
2022-10-18 19:31:51 +00:00
Carlos Garnacho
766314acb2 keyboard: Refactor code
Move inline anonymous function to be its own. This method
will become asynchronous in following commits.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2508>
2022-10-18 19:31:51 +00:00
Carlos Garnacho
622590179b keyboard: Use the right property to figure out modifiers set size
During development of the OSK updates, this._modifiers turned from an
array into a Set(), but this location missed switching from length()
to size() to get the cardinality of the contained values.

As a result, the OSK misbehaved and resorted for key event forwarding
for impossible keyvals, instead of handing them directly to the IM.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5766
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2445>
2022-08-23 18:37:11 +00:00
Carlos Garnacho
9bd91ca4d8 keyboard: Change page switching animation in emoji picker
Use a carousel animation to switch pages, and set up the emoji
picker to have the system text direction so that coordinate
flipping is mostly done for us in RTL.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5616
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
c6c6eb5310 keyboard: Shuffle text direction initialization
We do set the full OSK as LTR since keymaps are defined in that
direction. Other actors inside the OSK might want differently so
move this piece of setup to init(), so child actors can set their
own without the OSK overwriting the value.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
2b81355fa1 keyboard: Use SwipeTracker for emoji panel page switching
This allows handling scroll events, in addition to 1fg and pointer
gestures. 3fg gestures are less relevant here.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
aab9899cb7 keyboard: Propagate ratio changes to the emoji selection panel
Update the emoji panel so it can handle ratio changes dynamically,
and propagate the ratio from the Keyboard itself, so that the
emoji panel has a size that fits the OSK panel it was launched
from.

This is more important now with widely varying ratios, like
extended keyboards.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
90bd0c4a25 keyboard: Add Hangul OSK keymap
This keymap requires the corresponding input method for Hangul
input, and the hangul mode to be enabled. Look up for the right
state, and use a corresponding 'us' keymap for english input
otherwise, in order to follow hangul IM behavior.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
b18ab0086e keyboard: Let the OSK take 1/4th of height in portrait mode
And let the keys shrink horizontally to cover the full area.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
f1fcce66aa keyboard: Always use ratio of visible level
Since the OSK keymap actors are cached, we no longer update the aspect
container ratio as often as we should. Ensure to update the aspect ratio
with language/level changes.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
c0a75faf89 keyboard: Use extended US keyboard for terminal mode
If the input hint is "terminal", use the extended keyboard.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
aa341104e5 keyboard: Fill suggestion on button-press-event
Like OSK key buttons, we must avoid the default grabbing behavior
of StButton here. Hook to button-press-event to commit the selected
words, so we get a chance to prevent focus changes on the current
key focus.

Likewise, connect to ::touch-event to handle touch input.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
b210b2de72 ibusManager: Add OSK completion mode
This mode changes the current IBus engine to ibus-typing-booster
under the rug (i.e. no changes in keyboard status menu) for any
XKB engine selected.

In order to make it useful for the currently selected language,
the typing-booster dictionary is changed to the current XKB
layout language. And since the OSK has its own emoji panel,
typing-boosters own emoji completion is disabled.

These changes only apply as long as the OSK panel is shown,
reverting to the original engine and typing-booster configuration
after it is hidden. This in theory also caters for users that
do have ibus-typing-booster enabled as an input source.

The final effect is text prediction for the language that is
being typed, according to the OSK layout, given that
ibus-typing-booster and the relevant hunspell dictionaries are
used.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
fce376939f keyboard: Add 'delete' OSK key action
This action will replace CLUTTER_KEY_Backspace emission for
the OSK backspace key. Following the available mockups, implement
different modes of operation:

- Single tap deletes a single character
- Long tap starts deleting characters one by one
- Longer tap switches to word-by-word deletion

This is made possible via the input method surrounding text,
inspecting the string to look the previous char/word position
backwards, and relies on IM focus providing enough context.

Since deleting text and getting surrounding text are both
async operations, we make one happen after the other, until
the button is released.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
2920738d23 keyboard: Add/handle "modifier" action keys
These keys are handled so that the related modifier keyval (e.g. left
control) is toggled on, and flushed on the next non-modifier key
press submission.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
3b81e0f8eb keyboard: Trigger OSK level changes on button release
Keep it consistent with the rest of the actions. For long press
handling (i.e. shift turning to caps lock), this also means the
release action should be cancelled after any long press, to prevent
both from taking effect at the same time.

Prior to this commit, we used to switch level (and hide the button
being pressed) on button press, which made its long press handler
never get a paired release and end up triggering caps lock.
Performing the action on release ensures the shift key button does
not fall into this situation, making this issue moot.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
c609ffe0b5 data: Move extra keys to JSON files
Have these defined in the JSON files themselves, instead of trying
to add them from JS while minding the differences in number of levels
and rows.

This means more redundant data in the JSON files, but simplifies
OSK layout creation significantly, and allows finer control over the
appearance without quirks.

As a result, importing data from CLDR is no longer as straightforward
as running an script. After initial import, manual editions will be
required to add missing keys, assign key widths, and so on.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
5d0c49f0e0 keyboard: Add generic handling for key actions
These actions are defined in the JSON files themselves, allowing
us to migrate away from the sets of pre/post default keys, and fold
them into the JSON files for fuller control on layout and appearance.

As a first migration step, handle the relevant actions in the buttons
created from the JSON files, so we can port these.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
5b950d7db1 keyboard: Drop needless signal from OSK Key object
The 'pressed' signal will be emitted in the same circumstances now,
there's no need for a separate 'activated' signal.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
ce80e16173 keyboard: Unify OSK key commit handling in single-state signal
Instead of having callers handle pressed+released, emit string
commits on a distinct signal that is emitted all at once during
release.

This also unifies the behavior of keys that have an extended keys
popup and not wrt press/release behavior and key repeat.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
2e30fc2cbc keyboard: Separate aspect and behavior of OSK keys
Add distinct properties for label/icon, and commit string/keyval
actions. This makes keys figure less things on their own, allows
disociating aspect and behavior (e.g. label '↲' and keyval 0x13),
and makes the fallbacks clearer (keyval and label resorting to
commit string).

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
a2674c1166 keyboard: Read key width from model
Handle a "width" property to key structs in the JSON files, and
use that (so far) to give the right width to space keys in layouts.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
76933445c6 keyboard: Update OSK key model from JSON to new format
Every key is now a map able to handle more properties, so its behavior
can be extended. For now, minimally adapt to the new format so the OSK
does not break along the update.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2278>
2022-08-10 17:11:26 +00:00
Carlos Garnacho
23bfd08b3c misc: Toggle IBus OSK capability on OSK visibility
Newer versions of IBus (> 1.5.26) have the IBUS_CAP_OSK capability
which can be used to hint the active IM about an OSK driving input as
opposed to a physical keyboard. This may be used by IMs to tweak their
behavior to suit OSKs better.

Add the GNOME Shell side handling for this capability, and toggle it
on whenever the OSK is visible.

Since this is a far too new enum value and we don't want such new
IBus dependency, this change plays fast and loose with JS guarantees,
since a logical OR with an undefined value results in the other operand
unmodified it will work for older versions where the capability does not
exist and thus we want nothing extra enabled.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2415>
2022-08-10 10:14:40 +00:00
Evan Welsh
a88e59c1a8 Adopt EventEmitter class instead of injecting Signal methods
Introduce a new class, EventEmitter, which implements signal
handling for pure JavaScript classes. EventEmitter still
utilizes GJS' addSignalMethods internally.

EventEmitter allows static typechecking to understand the
structure of event-emitting JS classes and makes creating
child classes simpler.

The name 'EventEmitter' mirrors a common name for this pattern
in Node and in JS libraries.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2043>
2022-07-04 18:30:49 -04:00
Florian Müllner
eff23a87c3 keyboard: Fix updating suggestions visibility
Commit 237ba24dbe added corresponding API to the keyboard actor,
but ended up calling it on the KeyboardManager.

Add the expected method to KeyboardManager that forwards the request
to the keyboard.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5630

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2359>
2022-07-03 16:47:12 +02:00
Carlos Garnacho
237ba24dbe ibusCandidatePopup: Set OSK candidates list visibility correctly
Right now, we don't pay much attention to visibility hints from IBus
about the candidates list for the OSK suggestions bar. Since some
IMs rely on this visibility being honored, do that.

To fix this, the visibility hints for the lookup table are now
propagated to the keyboard, so the Suggestions actor hides its
internal candidate buttons. Since the Suggestions actor gets its
minimum height from CSS, this does not result in OSK size jumps.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5601
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2347>
2022-06-29 13:44:36 +00:00
Carlos Garnacho
35ff4bfe3c keyboard: Set ornament on selected item in OSK languages popup menu
There is no hint about the currently selected language, but this exists
in the mockups. Add a dot ornament on the language that is currently
selected at the time of showing the OSK languages popup.

Since this popup is created from scratch every time it is shown, and
the popup is torn down with the OSK during replacement. This also
works for further times the languages popup is shown.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5598
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2346>
2022-06-27 16:50:51 +02:00
Carlos Garnacho
9a0935305d keyboard: Set up OSK edge drag gesture on capture phase
This makes the gesture able to get the events that would otherwise
be meant for the background actor or client window.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5335
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2304>
2022-05-27 09:29:26 +00:00
Carlos Garnacho
da3d4e82e3 keyboard: Set up emoji panel pan gesture in capture phase
We need this to catch events before they reach to the emoji
keys, and the gesture can begin/end properly.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2304>
2022-05-27 09:29:26 +00:00
Jonas Dreßler
a3bfe0ab56 keyboard: Reset height before requesting preferred height
If we don't set the height property to -1 before requesting the
preferred height, get_preferred_height() will just return us the fixed
height that has been set before. We don't want this behavior here, so
set the height to -1 before calling get_preferred_height().

This fixes a resizing issue where the keyboard is sized incorrectly
after switching the monitor into portrait mode and back.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2306>
2022-05-26 18:16:03 +02:00
Jonas Dreßler
61713c7366 keyboard: Capture and bubble event in maybeHandleEvent
Right now the clutter_actor_event() call here errors out because the
second argument is missing, make it capture and bubble the event
properly to fix that.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2263>
2022-04-23 09:42:18 +00:00
Carlos Garnacho
c1d13fb471 keyboard: Prevent OSK key buttons from creating grabs
In a very un-StButton fashion, OSK keys are not interested in
altering the current focus state, as otherwise pressing those
will trigger a grab that will alter key focus, so the generated
key or IM state events will not be handled by the actor that
originally had the focus as long as the OSK key is pressed.

Despite being StButtons, OSK keys do already perform their own
press/release handling with internal state tracking, so it is
not a big stretch to simply consume the events, and update the
:active pseudo-class manually.

This makes OSK keys still look and behave as usual, but without
any grabbing shenanigans that might affect the focus state. This
makes all OSK keys work again.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4986
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2237>
2022-03-12 09:28:14 +00:00
Carlos Garnacho
c29e0cf6e6 grabHelper: Special case event funneling towards the OSK
In the case of bringing up the OSK while there is a grab (like, every
GNOME Shell entry), we used to special case event capturing so events
directed to the OSK would be let through.

When Clutter.Grab came around, events would be propagated only within
the actor hierarchy that holds the grab, which rendered this special
case just as useless as the OSK while a grab was hold. Since it wouldn't
be part of the grab hierarchy, clicking on the OSK would do nothing.

In order to let the OSK handle events, double down on the special case
and let it forward the event directly to the actor under the device,
instead of trying to let it through somehow. Since the actor under the
device are usually OSK buttons in this case, we don't need further
propagation to make it work, which makes the OSK functional again while
the shell holds a grab.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2237>
2022-03-12 09:28:14 +00:00
Florian Müllner
26235bbe54 js: Use (dis)connectObject()
Start using the new methods to simplify signal cleanup. For now,
focus on replacing existing cleanups; in most cases this means
signals connected in the constructor and disconnected on destroy,
but also other cases with a similarly defined lifetime (say: from
show to hide).

This doesn't change signal connections that only exist for a short
time (say: once), handlers that are connected on-demand (say: the
first time a particular method is called), or connections that
aren't tracked (read: disconnected) at all.

We will eventually replace the latter with connectObject() as
well - especially from actor subclasses - but the changeset is
already big enough as-is :-)

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1953>
2022-03-04 14:14:37 +00:00
Carlos Garnacho
678e34d371 keyboard: Query stage for target actor instead of event.get_source()
Events are going to stop containing the destinatary, so stop using this
API. Querying the stage is equivalent and ensured to be up-to-date.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2216>
2022-03-04 12:27:34 +00:00
Florian Müllner
2b45a01517 cleanup: Use new indentation style for object literals
We have made good progress on object literals as well, although there
are still a lot that use the old style, given how ubiquitous object
literals are.

But the needed reindentation isn't overly intrusive, as changes are
limited to the object literals themselves (i.e. they don't affect
surrounding code).

And given that object literals account for quite a bit of the remaining
differences between regular and legacy rules, doing the transition now
is still worthwhile.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2200>
2022-02-23 12:23:52 +00:00