Browse Source

Redesign the attached Rooms elements

Handle Room creation errors
pull/1466/head
Timothée Jaussoin 2 months ago
parent
commit
dd6fbabb93
  1. 128
      app/Conference.php
  2. 18
      app/Widgets/Chat/_chat_header.tpl
  3. 161
      app/Widgets/Rooms/_rooms_room.tpl
  4. 101
      app/Widgets/Rooms/rooms.css
  5. 2
      app/Widgets/Rooms/rooms.tpl
  6. 11
      app/Widgets/RoomsUtils/RoomsUtils.php
  7. 16
      public/theme/css/icon.css
  8. 34
      public/theme/css/listn.css
  9. 8
      src/Moxl/Xec/Action/Muc/CreateChannel.php
  10. 8
      src/Moxl/Xec/Action/Muc/CreateGroupChat.php

128
app/Conference.php

@ -39,12 +39,12 @@ class Conference extends Model
public function presences()
{
return $this->hasMany('App\Presence', ['jid', 'session_id'], ['conference', 'session_id'])
->where('resource', '!=', '')
->where('value', '<', 5)
->orderBy('mucrole')
->orderBy('mucaffiliation', 'desc')
->orderBy('value')
->orderBy('resource');
->where('resource', '!=', '')
->where('value', '<', 5)
->orderBy('mucrole')
->orderBy('mucaffiliation', 'desc')
->orderBy('value')
->orderBy('resource');
}
public function mujiCalls()
@ -61,82 +61,82 @@ class Conference extends Model
public function unreads()
{
return $this->hasMany('App\Message', 'jidfrom', 'conference')
->where('user_id', me()->id)
->where('type', 'groupchat')
->whereNull('subject')
->where('seen', false);
->where('user_id', me()->id)
->where('type', 'groupchat')
->whereNull('subject')
->where('seen', false);
}
public function quoted()
{
return $this->hasMany('App\Message', 'jidfrom', 'conference')
->where('user_id', me()->id)
->where('type', 'groupchat')
->whereNull('subject')
->where('quoted', true)
->where('seen', false);
->where('user_id', me()->id)
->where('type', 'groupchat')
->whereNull('subject')
->where('quoted', true)
->where('seen', false);
}
public function presence()
{
return $this->hasOne('App\Presence', ['jid', 'session_id'], ['conference', 'session_id'])
->where('value', '<', 5)
->where('mucjid', me()->id);
->where('value', '<', 5)
->where('mucjid', me()->id);
}
public function members()
{
return $this->hasMany('App\Member', 'conference', 'conference')
->orderBy('role')
->orderBy('affiliation', 'desc');
->orderBy('role')
->orderBy('affiliation', 'desc');
}
public function activeMembers()
{
return $this->hasMany('App\Member', 'conference', 'conference')
->where('affiliation', '!=', 'outcast')
->where('affiliation', '!=', 'none')
->orderBy('role')
->orderBy('affiliation', 'desc');
->where('affiliation', '!=', 'outcast')
->where('affiliation', '!=', 'none')
->orderBy('role')
->orderBy('affiliation', 'desc');
}
public function pictures()
{
return $this->hasMany('App\Message', 'jidfrom', 'conference')
->where('user_id', me()->id)
->where('type', 'groupchat')
->where('picture', true)
->where('retracted', false)
->orderBy('published', 'desc');
->where('user_id', me()->id)
->where('type', 'groupchat')
->where('picture', true)
->where('retracted', false)
->orderBy('published', 'desc');
}
public function links()
{
return $this->hasMany('App\Message', 'jidfrom', 'conference')
->where('user_id', me()->id)
->where('type', 'groupchat')
->whereNotNull('urlid')
->where('picture', false)
->where('retracted', false)
->orderBy('published', 'desc');
->where('user_id', me()->id)
->where('type', 'groupchat')
->whereNotNull('urlid')
->where('picture', false)
->where('retracted', false)
->orderBy('published', 'desc');
}
public function info()
{
return $this->hasOne('App\Info', 'server', 'conference')
->where(function ($query) {
$query->where('node', function ($query) {
$query->select('node')
->from('presences')
->where('session_id', me()->session->id)
->whereColumn('jid', 'infos.server')
->where('resource', '')
->take(1);
})
->orWhere('node', '');
})
->whereCategory('conference')
->whereType('text');
->where(function ($query) {
$query->where('node', function ($query) {
$query->select('node')
->from('presences')
->where('session_id', me()->session->id)
->whereColumn('jid', 'infos.server')
->where('resource', '')
->take(1);
})
->orWhere('node', '');
})
->whereCategory('conference')
->whereType('text');
}
public function contact()
@ -153,16 +153,18 @@ class Conference extends Model
$this->bookmarkversion = (int)substr((string)$item->conference->attributes()->xmlns, -1, 1);
if ($item->conference->extensions) {
if ($item->conference->extensions && $item->conference->extensions->notifications
&& $item->conference->extensions->notifications->attributes()->xmlns == self::$xmlnsNotifications) {
$this->notify = (int)array_flip(self::$notifications)[
(string)$item->conference->extensions->notifications->attributes()->notify
];
if (
$item->conference->extensions && $item->conference->extensions->notifications
&& $item->conference->extensions->notifications->attributes()->xmlns == self::$xmlnsNotifications
) {
$this->notify = (int)array_flip(self::$notifications)[(string)$item->conference->extensions->notifications->attributes()->notify];
unset($item->conference->extensions->notifications);
}
if ($item->conference->extensions && $item->conference->extensions->pinned
&& in_array($item->conference->extensions->pinned->attributes()->xmlns, [self::$xmlnsPinned, 'xmpp:movim.eu/pinned:0'])) {
if (
$item->conference->extensions && $item->conference->extensions->pinned
&& in_array($item->conference->extensions->pinned->attributes()->xmlns, [self::$xmlnsPinned, 'xmpp:movim.eu/pinned:0'])
) {
$this->pinned = true;
unset($item->conference->extensions->pinned);
}
@ -212,13 +214,13 @@ class Conference extends Model
public function getSubjectAttribute()
{
$subject = me()
->messages()
->jid($this->conference)
->whereNotNull('subject')
->whereNull('body')
->where('type', 'groupchat')
->orderBy('published', 'desc')
->first();
->messages()
->jid($this->conference)
->whereNotNull('subject')
->whereNull('body')
->where('type', 'groupchat')
->orderBy('published', 'desc')
->first();
return $subject ? $subject->subject : null;
}
@ -243,7 +245,9 @@ class Conference extends Model
public function currentMuji(): ?MujiCall
{
return $this->mujiCalls->filter(
function ($muji) { return $muji->joined; }
function ($muji) {
return $muji->joined;
}
)->first();
}

18
app/Widgets/Chat/_chat_header.tpl

@ -31,16 +31,14 @@
{/if}
</span>
{if="$conference && $conference->isGroupChat()"}
{if="$conference && $conference->info && $conference->info->related"}
{$related = $conference->info->related}
<span
title="{$c->__('page.communities')}{$related->name}"
onclick="MovimUtils.reload('{$c->route('community', [$related->server, $related->node])}')"
class="control icon bubble active small">
<img src="{$related->getPicture(\Movim\ImageSize::M)}"/>
</span>
{/if}
{if="$conference && $conference->info && $conference->info->related"}
{$related = $conference->info->related}
<span
title="{$c->__('page.communities')}{$related->name}"
onclick="MovimUtils.reload('{$c->route('community', [$related->server, $related->node])}')"
class="control icon bubble active small">
<img src="{$related->getPicture(\Movim\ImageSize::M)}"/>
</span>
{/if}
{if="$c->database('pgsql')"}

161
app/Widgets/Rooms/_rooms_room.tpl

@ -8,87 +8,102 @@
{if="$conference->unreads_count > 0 || $conference->quoted_count > 0"}unread{/if}
{if="$conference->mujiCalls->isNotEmpty()"}muc_call{/if}
">
<span class="primary icon bubble small"
id="{$conference->conference|cleanupId}-rooms-primary"
style="background-image: url({$conference->getPicture()});">
{autoescape="off"}
{$c->prepareRoomCounter($conference, true)}
{/autoescape}
</span>
<ul class="list thin">
<li>
<span class="primary icon bubble small"
id="{$conference->conference|cleanupId}-rooms-primary"
style="background-image: url({$conference->getPicture()});">
{autoescape="off"}
{$c->prepareRoomCounter($conference, true)}
{/autoescape}
</span>
{$info = $conference->info}
{$info = $conference->info}
<div>
<p class="normal line">
{if="$conference->pinned"}
<span class="info">
<i class="material-symbols fill" title="{$c->__('room.pinned')}">push_pin</i>
</span>
{/if}
{if="!$conference->isGroupChat() && $conference->connected"}
{$count = $conference->presences()->count()}
<span title="{$c->__('communitydata.sub', $count)}"
class="info
{if="$conference->connected && $conference->presence->mucrole == 'moderator'"}
moderator
<div>
<p class="normal line">
{if="$conference->pinned"}
<span class="info">
<i class="material-symbols fill" title="{$c->__('room.pinned')}">push_pin</i>
</span>
{/if}
{if="!$conference->isGroupChat() && $conference->connected"}
{$count = $conference->presences()->count()}
<span title="{$c->__('communitydata.sub', $count)}"
class="info
{if="$conference->connected && $conference->presence->mucrole == 'moderator'"}
moderator
{/if}
">
{$count} <i class="material-symbols">people</i>
</span>
{elseif="!$conference->isGroupChat() && isset($info) && $info->occupants > 0"}
<span title="{$c->__('communitydata.sub', $info->occupants)}"
class="info
{if="$conference->connected && $conference->presence->mucrole == 'moderator'"}
moderator
{/if}
">
{$info->occupants} <i class="material-symbols">people</i>
</span>
{/if}
<span title="{$conference->conference}">{$conference->title}</span>
<span class="second">
{if="$conference->notify == 0"}
<i class="material-symbols" title="{$c->__('room.notify_never')}">notifications_off</i>
{elseif="$conference->notify == 2"}
<i class="material-symbols" title="{$c->__('room.notify_always')}">notifications_active</i>
{/if}
">
{$count} <i class="material-symbols">people</i>
</span>
{elseif="!$conference->isGroupChat() && isset($info) && $info->occupants > 0"}
<span title="{$c->__('communitydata.sub', $info->occupants)}"
class="info
{if="$conference->connected && $conference->presence->mucrole == 'moderator'"}
moderator
{if="isset($info) && $info->name && $conference->title != $info->name"}
{$info->name}
{elseif="isset($info) && $info->description"}
{$info->description}
{else}
{$conference->conference}
{/if}
">
{$info->occupants} <i class="material-symbols">people</i>
</span>
{/if}
<span title="{$conference->conference}">{$conference->title}</span>
<span class="second">
{if="$conference->notify == 0"}
<i class="material-symbols" title="{$c->__('room.notify_never')}">notifications_off</i>
{elseif="$conference->notify == 2"}
<i class="material-symbols" title="{$c->__('room.notify_always')}">notifications_active</i>
{/if}
{if="isset($info) && $info->name && $conference->title != $info->name"}
{$info->name}
{elseif="isset($info) && $info->description"}
{$info->description}
{else}
{$conference->conference}
{/if}
</span>
</p>
</div>
<span class="control icon active gray" onclick="event.stopPropagation(); RoomsUtils_ajaxRemove('{$conference->conference|echapJS}');">
<i class="material-symbols">delete</i>
</span>
</p>
<span class="control icon active gray" onclick="event.stopPropagation(); RoomsUtils_ajaxAdd('{$conference->conference|echapJS}');">
<i class="material-symbols">edit</i>
</span>
</li>
{loop="$conference->mujiCalls"}
<p class="line" data-mujiid="{$value->id}">
<i class="material-symbols icon {if="$value->joined"}green blink{else}blue{/if}">
{$value->icon}
</i>
<span class="info">
{if="$value->joined"}
{$value->presences->count()}
{else}
{$value->participants->count()}
{/if}
<i class="material-symbols">people</i>
<li>
<span class="primary icon r2 small">
<i class="material-symbols icon gray">
line_curve
</i>
</span>
{if="$value->joined"}{$c->__('visio.joined_call')}{else}{$c->__('visio.in_call')}{/if}
<span class="second">
{$value->created_at|prepareDate:true,true}
{$c->__('visio.by', $value->inviter->name)}
<span class="primary icon small">
<i class="material-symbols icon {if="$value->joined"}green blink{else}blue{/if}">
{$value->icon}
</i>
</span>
</p>
<div>
<p class="line normal" data-mujiid="{$value->id}">
<span class="info">
{if="$value->joined"}
{$value->presences->count()}
{else}
{$value->participants->count()}
{/if}
<i class="material-symbols">people</i>
</span>
{if="$value->joined"}{$c->__('visio.joined_call')}{else}{$c->__('visio.in_call')}{/if}
<span class="second">
{$value->created_at|prepareDate:true,true}
{$c->__('visio.by', $value->inviter->name)}
</span>
</p>
</div>
</li>
{/loop}
</div>
<span class="control icon active gray" onclick="event.stopPropagation(); RoomsUtils_ajaxRemove('{$conference->conference|echapJS}');">
<i class="material-symbols">delete</i>
</span>
<span class="control icon active gray" onclick="event.stopPropagation(); RoomsUtils_ajaxAdd('{$conference->conference|echapJS}');">
<i class="material-symbols">edit</i>
</span>
</ul>
</li>

101
app/Widgets/Rooms/rooms.css

@ -8,11 +8,48 @@
animation: backgroundblink 1s;
}
#rooms > ul.list {
#rooms>ul.list>li:has(ul) {
padding-left: 0;
padding-right: 0;
}
#rooms>ul.list>li>ul:has(li:nth-child(2)) {
padding-bottom: 1rem;
}
#rooms>ul.list>li>ul>li:not(:first-child)>span.icon,
#rooms>ul.list>li>ul>li:not(:first-child)>div>p {
line-height: 3rem;
height: 3rem;
}
#rooms>ul.list>li>ul>li:not(:first-child)>div>p {
font-size: 1.9rem;
}
#rooms>ul.list>li>ul>li:not(:first-child)>span.icon>i {
font-size: 2.5rem;
line-height: 3rem;
}
#rooms>ul.list>li>ul>li:not(:first-child)>span.primary:first-child>i {
line-height: 2rem;
}
#rooms>ul.list>li>ul>li:not(:first-child)>span.primary:first-child {
margin-right: 0;
margin-left: 2rem;
}
#rooms>ul.list>li>ul>li:not(:first-child)>span.primary:not(:first-child) {
margin: 0;
}
#rooms>ul.list {
order: 1;
}
#rooms > ul.list.head {
#rooms>ul.list.head {
order: 0;
}
@ -24,7 +61,7 @@
min-height: 0;
}
#gateway_rooms:not(:empty) + div {
#gateway_rooms:not(:empty)+div {
display: none;
}
@ -37,74 +74,58 @@
flex-direction: column;
}
#rooms ul.list.rooms li:not(.connected) span.primary {
#rooms ul.list.rooms>li:not(.connected) span.primary {
opacity: 0.25;
filter: grayscale(0.5);
}
#rooms ul.toggle_show li div > p:first-child {
#rooms ul.toggle_show li div>p:first-child {
line-height: initial;
}
#rooms ul li.muc_call p:first-child {
padding-top: 0.75rem;
}
#rooms ul li.muc_call p:nth-child(2) {
padding: 1rem 0;
}
#rooms ul.list.rooms:empty ~ ul.toggle_show,
#rooms ul.list.rooms:not(:empty):not(.all) ~ ul.toggle_show li span.primary i:first-child,
#rooms ul.list.rooms:not(:empty).all ~ ul.toggle_show li span.primary i:last-child,
#rooms ul.list.rooms:not(:empty):not(.all) ~ ul.list.head li span.control.toggle_show i:first-child,
#rooms ul.list.rooms:not(:empty).all ~ ul.list.head li span.control.toggle_show i:last-child,
#rooms ul.list.rooms:not(:empty):not(.all) ~ ul.toggle_show li div > p:first-child,
#rooms ul.list.rooms:not(:empty).all ~ ul.toggle_show li div > p:last-child,
#rooms ul.list.rooms:not(:empty) ~ ul.empty,
#rooms ul.list.rooms:not(.different_states) ~ ul.toggle_show,
#rooms ul.list.rooms:not(.all) li:not(.connected) {
#rooms ul.list.rooms:empty~ul.toggle_show,
#rooms ul.list.rooms:not(:empty):not(.all)~ul.toggle_show>li span.primary i:first-child,
#rooms ul.list.rooms:not(:empty).all~ul.toggle_show>li span.primary i:last-child,
#rooms ul.list.rooms:not(:empty):not(.all)~ul.list.head>li span.control.toggle_show i:first-child,
#rooms ul.list.rooms:not(:empty).all~ul.list.head>li span.control.toggle_show i:last-child,
#rooms ul.list.rooms:not(:empty):not(.all)~ul.toggle_show>li div>p:first-child,
#rooms ul.list.rooms:not(:empty).all~ul.toggle_show>li div>p:last-child,
#rooms ul.list.rooms:not(:empty)~ul.empty,
#rooms ul.list.rooms:not(.different_states)~ul.toggle_show,
#rooms ul.list.rooms:not(.all)>li:not(.connected) {
display: none;
}
#rooms > ul.list.rooms > li div p[data-mujiid] {
padding: 0.5rem 0;
}
#rooms > ul.list.rooms > li div p[data-mujiid]:last-child {
padding-bottom: 1rem;
}
/* Rooms order */
#rooms > ul.list.rooms > li {
#rooms>ul.list.rooms>li {
order: 4;
}
#rooms > ul.list.rooms > li.groupchat {
#rooms>ul.list.rooms>li.groupchat {
order: 3;
}
#rooms > ul.list.rooms > li.pinned {
#rooms>ul.list.rooms>li.pinned {
order: 2;
}
#rooms > ul.list.rooms > li.unread {
#rooms>ul.list.rooms>li.unread {
order: 1;
}
#rooms > ul.list.rooms > li.connected {
#rooms>ul.list.rooms>li.connected {
order: 0;
}
#rooms > ul.list.rooms > li.connected.groupchat {
#rooms>ul.list.rooms>li.connected.groupchat {
order: -1;
}
#rooms > ul.list.rooms > li.connected.pinned {
#rooms>ul.list.rooms>li.connected.pinned {
order: -2;
}
#rooms > ul.list.rooms > li.connected.unread {
#rooms>ul.list.rooms>li.connected.unread {
order: -3;
}
}

2
app/Widgets/Rooms/rooms.tpl

@ -1,5 +1,5 @@
<div id="rooms">
<ul class="list rooms divided spaced thin active spin"></ul>
<ul class="list rooms divided thin spaced active spin"></ul>
<ul class="list head thin">
<li class="subheader" title="{$c->__('page.configuration')}">

11
app/Widgets/RoomsUtils/RoomsUtils.php

@ -48,6 +48,8 @@ class RoomsUtils extends Base
$this->registerEvent('disco_items_errorregistrationrequired', 'onDiscoRegistrationRequired');
$this->registerEvent('muc_creategroupchat_handle', 'onChatroomCreated');
$this->registerEvent('muc_createchannel_handle', 'onChatroomCreated');
$this->registerEvent('muc_creategroupchat_error', 'onChatroomCreatedError');
$this->registerEvent('muc_createchannel_error', 'onChatroomCreatedError');
$this->registerEvent('muc_changeaffiliation_handle', 'onAffiliationChanged');
$this->registerEvent('muc_changeaffiliation_errornotallowed', 'onAffiliationChangeUnauthorized');
$this->registerEvent('message_invite_error', 'onInviteError');
@ -333,6 +335,14 @@ class RoomsUtils extends Base
$this->rpc('Dialog_ajaxClear');
}
/**
* @brief If a chatroom creation is failing
*/
public function onChatroomCreatedError($packet)
{
Toast::send($packet->content);
}
/**
* @brief Get the subject form of a chatroom
*/
@ -455,6 +465,7 @@ class RoomsUtils extends Base
} else {
$m = new Muc;
$m->enableCreate()
->noNotify()
->setTo(strtolower($form->jid->value))
->setNickname($form->nick->value ?? $this->user->username)
->request();

16
public/theme/css/icon.css

@ -21,7 +21,19 @@ span.icon img {
object-fit: cover;
}
span.icon.top {
span.icon.r1 {
transform: rotate(90deg);
}
span.icon.r2 {
transform: rotate(180deg);
}
span.icon.r3 {
transform: rotate(270deg);
}
/*span.icon.top {
position: relative;
top: -1.5rem;
width: 100%;
@ -34,7 +46,7 @@ span.icon.top {
span.icon.top.preview {
height: 25rem;
}
}*/
span.icon.primary.thumb,
span.icon.control.thumb {

34
public/theme/css/listn.css

@ -50,52 +50,52 @@ ul.list.thick li:not(.subheader)>div {
/* Active list items */
ul.list.active li:hover:not(.subheader),
ul.list.active.all li:hover,
ul.list li.active:hover,
ul.list.active li.active:not(.subheader),
ul.list.active>li:hover:not(.subheader),
ul.list.active.all>li:hover,
ul.list>li.active:hover,
ul.list.active>li.active:not(.subheader),
ul.tabs>li:hover {
cursor: pointer;
user-select: none;
}
ul.list.active li:hover:not(.subheader):not(.color),
ul.list.active.all li:hover:not(.color),
ul.list li.active:hover:not(.color),
ul.list.active li.active:not(.subheader):not(.color) {
ul.list.active>li:hover:not(.subheader):not(.color),
ul.list.active.all>li:hover:not(.color),
ul.list>li.active:hover:not(.color),
ul.list.active>li.active:not(.subheader):not(.color) {
background-color: rgba(var(--movim-element-action), 0.1);
}
ul.list li>span.active:hover {
ul.list>li>span.active:hover {
cursor: pointer;
user-select: none;
}
ul.list li>span.bubble.active:hover {
ul.list>li>span.bubble.active:hover {
filter: grayscale(25%);
}
ul.list li>span:not(.bubble).active:hover,
ul.list li>div.bubble:not(.file):hover {
ul.list>li>span:not(.bubble).active:hover,
ul.list>li>div.bubble:not(.file):hover {
cursor: pointer;
background-color: rgba(var(--movim-element-action), 0.1);
}
/* Clicked list items */
ul.list.active li:active:not(.subheader),
ul.list.active.all li:active,
ul.list.active>li:active:not(.subheader),
ul.list.active.all>li:active,
ul.tabs>li:active {
background-color: rgba(var(--movim-element-action), 0.2);
}
ul.list li>span.bubble.active:active {
ul.list>li>span.bubble.active:active {
filter: grayscale(50%);
}
ul.list li>span:not(.bubble).active:active,
ul.list li>div.bubble:not(.file):active {
ul.list>li>span:not(.bubble).active:active,
ul.list>li>div.bubble:not(.file):active {
cursor: pointer;
background-color: rgba(var(--movim-element-action), 0.2);
font-variation-settings: 'FILL' 1;

8
src/Moxl/Xec/Action/Muc/CreateChannel.php

@ -32,4 +32,12 @@ class CreateChannel extends Action
]);
$this->deliver();
}
public function error(string $errorId, ?string $message = null)
{
if ($message) {
$this->pack($message);
$this->deliver();
}
}
}

8
src/Moxl/Xec/Action/Muc/CreateGroupChat.php

@ -32,4 +32,12 @@ class CreateGroupChat extends Action
]);
$this->deliver();
}
public function error(string $errorId, ?string $message = null)
{
if ($message) {
$this->pack($message);
$this->deliver();
}
}
}
Loading…
Cancel
Save