Support circles with multiple group chats, remove default group chat

This commit is contained in:
Matthew Wild
2023-11-06 13:05:58 +00:00
parent 7ce13b55ac
commit db363367da
7 changed files with 273 additions and 69 deletions

View File

@@ -458,6 +458,8 @@ class EditCircleForm(BaseForm):
_l("Add user")
)
action_remove_group_chat = wtforms.StringField()
@bp.route("/circle/<id_>", methods=["GET", "POST"])
@client.require_admin_session()
@@ -530,6 +532,15 @@ async def edit_circle(id_: str) -> typing.Union[str, werkzeug.Response]:
_("User removed from circle"),
"success",
)
elif form.action_remove_group_chat.data:
await client.remove_group_chat(
id_,
form.action_remove_group_chat.data,
)
await flash(
_("Chat removed from circle"),
"success",
)
return redirect(url_for(".edit_circle", id_=id_))
@@ -537,6 +548,7 @@ async def edit_circle(id_: str) -> typing.Union[str, werkzeug.Response]:
"admin_edit_circle.html",
target_circle=circle,
form=form,
circle_chats=circle.chats,
circle_members=circle_members,
invite_form=invite_form,
)
@@ -583,6 +595,56 @@ async def delete_circle(id_: str) -> typing.Union[str, werkzeug.Response]:
)
class AddCircleChatForm(BaseForm):
name = wtforms.StringField(
_l("Group chat name"),
validators=[wtforms.validators.InputRequired()],
)
action_save = wtforms.SubmitField(
_l("Create group chat")
)
@bp.route("/circle/<id_>/add_chat", methods=["GET", "POST"])
@client.require_admin_session()
async def edit_circle_add_chat(
id_: str
) -> typing.Union[str, werkzeug.Response]:
async with client.authenticated_session() as session:
try:
circle = await client.get_group_by_id(
id_,
session=session,
)
except aiohttp.ClientResponseError as exc:
if exc.status == 404:
await flash(
_("No such circle exists"),
"alert",
)
return redirect(url_for(".circles"))
raise
form = AddCircleChatForm()
if form.validate_on_submit():
if form.action_save.data:
await client.add_group_chat(id_, form.name.data)
await flash(
_("New group chat added to circle"),
"success",
)
return redirect(url_for(".edit_circle", id_=id_))
return await render_template(
"admin_create_circle_chat.html",
target_circle=circle,
group_chat_form=form,
)
_CPU_EPOCH = time.process_time()
_MONOTONIC_EPOCH = time.monotonic()

View File

@@ -117,12 +117,30 @@ class AdminInviteInfo:
)
@dataclasses.dataclass(frozen=True)
class AdminGroupChatInfo:
id_: str
jid: str
name: str
@classmethod
def from_api_response(
cls,
data: typing.Mapping[str, typing.Any],
) -> "AdminGroupChatInfo":
return cls(
id_=data["id"],
jid=data["jid"],
name=data["name"],
)
@dataclasses.dataclass(frozen=True)
class AdminGroupInfo:
id_: str
name: str
muc_jid: typing.Optional[str]
members: typing.Collection[str]
chats: typing.Collection[AdminGroupChatInfo]
@classmethod
def from_api_response(
@@ -132,8 +150,11 @@ class AdminGroupInfo:
return cls(
id_=data["id"],
name=data["name"],
muc_jid=data.get("muc_jid") or None,
members=data.get("members", []),
chats=[
AdminGroupChatInfo.from_api_response(x)
for x in data.get("chats", [])
]
)
@@ -1032,7 +1053,7 @@ class ProsodyClient:
self,
name: str,
*,
create_muc: bool = True,
create_muc: bool = False,
session: aiohttp.ClientSession,
) -> AdminGroupInfo:
payload = {
@@ -1107,6 +1128,27 @@ class ProsodyClient:
) as resp:
self._raise_error_from_response(resp)
@autosession
async def add_group_chat(
self,
id_: str,
name: str,
*,
session: aiohttp.ClientSession,
) -> None:
payload: typing.Dict[str, typing.Any] = {
"name": name,
}
async with session.post(
self._admin_v1_endpoint(
"/groups/{}/chats".format(id_)
),
json=payload,
) as resp:
self._raise_error_from_response(resp)
@autosession
async def remove_group_member(
self,
@@ -1122,6 +1164,21 @@ class ProsodyClient:
) as resp:
self._raise_error_from_response(resp)
@autosession
async def remove_group_chat(
self,
group_id: str,
chat_id: str,
*,
session: aiohttp.ClientSession,
) -> None:
async with session.delete(
self._admin_v1_endpoint(
"/groups/{}/chats/{}".format(group_id, chat_id)
),
) as resp:
self._raise_error_from_response(resp)
@autosession
async def delete_group(
self,

View File

@@ -3,7 +3,7 @@
{% block content %}
<h1>{% trans %}Manage circles{% endtrans %}</h1>
<p>{% trans %}<em>Circles</em> aim to help people who are in the same social circle find each other on your service.{% endtrans %}</p>
<p>{% trans %}Users who are in the same circle will see each other in their contact list. In addition, each circle has a group chat where the circle members are included.{% endtrans %}</p>
<p>{% trans %}Users who are in the same circle will see each other in their contact list. In addition, each circle may have group chats where the circle members are included.{% endtrans %}</p>
{%- if circles -%}
<form method="POST" action="{{ url_for(".create_invite") }}">
{{- invite_form.csrf_token -}}

View File

@@ -0,0 +1,5 @@
{% extends "admin_app.html" %}
{% block content %}
<h1>{{ target_circle.name }}</h1>
{%- include "admin_create_circle_group_chat_form.html" -%}
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% from "library.j2" import form_button, render_errors %}
<form method="POST" action="{{ url_for(".edit_circle_add_chat", id_=target_circle.id_) }}">
{{- group_chat_form.csrf_token -}}
<div class="form layout-expanded">
<h2 class="form-title">{% trans %}Create new circle group chat{% endtrans %}</h2>
<p class="form-descr weak">{% trans %}Add a chat to your circle so its members can hold group discussions.{% endtrans %}</p>
<p class="form-descr weak"><strong>{% trans %}Tip:{% endtrans %}</strong> {% trans %}This is only for creating group chats that automatically include <em>all</em> members of the circle. If you want a normal group chat, create it in the Snikket app instead.{% endtrans %}</p>
<div class="f-ebox">
{{ group_chat_form.name.label }}
{{ group_chat_form.name }}
</div>
<div class="f-bbox">
{%- call form_button("add", group_chat_form.action_save, class="primary") %}{% endcall -%}
</div>
</div></form>

View File

@@ -13,13 +13,6 @@
<div class="box hint form layout-expanded">
<header>{% trans %}This is your main circle{% endtrans %}</header>
<p>{% trans %}This circle is managed automatically and cannot be removed or renamed.{% endtrans %}</p>
{%- if target_circle.muc_jid -%}
<div><label for="circle-muc-jid">{% trans %}Group chat address{% endtrans %}</label></div>
<div><input type="text" readonly="readonly" id="circle-muc-jid" value="{{ target_circle.muc_jid }}"></div>
{%- call clipboard_button(target_circle.muc_jid, show_label=True) -%}
{%- trans -%}Copy address{%- endtrans -%}
{%- endcall -%}
{%- endif -%}
</div>
{%- else -%}
<div class="form layout-expanded">
@@ -28,17 +21,6 @@
{{ form.name.label }}
{{ form.name }}
</div>
<div class="f-ebox">
{%- if target_circle.muc_jid -%}
<label for="circle-muc-jid">{% trans %}Group chat address{% endtrans %}</label>
<input type="text" readonly="readonly" id="circle-muc-jid" value="{{ target_circle.muc_jid }}">
{%- call clipboard_button(target_circle.muc_jid, show_label=True) -%}
{%- trans -%}Copy address{%- endtrans -%}
{%- endcall -%}
{%- else -%}
<p>{% trans %}This circle has no group chat associated.{% endtrans %}<p>
{%- endif -%}
</div>
<div class="f-bbox">
{%- call standard_button("back", url_for(".circles"), class="tertiary") -%}
{% trans %}Return to circle list{% endtrans %}
@@ -52,7 +34,39 @@
</div>
</div>
{%- endif -%}
<h2 id="chats">{% trans %}Group chats{% endtrans %}</h2>
<p>{% trans %}These group chats will be available to all members of the circle.{% endtrans %}</p>
{%- if circle_chats -%}
<div class="el-2 elevated"><table>
<thead>
<th>{% trans %}Name{% endtrans %}</th>
<th>{% trans %}Actions{% endtrans %}</th>
</thead>
<tbody>
{%- for chat in circle_chats -%}
<tr>
<td class="collapsible">{% call value_or_hint(chat.name) %}{% endcall %}</td>
<td class="nowrap">
{%- call custom_form_button("delete", form.action_remove_group_chat.name, chat.id_, class="primary danger", slim=True) -%}
{% trans name=chat.name %}Delete group chat '{{ name }}'{% endtrans %}
{%- endcall -%}
</td>
</tr>
{%- endfor -%}
</tbody>
</table></div>
{%- else -%}
<p>{% trans %}This circle currently has no group chats.{% endtrans %}</p>
{%- endif -%}
{%- call standard_button("add", url_for(".edit_circle_add_chat", id_=target_circle.id_), class="secondary") -%}
{% trans %}Add group chat{% endtrans %}
{%- endcall -%}
<h2 id="members">{% trans %}Circle members{% endtrans %}</h2>
<p>{% trans %}All members of the circle will see each other in their contact list.{% endtrans %}</p>
{%- if circle_members -%}
<div class="el-2 elevated"><table>
<thead>

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-10-25 16:15+0100\n"
"POT-Creation-Date: 2023-11-06 13:46+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,13 +18,13 @@ msgstr ""
"Generated-By: Babel 2.13.1\n"
#: snikket_web/admin.py:69 snikket_web/templates/admin_delete_user.html:10
#: snikket_web/templates/admin_edit_circle.html:59
#: snikket_web/templates/admin_edit_circle.html:73
#: snikket_web/templates/admin_users.html:8
msgid "Login name"
msgstr ""
#: snikket_web/admin.py:73 snikket_web/templates/admin_delete_user.html:12
#: snikket_web/templates/admin_edit_circle.html:60
#: snikket_web/templates/admin_edit_circle.html:74
#: snikket_web/templates/admin_users.html:9 snikket_web/user.py:63
msgid "Display name"
msgstr ""
@@ -143,6 +143,7 @@ msgstr ""
#: snikket_web/admin.py:394 snikket_web/admin.py:442
#: snikket_web/templates/admin_delete_circle.html:10
#: snikket_web/templates/admin_edit_circle.html:44
msgid "Name"
msgstr ""
@@ -166,47 +167,63 @@ msgstr ""
msgid "Add user"
msgstr ""
#: snikket_web/admin.py:474 snikket_web/admin.py:563
#: snikket_web/admin.py:476 snikket_web/admin.py:575 snikket_web/admin.py:623
msgid "No such circle exists"
msgstr ""
#: snikket_web/admin.py:511
#: snikket_web/admin.py:513
msgid "Circle data updated"
msgstr ""
#: snikket_web/admin.py:521
#: snikket_web/admin.py:523
msgid "User added to circle"
msgstr ""
#: snikket_web/admin.py:530
#: snikket_web/admin.py:532
msgid "User removed from circle"
msgstr ""
#: snikket_web/admin.py:547
#: snikket_web/admin.py:541
msgid "Chat removed from circle"
msgstr ""
#: snikket_web/admin.py:559
msgid "Delete circle permanently"
msgstr ""
#: snikket_web/admin.py:574
#: snikket_web/admin.py:586
msgid "Circle deleted"
msgstr ""
#: snikket_web/admin.py:640
#: snikket_web/admin.py:600
msgid "Group chat name"
msgstr ""
#: snikket_web/admin.py:605
msgid "Create group chat"
msgstr ""
#: snikket_web/admin.py:635
msgid "New group chat added to circle"
msgstr ""
#: snikket_web/admin.py:702
msgid "Message contents"
msgstr ""
#: snikket_web/admin.py:646
#: snikket_web/admin.py:708
msgid "Only send to online users"
msgstr ""
#: snikket_web/admin.py:650
#: snikket_web/admin.py:712
msgid "Post to all users"
msgstr ""
#: snikket_web/admin.py:654
#: snikket_web/admin.py:716
msgid "Send preview to yourself"
msgstr ""
#: snikket_web/admin.py:676
#: snikket_web/admin.py:738
msgid "Announcement sent!"
msgstr ""
@@ -474,8 +491,8 @@ msgstr ""
#: snikket_web/templates/admin_circles.html:6
msgid ""
"Users who are in the same circle will see each other in their contact "
"list. In addition, each circle has a group chat where the circle members "
"are included."
"list. In addition, each circle may have group chats where the circle "
"members are included."
msgstr ""
#: snikket_web/templates/admin_circles.html:13
@@ -487,7 +504,8 @@ msgid "Members"
msgstr ""
#: snikket_web/templates/admin_circles.html:15
#: snikket_web/templates/admin_edit_circle.html:61
#: snikket_web/templates/admin_edit_circle.html:45
#: snikket_web/templates/admin_edit_circle.html:75
#: snikket_web/templates/admin_invites.html:24
#: snikket_web/templates/admin_users.html:10
msgid "Actions"
@@ -523,6 +541,25 @@ msgstr ""
msgid "New circle"
msgstr ""
#: snikket_web/templates/admin_create_circle_group_chat_form.html:5
msgid "Create new circle group chat"
msgstr ""
#: snikket_web/templates/admin_create_circle_group_chat_form.html:6
msgid "Add a chat to your circle so its members can hold group discussions."
msgstr ""
#: snikket_web/templates/admin_create_circle_group_chat_form.html:7
msgid "Tip:"
msgstr ""
#: snikket_web/templates/admin_create_circle_group_chat_form.html:7
msgid ""
"This is only for creating group chats that automatically include "
"<em>all</em> members of the circle. If you want a normal group chat, "
"create it in the Snikket app instead."
msgstr ""
#: snikket_web/templates/admin_create_invite.html:3
msgid "Create invitation"
msgstr ""
@@ -565,8 +602,8 @@ msgid "Delete circle %(circle_name)s"
msgstr ""
#: snikket_web/templates/admin_delete_circle.html:6
#: snikket_web/templates/admin_edit_circle.html:48
#: snikket_web/templates/admin_edit_circle.html:51
#: snikket_web/templates/admin_edit_circle.html:30
#: snikket_web/templates/admin_edit_circle.html:33
msgid "Delete circle"
msgstr ""
@@ -625,69 +662,78 @@ msgstr ""
msgid "This circle is managed automatically and cannot be removed or renamed."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:17
#: snikket_web/templates/admin_edit_circle.html:33
msgid "Group chat address"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:20
#: snikket_web/templates/admin_edit_circle.html:36
#: snikket_web/templates/invite_success.html:15
#: snikket_web/templates/user_home.html:21
msgid "Copy address"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:26
#: snikket_web/templates/admin_edit_circle.html:19
msgid "Circle information"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:39
msgid "This circle has no group chat associated."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:44
#: snikket_web/templates/admin_edit_circle.html:26
msgid "Return to circle list"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:49
#: snikket_web/templates/admin_edit_circle.html:31
msgid "Deleting a circle does not delete any users in the circle."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:55
#: snikket_web/templates/admin_edit_circle.html:38
msgid "Group chats"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:39
msgid "These group chats will be available to all members of the circle."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:53
#, python-format
msgid "Delete group chat '%(name)s'"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:61
msgid "This circle currently has no group chats."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:64
msgid "Add group chat"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:67
msgid "Circle members"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:71
#: snikket_web/templates/admin_edit_circle.html:68
msgid "All members of the circle will see each other in their contact list."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:85
msgid "The user has been deleted from the server."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:71
#: snikket_web/templates/admin_edit_circle.html:85
#: snikket_web/templates/library.j2:108
msgid "deleted"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:77
#: snikket_web/templates/admin_edit_circle.html:91
#, python-format
msgid "Remove user %(username)s from circle"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:85
#: snikket_web/templates/admin_edit_circle.html:99
msgid "This circle currently has no members."
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:87
#: snikket_web/templates/admin_edit_circle.html:101
msgid "Invite more members"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:90
#: snikket_web/templates/admin_edit_circle.html:104
msgid "Add existing user"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:101
#: snikket_web/templates/admin_edit_circle.html:115
msgid "All users added"
msgstr ""
#: snikket_web/templates/admin_edit_circle.html:102
#: snikket_web/templates/admin_edit_circle.html:116
msgid "All users on this service are already in this circle."
msgstr ""
@@ -1238,6 +1284,11 @@ msgstr ""
msgid "Your address"
msgstr ""
#: snikket_web/templates/invite_success.html:15
#: snikket_web/templates/user_home.html:21
msgid "Copy address"
msgstr ""
#: snikket_web/templates/invite_success.html:17
msgid ""
"You can now set up your legacy XMPP client with the above address and the"