implement requesting of reactions

This commit is contained in:
Mario Vavti
2025-04-25 22:35:13 +02:00
parent 0940be57fd
commit c614f899b7
5 changed files with 191 additions and 35 deletions

129
Zotlabs/Module/Request.php Normal file
View File

@@ -0,0 +1,129 @@
<?php
namespace Zotlabs\Module;
use Zotlabs\Web\Controller;
class Request extends Controller
{
private function mapVerb(string $verb) : string
{
$verbs = [
'like' => 'Like',
'dislike' => 'Dislike',
'announce' => 'Announce',
'attendyes' => 'Accept',
'attendno' => 'Reject',
'attendmaybe' => 'TentativeAccept'
];
if (array_key_exists($verb, $verbs)) {
return $verbs[$verb];
}
return EMPTY_STR;
}
private function like_response($arr) {
$page_mode = (($arr['item']['item_thread_top'] && $_REQUEST['page_mode']) ? $_REQUEST['page_mode'] : 'r_preview');
$conv_mode = (($_REQUEST['conv_mode']) ? $_REQUEST['conv_mode'] : 'network');
if ($conv_mode === 'channel') {
$parts = explode('@', $arr['owner_xchan']['xchan_addr']);
profile_load($parts[0]);
}
$item_normal = item_normal();
if ($page_mode === 'list') {
$items = q("SELECT item.*, item.id AS item_id FROM item
WHERE uid = %d $item_normal
AND parent = %d",
intval($arr['item']['uid']),
intval($arr['item']['parent'])
);
xchan_query($items, true);
$items = fetch_post_tags($items, true);
$items = conv_sort($items, 'commented');
}
else {
$activities = q("SELECT item.*, item.id AS item_id FROM item
WHERE uid = %d $item_normal
AND thr_parent = '%s'
AND verb IN ('%s', '%s', '%s', '%s', '%s', '%s', 'Accept', 'Reject', 'TentativeAccept')",
intval($arr['item']['uid']),
dbesc($arr['item']['mid']),
dbesc('Like'),
dbesc('Dislike'),
dbesc(ACTIVITY_SHARE),
dbesc(ACTIVITY_ATTEND),
dbesc(ACTIVITY_ATTENDNO),
dbesc(ACTIVITY_ATTENDMAYBE)
);
xchan_query($activities, true);
$items = array_merge([$arr['item']], $activities);
$items = fetch_post_tags($items, true);
}
$ret = [
'success' => 1,
'orig_id' => $arr['orig_item_id'], //this is required for pubstream items where $item_id != $item['id']
'id' => $arr['item']['id'],
'html' => conversation($items, $conv_mode, true, $page_mode),
];
// mod photos
if (isset($_REQUEST['reload']) && $_REQUEST['reload']) {
$ret['reload'] = 1;
}
return $ret;
}
public function get() : string
{
if (!local_channel()) {
killme();
}
$verb = self::mapVerb($_GET['verb']);
if (!$verb) {
killme();
}
$id = intval($_GET['id']);
$mid = strip_tags($_GET['mid']);
$hash = get_observer_hash();
$item_normal = item_normal();
$r = q("SELECT xchan_hash, xchan_name as name, xchan_url as url, xchan_photo_s as photo FROM item
LEFT JOIN xchan ON author_xchan = xchan_hash
WHERE uid = %d
AND parent = %d
AND thr_parent = '%s'
AND verb = '%s'
AND item_thread_top = 0
$item_normal",
intval(local_channel()),
dbesc($id),
dbesc($mid),
dbesc($verb)
);
$ret = [
'result' => $r,
'action' => (($verb === 'Announce') ? 'jotShare' : 'dolike'),
'action_label' => ((find_xchan_in_array($hash, $r)) ? t('- Remove yours') : t('+ Add yours'))
];
json_return_and_die($ret);
}
}

View File

@@ -1556,28 +1556,28 @@ function get_responses($conv_responses,$response_verbs,$ob,$item) {
return $ret;
}
function get_response_button_text($v,$count) {
function get_response_button_text($v, $count) {
switch($v) {
case 'like':
return ['label' => tt('Like','Likes',$count,'noun'), 'icon' => 'hand-thumbs-up', 'class' => 'like', 'onclick' => 'dolike'];
return ['label' => tt('Like','Likes',$count,'noun'), 'icon' => 'hand-thumbs-up', 'class' => 'like', 'action' => 'dolike'];
break;
case 'announce':
return ['label' => tt('Repeat','Repeats',$count,'noun'), 'icon' => 'repeat', 'class' => 'announce', 'onclick' => 'jotShare'];
return ['label' => tt('Repeat','Repeats',$count,'noun'), 'icon' => 'repeat', 'class' => 'announce', 'action' => 'jotShare'];
break;
case 'dislike':
return ['label' => tt('Dislike','Dislikes',$count,'noun'), 'icon' => 'hand-thumbs-down', 'class' => 'dislike', 'onclick' => 'dolike'];
return ['label' => tt('Dislike','Dislikes',$count,'noun'), 'icon' => 'hand-thumbs-down', 'class' => 'dislike', 'action' => 'dolike'];
break;
case 'comment':
return ['label' => tt('Comment','Comments',$count,'noun'), 'icon' => 'chat', 'class' => 'comment', 'onclick' => ''];
return ['label' => tt('Comment','Comments',$count,'noun'), 'icon' => 'chat', 'class' => 'comment', 'action' => ''];
break;
case 'attendyes':
return ['label' => tt('Attending','Attending',$count,'noun'), 'icon' => 'calendar-check', 'class' => 'attendyes', 'onclick' => 'dolike'];
return ['label' => tt('Attending','Attending',$count,'noun'), 'icon' => 'calendar-check', 'class' => 'attendyes', 'action' => 'dolike'];
break;
case 'attendno':
return ['label' => tt('Not Attending','Not Attending',$count,'noun'), 'icon' => 'calendar-x', 'class' => 'attendno', 'onclick' => 'dolike'];
return ['label' => tt('Not Attending','Not Attending',$count,'noun'), 'icon' => 'calendar-x', 'class' => 'attendno', 'action' => 'dolike'];
break;
case 'attendmaybe':
return ['label' => tt('Undecided','Undecided',$count,'noun'), 'icon' => 'calendar', 'class' => 'attendmaybe', 'onclick' => 'dolike'];
return ['label' => tt('Undecided','Undecided',$count,'noun'), 'icon' => 'calendar', 'class' => 'attendmaybe', 'action' => 'dolike'];
break;
default:
return [];

View File

@@ -1266,6 +1266,42 @@ function justifyPhotosAjax(id) {
$('#' + id).justifiedGallery('norewind').on('jg.complete', function(e){ justifiedGalleryActive = false; });
}
function request(id, mid, verb) {
const modal = new bootstrap.Modal('#reactions');
modal.show();
const modal_content = document.getElementById('reactions_body');
modal_content.innerHTML = 'Loading...';
const modal_title = document.getElementById('reactions_title');
modal_title.innerHTML = verb;
const modal_action = document.getElementById('reactions_action');
modal_action.innerHTML = '';
fetch('/request?verb=' + verb + '&mid=' + mid + '&id=' + id)
.then(response => response.json())
.then(obj => {
modal_content.innerHTML = '';
modal_action.innerHTML = '<a href="#" onclick="' + obj.action + '(' + id + ',\'' + verb + '\'); return false;">' + obj.action_label + '</a>';
obj.result.forEach(e => {
modal_content.innerHTML += '<a href="' + e.url + '" class="list-group-item list-group-item-action border-0"><img src="' + e.photo + '" class="menu-img-1" loading="lazy">&nbsp;' + e.name + '</a>';
});
})
.catch(error => {
console.error('Error fetching data:', error);
});
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function dolike(ident, verb) {
$('#like-rotator-' + ident).show();

View File

@@ -18,4 +18,19 @@
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal modal-sm" id="reactions" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title text-capitalize" id="reactions_title"></h3>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
</div>
<div class="modal-header" id="reactions_action">
</div>
<div class="modal-body list-group" id="reactions_body">
{{$wait}}
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{{include file="contact_edit_modal.tpl"}}

View File

@@ -112,39 +112,15 @@
{{foreach $item.responses as $verb=>$response}}
{{if $item.reactions_allowed || (!$item.reactions_allowed && $response.count)}}
<div class="">
<button type="button" title="{{$response.count}} {{$response.button.label}}" class="btn btn-sm btn-link{{if !$item.my_responses.$verb}} link-secondary{{/if}} wall-item-{{$response.button.class}}"{{if $response.modal}} data-bs-toggle="modal" data-bs-target="#{{$verb}}Modal-{{$item.id}}"{{else if $response.count}} data-bs-toggle="dropdown"{{elseif $item.reactions_allowed}} onclick="{{$response.button.onclick}}({{$item.id}},'{{$verb}}'); return false;"{{/if}} id="wall-item-{{$verb}}-{{$item.id}}">
<button type="button" title="{{$response.count}} {{$response.button.label}}" class="btn btn-sm btn-link{{if !$item.my_responses.$verb}} link-secondary{{/if}} wall-item-{{$response.button.class}}"{{if $item.reactions_allowed}} onclick="request({{$item.id}}, '{{$item.rawmid}}', '{{$verb}}'); return false;"{{/if}} id="wall-item-{{$verb}}-{{$item.id}}">
<i class="bi bi-{{$response.button.icon}} generic-icons"></i>{{if $response.count}}<span style="display: inline-block; margin-top: -.25rem;" class="align-top">{{$response.count}}</span>{{/if}}
</button>
{{if $response.modal}}
<div class="modal" id="{{$verb}}Modal-{{$item.id}}">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">{{$response.count}} {{$response.button.label}}</h3>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
</div>
{{if $item.reactions_allowed && !($verb === 'announce' && $item.my_responses.$verb)}} {{** undo announce is not yet supported **}}
<div class="modal-header">
<a href="#" class="text-reset" onclick="{{$response.button.onclick}}({{$item.id}},'{{$verb}}'); return false;">{{if $item.my_responses.$verb}}- {{$item.reaction_str.1}}{{else}}+ {{$item.reaction_str.0}}{{/if}}</a>
</div>
{{/if}}
<div class="modal-body response-list">
<ul class="nav nav-pills flex-column">
{{foreach $response.list as $liker}}
{{$liker}}
{{/foreach}}
</ul>
</div>
<div class="modal-footer clear">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{{$item.modal_dismiss}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{{else}}
<div class="dropdown-menu">
{{if $item.reactions_allowed && !($verb === 'announce' && $item.my_responses.$verb)}} {{** undo announce is not yet supported **}}
<a href="#" class="text-reset dropdown-item" onclick="{{$response.button.onclick}}({{$item.id}},'{{$verb}}'); return false;">{{if $item.my_responses.$verb}}- {{$item.reaction_str.1}}{{else}}+ {{$item.reaction_str.0}}{{/if}}</a>
<a href="#" class="text-reset dropdown-item" onclick="{{$response.button.onclick}}('{{$item.rawmid}}','{{$verb}}'); return false;">{{if $item.my_responses.$verb}}- {{$item.reaction_str.1}}{{else}}+ {{$item.reaction_str.0}}{{/if}}</a>
<div class="dropdown-divider"></div>
{{/if}}
{{foreach $response.list as $liker}}{{$liker}}{{/foreach}}