1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use codec::{Decode, Encode};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_runtime::traits::Zero;
use sp_std::collections::btree_map::BTreeMap;
use sp_std::prelude::*;

use pallet_utils::{PostId, rpc::FlatWhoAndWhen};

use crate::{Module, Reaction, ReactionId, ReactionKind, Trait};

#[derive(Eq, PartialEq, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct FlatReaction<AccountId, BlockNumber> {
    pub id: ReactionId,
    #[cfg_attr(feature = "std", serde(flatten))]
    pub who_and_when: FlatWhoAndWhen<AccountId, BlockNumber>,
    pub kind: ReactionKind,
}

#[cfg(feature = "std")]
impl Serialize for ReactionKind {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
        let reaction_kind_bytes: &[u8; 1] = match self {
            ReactionKind::Upvote => b"U",
            ReactionKind::Downvote => b"D"
        };

        serializer.serialize_str(
            std::str::from_utf8(reaction_kind_bytes).unwrap_or_default()
        )
    }
}

impl<T: Trait> From<Reaction<T>> for FlatReaction<T::AccountId, T::BlockNumber> {
    fn from(from: Reaction<T>) -> Self {
        let Reaction { id, created, updated, kind } = from;

        Self {
            id,
            who_and_when: (created, updated).into(),
            kind,
        }
    }
}

impl<T: Trait> Module<T> {
    pub fn get_reactions_by_ids(
        reaction_ids: Vec<ReactionId>
    ) -> Vec<FlatReaction<T::AccountId, T::BlockNumber>> {
        reaction_ids.iter()
                    .filter_map(|id| Self::require_reaction(*id).ok())
                    .map(|reaction| reaction.into())
                    .collect()
    }

    pub fn get_reactions_by_post_id(
        post_id: PostId,
        limit: u64,
        offset: u64,
    ) -> Vec<FlatReaction<T::AccountId, T::BlockNumber>> {
        let mut reactions = Vec::new();

        let reaction_ids: Vec<PostId> = Self::reaction_ids_by_post_id(&post_id);
        let mut i = reaction_ids.len().saturating_sub(1 + offset as usize);

        while reactions.len() < limit as usize {
            if let Some(reaction_id) = reaction_ids.get(i) {
                if let Some(reaction) = Self::require_reaction(*reaction_id).ok() {
                    reactions.push(reaction.into());
                }
            }

            if i.is_zero() { break; }

            i = i.saturating_sub(1);
        }

        reactions
    }

    pub fn get_reaction_kinds_by_post_ids_and_reactor(
        post_ids: Vec<PostId>,
        reactor: T::AccountId,
    ) -> BTreeMap<PostId, ReactionKind> {
        let res = post_ids.iter()
            .filter_map(|post_id| Some(*post_id).zip(
                Option::from(Self::post_reaction_id_by_account((&reactor, post_id)))
                    .filter(|v| *v != 0)
                    .and_then(|reaction_id|
                        Self::require_reaction(reaction_id).ok().map(|reaction| reaction.kind)
                    )
            ));

        res.clone().collect()
    }
}