Add smart_flatten_conversation function

- Fix doc
- Fix formatting
- Update conv_sort with smart threading
This commit is contained in:
Hypolite Petovan 2017-11-19 00:57:06 -05:00
parent dc06b851f5
commit 81c3ddd1fb

View File

@ -1361,13 +1361,20 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) {
return $o; return $o;
} }
/**
function get_item_children($arr, $parent) { * Returns all the children in the given item list of the given parent. If threading
$children = array(); * is allowed, does it recursively.
$a = get_app(); *
foreach ($arr as $item) { * @param array $item_list
* @param array $parent
* @return type
*/
function get_item_children(array $item_list, array $parent, $recursive = true)
{
$children = [];
foreach ($item_list as $item) {
if ($item['id'] != $item['parent']) { if ($item['id'] != $item['parent']) {
if (Config::get('system', 'thread_allow') && $a->theme_thread_allow) { if ($recursive) {
// Fallback to parent-uri if thr-parent is not set // Fallback to parent-uri if thr-parent is not set
$thr_parent = $item['thr-parent']; $thr_parent = $item['thr-parent'];
if ($thr_parent == '') { if ($thr_parent == '') {
@ -1375,7 +1382,7 @@ function get_item_children($arr, $parent) {
} }
if ($thr_parent == $parent['uri']) { if ($thr_parent == $parent['uri']) {
$item['children'] = get_item_children($arr, $item); $item['children'] = get_item_children($item_list, $item);
$children[] = $item; $children[] = $item;
} }
} elseif ($item['parent'] == $parent['id']) { } elseif ($item['parent'] == $parent['id']) {
@ -1386,54 +1393,124 @@ function get_item_children($arr, $parent) {
return $children; return $children;
} }
/// @TODO Add type-hint /**
function sort_item_children($items) { * Recursively sorts a tree-like item array
*
* @param array $items
* @return array
*/
function sort_item_children(array $items)
{
$result = $items; $result = $items;
usort($result, 'sort_thr_created_rev'); usort($result, 'sort_thr_created_rev');
foreach ($result as $k => $i) { foreach ($result as $k => $i) {
if (count($result[$k]['children'])) { if (isset($result[$k]['children'])) {
$result[$k]['children'] = sort_item_children($result[$k]['children']); $result[$k]['children'] = sort_item_children($result[$k]['children']);
} }
} }
return $result; return $result;
} }
/// @TODO Add type-hint /**
function add_children_to_list($children, &$arr) { * Recursively add all children items at the top level of a list
foreach ($children as $y) { *
$arr[] = $y; * @param array $children List of items to append
if (count($y['children'])) { * @param array $item_list
add_children_to_list($y['children'], $arr); */
function add_children_to_list(array $children, array &$item_list)
{
foreach ($children as $child) {
$item_list[] = $child;
if (isset($child['children'])) {
add_children_to_list($child['children'], $item_list);
} }
} }
} }
/// @TODO Add type-hint /**
function conv_sort($arr, $order) { * This recursive function takes the item tree structure created by conv_sort() and
* flatten the extraneous depth levels when people reply sequentially, removing the
if ((!(is_array($arr) && count($arr)))) { * stairs effect in threaded conversations limiting the available content width.
return array(); *
* The basic principle is the following: if a post item has only one reply and is
* the last reply of its parent, then the reply is moved to the parent.
*
* This process is rendered somewhat more complicated because items can be either
* replies or likes, and these don't factor at all in the reply count/last reply.
*
* @param array $parent A tree-like array of items
* @return array
*/
function smart_flatten_conversation(array $parent)
{
if (! isset($parent['children']) || count($parent['children']) == 0) {
return $parent;
} }
$parents = array(); // We use a for loop to ensure we process the newly-moved items
$children = array(); for ($i = 0; $i < count($parent['children']); $i++) {
$newarr = array(); $child = $parent['children'][$i];
/* if (isset($child['children']) && count($child['children'])) {
* This is a preparation for having two different items with the same uri in one thread // This helps counting only the regular posts
* This will otherwise lead to an endless loop. $count_post_closure = function($var) {
*/ return $var['verb'] === 'http://activitystrea.ms/schema/1.0/post';
foreach ($arr as $x) { };
if (!isset($newarr[$x['uri']])) {
$newarr[$x['uri']] = $x; $child_post_count = count(array_filter($child['children'], $count_post_closure));
$remaining_post_count = count(array_filter(array_slice($parent['children'], $i), $count_post_closure));
// If there's only one child's children post and this is the last child post
if ($child_post_count == 1 && $remaining_post_count == 1) {
// Searches the post item in the children
$j = 0;
while($child['children'][$j]['verb'] !== 'http://activitystrea.ms/schema/1.0/post' && $j < count($child['children'])) {
$j ++;
}
$moved_item = $child['children'][$j];
unset($parent['children'][$i]['children'][$j]);
$parent['children'][] = $moved_item;
} else {
$parent['children'][$i] = smart_flatten_conversation($child);
}
} }
} }
$arr = $newarr; return $parent;
}
foreach ($arr as $x) {
if ($x['id'] == $x['parent']) { /**
$parents[] = $x; * Expands a flat list of items into corresponding tree-like conversation structures,
* sort the top-level posts either on "created" or "commented", and finally
* append all the items at the top level (???)
*
* @param array $item_list A list of items belonging to one or more conversations
* @param string $order Either on "created" or "commented"
* @return array
*/
function conv_sort(array $item_list, $order)
{
$parents = [];
if (!(is_array($item_list) && count($item_list))) {
return $parents;
}
$item_array = [];
// Dedupes the item list on the uri to prevent infinite loops
foreach ($item_list as $item) {
$item_array[$item['uri']] = $item;
}
// Extract the top level items
foreach ($item_array as $item) {
if ($item['id'] == $item['parent']) {
$parents[] = $item;
} }
} }
@ -1443,73 +1520,69 @@ function conv_sort($arr, $order) {
usort($parents, 'sort_thr_commented'); usort($parents, 'sort_thr_commented');
} }
if (count($parents)) { $thread_allowed = Config::get('system', 'thread_allow') && get_app()->theme_thread_allow;
foreach ($parents as $i => $_x) {
$parents[$i]['children'] = get_item_children($arr, $_x); foreach ($parents as $i => $parent) {
$parents[$i]['children'] = get_item_children($item_array, $parent, $thread_allowed);
}
foreach ($parents as $i => $parent) {
$parents[$i]['children'] = sort_item_children($parents[$i]['children']);
}
if ($thread_allowed && PConfig::get(local_user(), 'system', 'smart_threading', 0)) {
foreach ($parents as $i => $parent) {
$parents[$i] = smart_flatten_conversation($parent);
} }
} }
/// @TODO Old-lost code? /// @TODO: Stop recusrsively adding all children back to the top level (!!!)
/*foreach ($arr as $x) { /// However, this apparently ensures responses (likes, attendance) display (?!)
if ($x['id'] != $x['parent']) { foreach ($parents as $parent) {
$p = find_thread_parent_index($parents,$x); if (count($parent['children'])) {
if ($p !== false) add_children_to_list($parent['children'], $parents);
$parents[$p]['children'][] = $x;
}
}*/
if (count($parents)) {
foreach ($parents as $k => $v) {
if (count($parents[$k]['children'])) {
$parents[$k]['children'] = sort_item_children($parents[$k]['children']);
/// @TODO Old-lost code?
/*$y = $parents[$k]['children'];
usort($y,'sort_thr_created_rev');
$parents[$k]['children'] = $y;*/
}
} }
} }
$ret = array(); return $parents;
if (count($parents)) {
foreach ($parents as $x) {
$ret[] = $x;
if (count($x['children'])) {
add_children_to_list($x['children'], $ret);
/// @TODO Old-lost code?
/*foreach ($x['children'] as $y)
$ret[] = $y;*/
}
}
}
return $ret;
} }
/// @TODO Add type-hint /**
function sort_thr_created($a, $b) { * usort() callback to sort item arrays by the created key
*
* @param array $a
* @param array $b
* @return int
*/
function sort_thr_created(array $a, array $b)
{
return strcmp($b['created'], $a['created']); return strcmp($b['created'], $a['created']);
} }
/// @TODO Add type-hint /**
function sort_thr_created_rev($a, $b) { * usort() callback to reverse sort item arrays by the created key
*
* @param array $a
* @param array $b
* @return int
*/
function sort_thr_created_rev(array $a, array $b)
{
return strcmp($a['created'], $b['created']); return strcmp($a['created'], $b['created']);
} }
/// @TODO Add type-hint /**
function sort_thr_commented($a, $b) { * usort() callback to sort item arrays by the commented key
*
* @param array $a
* @param array $b
* @return type
*/
function sort_thr_commented(array $a, array $b)
{
return strcmp($b['commented'], $a['commented']); return strcmp($b['commented'], $a['commented']);
} }
/// @TODO Add type-hint
function find_thread_parent_index($arr, $x) {
foreach ($arr as $k => $v) {
if ($v['id'] == $x['parent']) {
return $k;
}
}
return false;
}
/// @TODO Add type-hint /// @TODO Add type-hint
function render_location_dummy($item) { function render_location_dummy($item) {
if ($item['location'] != "") { if ($item['location'] != "") {