From: Greg Burri Date: Thu, 26 Jun 2008 22:42:32 +0000 (+0000) Subject: ADD changement dans le protocole, le premier message de chaque conversation est maint... X-Git-Tag: 1.1.0~112 X-Git-Url: https://git.euphorik.ch/?a=commitdiff_plain;h=c4a3bf46b1974dce83a6333390dd97a800070b59;p=euphorik.git ADD changement dans le protocole, le premier message de chaque conversation est maintenant envoyé (il faut encore l'afficher) --- diff --git a/doc/protocole3.txt b/doc/protocole3.txt index 0af2cce..12b3f1f 100644 --- a/doc/protocole3.txt +++ b/doc/protocole3.txt @@ -37,14 +37,14 @@ Message ok générique : { "reply" : "ok" - } - -Entete des messages clients : -Si la version du protocole n'est pas similaire du coté serveur la requête est refusée - - { - "action" : , - "version" : 3 + } + +Entete des messages clients : +Si la version du protocole n'est pas similaire du coté serveur la requête est refusée + + { + "action" : , + "version" : 3 } @@ -65,7 +65,7 @@ ou "cookie" : "LKJDLAKSJBFLKASN" } ou - { + { "header" : {action : "authentification", version : 3}, "login" : "paul", "password" : "IJKJDHHSAD9081238" @@ -101,7 +101,7 @@ ou === Logout === c -> s - { + { "header" : {action : "logout", version : 3}, "cookie" : "LKJDLAKSJBFLKASN" } @@ -109,7 +109,7 @@ c -> s === Profile === c -> s - { + { "header" : {action : "set_profile", version : 3}, "cookie" : "LKJDLAKSJBFLKASN", "login" : "paul49", @@ -134,28 +134,28 @@ ou Si "last_message_id" est absent alors le client ne possède pas de message. Si "main_page" est absent alors est vaut 1. "cookie" n'est pas obligatoire. - - - { - "id" : 54, - "user_id" : 344, - "date" : "Hier 17:26:54", - "system" : true | false, - "owner" : true | false, - "answered" : true | false, - "is_a_reply" : true | false, - "nick" : "Paul", - "login" : "paul_22", - "content" : "Salut", - "root" : 453, - "answer_to" : [ - { "id" : 123, "nick" : "Pierre", "login" : "pierre_45" } - ] - "ek_master" : true | false - } + + + { + "id" : 54, + "user_id" : 344, + "date" : "Hier 17:26:54", + "system" : true | false, + "owner" : true | false, + "answered" : true | false, + "is_a_reply" : true | false, + "nick" : "Paul", + "login" : "paul_22", + "content" : "Salut", + "root" : 453, + "answer_to" : [ + { "id" : 123, "nick" : "Pierre", "login" : "pierre_45" } + ] + "ek_master" : true | false + } c -> s - { + { "header" : {action : "wait_event", version : 3}, "page" : "chat" "cookie" : "LKJDLAKSJBFLKASN", @@ -176,12 +176,13 @@ s -> c La première conversation est la principale (main). L'ordre des conversation est le même que celui des données de l'utilisateur. Le format de la date n'est pas formel. +first correpond au premier message de la conversation, vaut 'undefined' pour la conversation principale ainsi que pour les conversations vides. { "reply" : "new_message", "conversations" : [ { - "last_page" : true | false, - "first" : , + "last_page" : true | false, + "first" : | undefined, "messages" : [ , .. ] } ] @@ -205,7 +206,7 @@ ou === Wait event (page = admin) === c -> s - { + { "header" : {action : "wait_event", version : 3}, "page" : "admin", "last_troll" : 5 @@ -247,7 +248,7 @@ s -> c === Envoie d'un troll === c -> s - { + { "header" : {action : "put_troll", version : 3}, "cookie" : "LKJDLAKSJBFLKASN", "content" : "Un bon troll velu !" @@ -261,7 +262,7 @@ ou === Modification d'un troll === c -> s - { + { "header" : {action : "mod_troll", version : 3}, "cookie" : "LKJDLAKSJBFLKASN", "troll_id" : 3, @@ -276,7 +277,7 @@ ou === Suppression d'un troll === c -> s - { + { "header" : {action : "del_troll", version : 3}, "cookie" : "LKJDLAKSJBFLKASN", "troll_id" : 3 @@ -293,7 +294,7 @@ Le client envoie un message, le message peut répondre à un certain nombre d'au "answer_to" n'est pas obligatoire. c -> s - { + { "header" : {action : "put_message", version : 3}, "cookie" : "LKJDLAKSJBFLKASN", "nick" : "Paul", @@ -309,7 +310,7 @@ ou === Slapage === c -> s - { + { "header" : {action : "slap", version : 3}, "cookie" : "LKJDLAKSJBFLKASN", "user_id" : 67, @@ -324,7 +325,7 @@ ou === Bannissement === c -> s - { + { "header" : {action : "ban", version : 3}, "cookie" : "LKJDLAKSJBFLKASN", "duration" : 15, // en minute @@ -340,7 +341,7 @@ ou === Liste des ip bannis === c -> s - { + { "header" : {action : "list_banned_ips", version : 3}, "cookie" : "LKJDLAKSJBFLKASN" } @@ -365,7 +366,7 @@ s -> c === Débannissement === c -> s - { + { "header" : {action : "unban", version : 3}, "cookie" : "LKJDLAKSJBFLKASN" "ip" : "192.168.1.2" @@ -383,7 +384,7 @@ Le message est appondu avec un " +++ " devant, par exemple : > Gnome c'est mieux que KDE +++ Euh non ok, c'est faux c -> s - { + { "header" : {action : "correction", version : 3}, "cookie" : "LKJDLAKSJBFLKASN", "content" : "Euh non ok, c'est faux" diff --git a/modules/erl/euphorik_bd.erl b/modules/erl/euphorik_bd.erl index ed07a56..be46ab2 100755 --- a/modules/erl/euphorik_bd.erl +++ b/modules/erl/euphorik_bd.erl @@ -61,6 +61,8 @@ reponses/0, parents/1, enfants/1, + parents_id/1, + enfants_id/1, est_une_reponse_a_user/2, a_repondu_a_message/2, possede_message/2, @@ -619,6 +621,36 @@ enfants(M_id) -> end )). + +% Renvoie les parents d'un message M (les messages auquels répond M) +% ordrés du plus petit au plus grand.. +% @spec parents_id(integer()) -> [integer()] +parents_id(M) -> + resultat_transaction(mnesia:transaction(fun() -> + e( + qlc:sort( + q([E#reponse_minichat.cible || E <- mnesia:table(reponse_minichat), E#reponse_minichat.repondant =:= M]), + [{order, ascending}] + ), + [{tmpdir, ?KEY_SORT_TEMP_DIR}] + ) + end)). + + +% Renvoie les id des enfants d'un message M (les messages qui répondent à M) +% ordrés du plus petit au plus grand. +% @spec enfants_id(integer()) -> [integer()] +enfants_id(M) -> + resultat_transaction(mnesia:transaction(fun() -> + e( + qlc:sort( + q([E#reponse_minichat.repondant || E <- mnesia:table(reponse_minichat), E#reponse_minichat.cible =:= M]), + [{order, ascending}] + ), + [{tmpdir, ?KEY_SORT_TEMP_DIR}] + ) + end)). + % Est-ce que le message Id_mess est une réponse d'une message de Id_user ? est_une_reponse_a_user(Id_user, Id_mess) -> diff --git a/modules/erl/euphorik_minichat_conversation.erl b/modules/erl/euphorik_minichat_conversation.erl index e46ca91..2f932e2 100755 --- a/modules/erl/euphorik_minichat_conversation.erl +++ b/modules/erl/euphorik_minichat_conversation.erl @@ -15,227 +15,204 @@ % % You should have received a copy of the GNU General Public License % along with Euphorik. If not, see . -% -% Ce module permet la gestion des conversations du minichat d'euphorik. -% Un message (enfant) peut répondre à des messages (ses parents). -% Un message (parent) peut avoir plusieurs réponses (enfants) -% @author G.Burri -% -% @type Message() = {integer(), [integer()]} -% @type Conversation_detailee() = {[integer()], [integer()], [integer()], bool()} -% @type Conversation = {[Message()] , bool()} bool() : si true alors il y a encore des messages dans les pages suivantes. +% +% Ce module permet la gestion des conversations du minichat d'euphorik. +% Un message (enfant) peut répondre à des messages (ses parents). +% Un message (parent) peut avoir plusieurs réponses (enfants) +% @author G.Burri +% +% Les conversation se compose d'une liste de tuple comprenant la conversation, du premier message de la conversation, +% et d'un booleen indiquant s'il y a encore des messages +% @type Conversations() = [{Conversation(), Message(), bool()}] +% +% Une conversation est simplement une liste de messages +% @type Conversation() = [Message()] +% +% Un message est un tuple représentant le message et la liste des id +% des messages auquels il répond +% @type Message() = {#minichat, [int()]} +% + - -module(euphorik_minichat_conversation). -export([ conversations/4 -]). --include("../include/euphorik_bd.hrl"). --include("../include/euphorik_defines.hrl"). --include_lib("stdlib/include/qlc.hrl"). --import(lists, [reverse/1, any/2, map/2, sublist/3, filter/2]). --import(euphorik_bd, [resultat_transaction/1]). --import(qlc, [e/2, q/1]). --import(mnesia, [table/1, transaction/1]). - - -% Renvoie les conversations sous la forme d'une liste de conversation. -% Chaque conversation est un tuple {[{Message, Parents}], Plus} où -% Message est le message de type #minichat et Parents une liste d'Id. -% Plus est un bool. Si Plus vaut true alors il y a encore des messages. -% Si il n'y a pas de nouveaux message alors vide est renvoyé. -% Chaque racine est un tuple {R, P, D} -% N : le nombre de message -% D : le dernier message connu, 0 si aucun -% P : la page souhaité, la premier est la 1 -% @spec conversations([{integer(), integer(), integer()}], integer(), integer(), integer()) -> [Conversation()] -conversations(Racines, N, D, P) -> - Conversations = conversations_detailees(Racines, N, D, P), - % si les conversations sont vides alors on attend un nouveau message - Vide = not any( - fun(C) -> - case C of - {[], _} -> false; - {_, [], _, _} -> false; - _ -> true - end - end, - Conversations - ), - if Vide -> - vide; - true -> - mise_en_forme_conversations(Conversations) - end. - - -% Mise en forme des conversations pour l'utilisateur du module. -% @spec mise_en_forme_conversations([[integer()] | Conversation_detailee()]) -> [Conversation()] -mise_en_forme_conversations([]) -> []; -mise_en_forme_conversations([{Principale, Plus_principale} | Conversations]) -> - [{mise_en_forme_conversation(Principale), Plus_principale} | map(fun({_, Cn, _, Plus}) -> {mise_en_forme_conversation(Cn), Plus} end, Conversations)]. - - -% Mise en forme d'une liste d'id de messages : [4, 5, 8, ...] -> [{4, [5, 6]}, ...]. -% Ajoute les parents de chaque message. -% @spec mise_en_forme_conversation([integer()]) -> [{integer(), [integer()]}] -mise_en_forme_conversation(Messages) -> - resultat_transaction(mnesia:transaction( - fun() -> - lists:foldr( - fun(Id, Acc) -> - case euphorik_bd:message_by_id(Id) of - {ok, Message} -> - [{Message, parents(Id)} | Acc]; - _ -> - Acc - end - end, - [], - Messages - ) - end - )). - - -% Renvoie une liste de conversations, le première élément correspond à la conversation principale. -% Les autres éléments sont des tuples {C, Cn, X}, voir conversation/4 pour plus d'infos. -% Racines est une liste de tuple {Id, P} des racines des conversations ou P est la page et Id l'id du message. -% @spec conversations_detailees([{integer(), integer()}], integer(), integer(), integer()) -> [[integer()] | Conversation_detailee()] -conversations_detailees(Racines, N, D, P) -> - Conversations = map(fun({Racine, P_conv, Dernier}) -> conversation(Racine, N, Dernier, P_conv) end, Racines), - Conversation_principale = resultat_transaction(transaction(fun() -> - Curseur = qlc:cursor( - qlc:sort(q([E#minichat.id || E <- table(minichat)]), [{order, descending}]), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ), - {CP, Plus} = conversation_principale(Curseur, Conversations, N, P), - qlc:delete_cursor(Curseur), - {[M || M <- CP, M > D], Plus} % filtre en fonction de D - end)), - [Conversation_principale | Conversations]. - - -% Construit la conversation principale en fonction d'un curseur C initialement placé sur le dernier message -% et la liste de conversations. -% N est le nombre de messages que l'on souhaite. -% P est le numéro de la page (1, 2, 3...) -% @spec conversation_principale(qlc:QueryCursor(), [Conversation_detailee()], integer(), integer()) -> {[Id], Plus} -conversation_principale(C, Conversations, N, P) -> - % on prend en message de plus pour savoir s'il y en a plus que ce que l'on désire - CP = reverse(conversation_principale2(C, lists:flatten(map(fun({C2, _, X, _}) -> C2 -- X end, Conversations)), N + 1, (P - 1) * N)), - Plus = length(CP) =:= N + 1, - { - if Plus -> - [_| Suivants] = CP, - Suivants; - true -> - CP - end, - Plus - }. - - -% C est le curseur (voir ci dessus) -% 'Messages' sont les messages que l'on doit enlever de la conversation -% S est le nombre de messages qu'il faut sauter. -% @spec conversation_principale2(qlc:QueryCursor(), [integer()], integer(), integer()) -conversation_principale2(_, _, 0, _) -> - []; -conversation_principale2(C, Messages, N, S) -> - case qlc:next_answers(C, 1) of - [] -> []; - [M] -> % traitement message par message (pas des plus performant :/) - Doit_etre_saute = any(fun(E) -> E == M end, Messages), - if Doit_etre_saute -> - conversation_principale2(C, Messages, N, S); % le message ne fait pas partie de la conversation - S =:= 0 -> - [M | conversation_principale2(C, Messages, N - 1, S)]; % ok : le message fait partie de la conversation - true -> - conversation_principale2(C, Messages, N, S - 1) % on n'a pas encore atteint le début de la page - end - end. - - -% Renvoie un tuple {C, Cn, X, Plus} où -% C : La conversation complète -% Cn : La conversation tronqué en fonction de N, D et P -% X : La liste des messages répondant à des mess qui ne font pas partie de la conversation -% Plus : true s'il y a encore des messages après -% Inputs : -% R : l'id d'un message représentant la racine de la conversation -% N : le nombre de message par page -% D : Le dernier message connu 0 si aucun de connu -% P : La page désirée -% @spec conversation([integer()], integer(), integer(), integer()) -> Conversation_detailee() -conversation(R, N, D, P) -> - {C, X} = conversation([], [R], []), - Decalage = N * (P - 1) + 1, - { - reverse(C), - if Decalage > length(C) -> - []; - true -> - filter( - fun(E) -> E > D end, - reverse(sublist(C, Decalage, N)) - ) - end, - reverse(X), - Decalage + N - 1 < length(C) - }. - - -% Renvoie un tuple {C, X} où C est la conversation complète et X les messages répondant à des mess qui ne font pas partie de la conversation -% Attention : les messages de C et de X sont ordrés du plus grand Id au plus petit. -% @spec conversation([integer()], [integer()], [integer()]) -> {} -conversation(Conv, [M | Reste], X) -> - Est_deja_traite = any(fun(E) -> E =:= M end, Conv), - if Est_deja_traite -> - conversation(Conv, Reste, X); - true -> - Enfants = enfants(M), - Parents = parents(M), - % un message est dit externe si un de ses parent ne fait pas partie de la conversation ou si un de ses parents fait partie de X - Est_message_externe = Parents -- Conv =/= [] orelse intersection(Parents, X) =/= [], - conversation([M | Conv], lists:merge(Reste, Enfants), if Est_message_externe -> [M | X]; true -> X end) - end; -conversation(Messages, [], X) -> - {Messages, X}. - - -% Renvoie les enfants d'un message M (les messages qui répondent à M) -% ordrés du plus petit au plus grand. -% @spec enfants(integer()) -> [integer()] -enfants(M) -> - resultat_transaction(transaction(fun() -> - e( - qlc:sort( - q([E#reponse_minichat.repondant || E <- table(reponse_minichat), E#reponse_minichat.cible =:= M]), - [{order, ascending}] - ), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ) - end)). - - -% Renvoie les parents d'un message M (les messages auquels répond M) -% ordrés du plus petit au plus grand.. -% @spec parents(integer()) -> [integer()] -parents(M) -> - resultat_transaction(transaction(fun() -> - e( - qlc:sort( - q([E#reponse_minichat.cible || E <- table(reponse_minichat), E#reponse_minichat.repondant =:= M]), - [{order, ascending}] - ), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ) - end)). - - -% Intersection entre deux listes : [1, 3, 4] n [2, 4, 7] = [4] -% @spec intersection(list(term()), list(term())) -> list(term()) -intersection(L1, L2) -> - filter(fun(X) -> lists:member(X, L1) end, L2). - +]). +-include("../include/euphorik_bd.hrl"). +-include("../include/euphorik_defines.hrl"). +-include_lib("stdlib/include/qlc.hrl"). +-import(lists, [reverse/1, any/2, map/2, sublist/3, filter/2]). +-import(euphorik_bd, [resultat_transaction/1]). +-import(qlc, [e/2, q/1]). +-import(mnesia, [table/1, transaction/1]). + + +% Renvoie les conversations. +% Chaque racine est un tuple {R, P, D} +% R : l'id de la racine +% N : le nombre de message +% D : le dernier message connu, 0 si aucun +% P : la page souhaité, la premier est la 1 +% @spec conversations([{integer(), integer(), integer()}], integer(), integer(), integer()) -> Conversations() +conversations(Racines, N, D, P) -> + Conversations = conversations_detailees(Racines, N, D, P), + % si les conversations sont vides alors on attend un nouveau message + Vide = not any( + fun(C) -> + case C of + {[], _} -> false; + {_, [], _, _} -> false; + _ -> true + end + end, + Conversations + ), + if Vide -> + vide; + true -> + mise_en_forme_conversations(Conversations) + end. + + +% Mise en forme des conversations pour l'utilisateur du module. +% @type Conversation_principale() = {[integer()], bool} +% @type Conversation_detailee() = {[integer()], [integer()], [integer()], bool()} +% @spec mise_en_forme_conversations([Conversation_principal() | Conversation_detailee()]) -> [Conversation()] +mise_en_forme_conversations([]) -> []; +mise_en_forme_conversations([{Principale, Plus_principale} | Conversations]) -> + [{mise_en_forme_conversation(Principale), Plus_principale} | map(fun({_, Cn, _, Plus}) -> {mise_en_forme_conversation(Cn), Plus} end, Conversations)]. + + +% Mise en forme d'une liste d'id de messages : [4, 9, 8, ...] -> [{#minichat, [5, 6]}, ...]. +% Ajoute les parents de chaque message. +% @spec mise_en_forme_conversation([integer()]) -> [{#minichat, [integer()]}] +mise_en_forme_conversation(Messages) -> + resultat_transaction(transaction( + fun() -> + lists:foldr( + fun(Id, Acc) -> + case euphorik_bd:message_by_id(Id) of + {ok, Message} -> + [{Message, euphorik_bd:parents_id(Id)} | Acc]; + _ -> + Acc + end + end, + [], + Messages + ) + end + )). + + +% Renvoie une liste de conversations, le première élément correspond à la conversation principale. +% Les autres éléments sont des tuples {C, Cn, X, Plus}, voir conversation/4 pour plus d'infos. +% Racines est une liste de tuple {Id, P} des racines des conversations ou P est la page et Id l'id du message. +% @spec conversations_detailees([{integer(), integer()}], integer(), integer(), integer()) -> [[{integer(), bool()}] | Conversation_detailee()] +conversations_detailees(Racines, N, D, P) -> + Conversations = map(fun({Racine, P_conv, Dernier}) -> conversation(Racine, N, Dernier, P_conv) end, Racines), + Conversation_principale = resultat_transaction(transaction(fun() -> + Curseur = qlc:cursor( + qlc:sort(q([E#minichat.id || E <- table(minichat)]), [{order, descending}]), + [{tmpdir, ?KEY_SORT_TEMP_DIR}] + ), + {CP, Plus} = conversation_principale(Curseur, Conversations, N, P), + qlc:delete_cursor(Curseur), + {[M || M <- CP, M > D], Plus} % filtre en fonction de D + end)), + [Conversation_principale | Conversations]. + + +% Construit la conversation principale en fonction d'un curseur C initialement placé sur le dernier message +% et la liste de conversations. +% N est le nombre de messages que l'on souhaite. +% P est le numéro de la page (1, 2, 3...) +% @spec conversation_principale(qlc:QueryCursor(), [Conversation_detailee()], integer(), integer()) -> {[integer()], bool()} +conversation_principale(C, Conversations, N, P) -> + % on prend en message de plus pour savoir s'il y en a plus que ce que l'on désire + CP = reverse(conversation_principale2(C, lists:flatten(map(fun({C2, _, X, _}) -> C2 -- X end, Conversations)), N + 1, (P - 1) * N)), + Plus = length(CP) =:= N + 1, + { + if Plus -> + [_| Suivants] = CP, + Suivants; + true -> + CP + end, + Plus + }. + + +% C est le curseur (voir ci dessus) +% 'Messages' sont les messages que l'on doit enlever de la conversation +% S est le nombre de messages qu'il faut sauter. +% @spec conversation_principale2(qlc:QueryCursor(), [integer()], integer(), integer()) -> [integer()] +conversation_principale2(_, _, 0, _) -> + []; +conversation_principale2(C, Messages, N, S) -> + case qlc:next_answers(C, 1) of + [] -> []; + [M] -> % traitement message par message (pas des plus performant :/) + Doit_etre_saute = any(fun(E) -> E == M end, Messages), + if Doit_etre_saute -> + conversation_principale2(C, Messages, N, S); % le message ne fait pas partie de la conversation + S =:= 0 -> + [M | conversation_principale2(C, Messages, N - 1, S)]; % ok : le message fait partie de la conversation + true -> + conversation_principale2(C, Messages, N, S - 1) % on n'a pas encore atteint le début de la page + end + end. + + +% Renvoie un tuple {C, Cn, X, Plus} où +% C : La conversation complète +% Cn : La conversation tronqué en fonction de N, D et P +% X : La liste des messages répondant à des mess qui ne font pas partie de la conversation +% Plus : true s'il y a encore des messages après +% Inputs : +% R : l'id d'un message représentant la racine de la conversation +% N : le nombre de message par page +% D : Le dernier message connu 0 si aucun de connu +% P : La page désirée +% @spec conversation([integer()], integer(), integer(), integer()) -> Conversation_detailee() +conversation(R, N, D, P) -> + {C, X} = conversation([], [R], []), + Decalage = N * (P - 1) + 1, + { + reverse(C), + if Decalage > length(C) -> + []; + true -> + filter( + fun(E) -> E > D end, + reverse(sublist(C, Decalage, N)) + ) + end, + reverse(X), + Decalage + N - 1 < length(C) + }. + + +% Renvoie un tuple {C, X} où C est la conversation complète et X les messages répondant à des mess qui ne font pas partie de la conversation +% Attention : les messages de C et de X sont ordrés du plus grand Id au plus petit. +% @spec conversation([integer()], [integer()], [integer()]) -> {[int()], [int()]} +conversation(Conv, [M | Reste], X) -> + Est_deja_traite = any(fun(E) -> E =:= M end, Conv), + if Est_deja_traite -> + conversation(Conv, Reste, X); + true -> + Enfants = euphorik_bd:enfants_id(M), + Parents = euphorik_bd:parents_id(M), + % un message est dit externe si un de ses parent ne fait pas partie de la conversation ou si un de ses parents fait partie de X + Est_message_externe = Parents -- Conv =/= [] orelse intersection(Parents, X) =/= [], + conversation([M | Conv], lists:merge(Reste, Enfants), if Est_message_externe -> [M | X]; true -> X end) + end; +conversation(Messages, [], X) -> + {Messages, X}. + + +% Intersection entre deux listes : [1, 3, 4] n [2, 4, 7] = [4] +% @spec intersection(list(term()), list(term())) -> list(term()) +intersection(L1, L2) -> + filter(fun(X) -> lists:member(X, L1) end, L2). + diff --git a/modules/erl/euphorik_protocole.erl b/modules/erl/euphorik_protocole.erl index 448a20b..e8c1569 100755 --- a/modules/erl/euphorik_protocole.erl +++ b/modules/erl/euphorik_protocole.erl @@ -249,50 +249,48 @@ wait_event_page_chat(User, Racines_conversations, Message_count, Last_message_id {reply, "new_messages"}, {conversations, {array, lists:map( - fun({Conv, Plus}) -> + fun({Racine, {Conv, Plus}}) -> {struct, [ {last_page, not Plus}, - {messages, {array, + {first, + if Racine =:= undefined orelse Conv =:= [] -> + "undefined"; + true -> + {Racine_id, _, _} = Racine, + case euphorik_bd:message_by_id(Racine_id) of + {ok, Mess} -> + json_message(Mess, euphorik_bd:parents(Racine), User); + _ -> + "undefined" + end + end + }, % le premier message de la conversation, peut correspondre + {messages, {array, lists:map( - fun({Mess, Repond_a}) -> - Est_proprietaire = User =/= inconnu andalso User#user.id =:= Mess#minichat.auteur_id, - A_repondu_a_message = User =/= inconnu andalso euphorik_bd:a_repondu_a_message(User#user.id, Mess#minichat.id), - Est_une_reponse_a_user = User =/= inconnu andalso euphorik_bd:est_une_reponse_a_user(User#user.id, Mess#minichat.id), - {ok, User_mess } = euphorik_bd:user_by_id(Mess#minichat.auteur_id), - {struct, [ - {id, Mess#minichat.id}, - {user_id, User_mess#user.id}, - {date, format_date(Mess#minichat.date)}, - {system, Mess#minichat.auteur_id =:= 0}, - {owner, Est_proprietaire}, - {answered, A_repondu_a_message}, - {is_a_reply, Est_une_reponse_a_user}, - {nick, Mess#minichat.pseudo}, - {login, User_mess#user.login}, - {content, Mess#minichat.contenu}, - {root, Mess#minichat.racine_id}, - {answer_to, {array, lists:map( - fun(Id_mess) -> - {ok, M} = euphorik_bd:message_by_id(Id_mess), - {ok, User_reponse} = euphorik_bd:user_by_mess(M#minichat.id), - {struct, [{id, M#minichat.id}, {nick, M#minichat.pseudo}, {login, User_reponse#user.login}]} - end, - Repond_a - )}}, - {ek_master, User_mess#user.ek_master} - ]} + fun({Mess, Repond_a}) -> + json_message(Mess, Repond_a, User) end, Conv ) }} ]} end, - Conversations + % on ajoute un 'undefined' correspondant à la premier conversation qui ne possède pas de racine + % TODO : peut être à revoir car un peu lourd est compliqué + aggregation_racines_conversations([undefined | Racines_conversations], Conversations) ) }} ]} end end. + + +aggregation_racines_conversations(L1, L2) -> + aggregation_racines_conversations(L1, L2, []). +aggregation_racines_conversations([], [], L) -> lists:reverse(L); +aggregation_racines_conversations([E1|R1], [E2|R2], L) -> + aggregation_racines_conversations(R1, R2, [{E1, E2} | L]). + % Attend un événement lié à la page 'chat'. @@ -664,3 +662,35 @@ json_reponse_login_ok(User) -> {ek_master, User#user.ek_master} ] }. + +% Renvoie le message formaté en JSON. +% Mess est de type #minichat +% Repond_a est une liste d'id des messages auquel répond Mess +% User est l'utilisateur courant de type #user +json_message(Mess, Repond_a, User) -> + Est_proprietaire = User =/= inconnu andalso User#user.id =:= Mess#minichat.auteur_id, + A_repondu_a_message = User =/= inconnu andalso euphorik_bd:a_repondu_a_message(User#user.id, Mess#minichat.id), + Est_une_reponse_a_user = User =/= inconnu andalso euphorik_bd:est_une_reponse_a_user(User#user.id, Mess#minichat.id), + {ok, User_mess } = euphorik_bd:user_by_id(Mess#minichat.auteur_id), + {struct, [ + {id, Mess#minichat.id}, + {user_id, User_mess#user.id}, + {date, format_date(Mess#minichat.date)}, + {system, Mess#minichat.auteur_id =:= 0}, + {owner, Est_proprietaire}, + {answered, A_repondu_a_message}, + {is_a_reply, Est_une_reponse_a_user}, + {nick, Mess#minichat.pseudo}, + {login, User_mess#user.login}, + {content, Mess#minichat.contenu}, + {root, Mess#minichat.racine_id}, + {answer_to, {array, lists:map( + fun(Id_mess) -> + {ok, M} = euphorik_bd:message_by_id(Id_mess), + {ok, User_reponse} = euphorik_bd:user_by_mess(M#minichat.id), + {struct, [{id, M#minichat.id}, {nick, M#minichat.pseudo}, {login, User_reponse#user.login}]} + end, + Repond_a + )}}, + {ek_master, User_mess#user.ek_master} + ]}.