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
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use codec::{Decode, Encode};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_std::prelude::*;

use pallet_utils::{bool_to_option, SpaceId, rpc::{FlatContent, FlatWhoAndWhen, ShouldSkip}};

use crate::{Module, Space, Trait, FIRST_SPACE_ID};

#[derive(Eq, PartialEq, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct FlatSpace<AccountId, BlockNumber> {
    pub id: SpaceId,

    #[cfg_attr(feature = "std", serde(flatten))]
    pub who_and_when: FlatWhoAndWhen<AccountId, BlockNumber>,

    pub owner_id: AccountId,

    #[cfg_attr(feature = "std", serde(skip_serializing_if = "ShouldSkip::should_skip"))]
    pub parent_id: Option<SpaceId>,

    #[cfg_attr(feature = "std", serde(skip_serializing_if = "ShouldSkip::should_skip", serialize_with = "bytes_to_string"))]
    pub handle: Option<Vec<u8>>,

    #[cfg_attr(feature = "std", serde(flatten))]
    pub content: FlatContent,

    #[cfg_attr(feature = "std", serde(skip_serializing_if = "ShouldSkip::should_skip"))]
    pub is_hidden: Option<bool>,

    pub posts_count: u32,
    pub hidden_posts_count: u32,
    pub visible_posts_count: u32,
    pub followers_count: u32,

    pub score: i32,
}

#[cfg(feature = "std")]
fn bytes_to_string<S>(field: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
    let field_unwrapped = field.clone().unwrap_or_default();
    // If Bytes slice is invalid, then empty string will be returned
    serializer.serialize_str(
        std::str::from_utf8(&field_unwrapped).unwrap_or_default()
    )
}

impl<T: Trait> From<Space<T>> for FlatSpace<T::AccountId, T::BlockNumber> {
    fn from(from: Space<T>) -> Self {
        let Space {
            id, created, updated, owner,
            parent_id, handle, content, hidden, posts_count,
            hidden_posts_count, followers_count, score, ..
        } = from;

        Self {
            id,
            who_and_when: (created, updated).into(),
            owner_id: owner,
            parent_id,
            handle,
            content: content.into(),
            is_hidden: bool_to_option(hidden),
            posts_count,
            hidden_posts_count,
            visible_posts_count: posts_count.saturating_sub(hidden_posts_count),
            followers_count,
            score,
        }
    }
}

impl<T: Trait> Module<T> {
    pub fn get_spaces_by_ids(space_ids: Vec<SpaceId>) -> Vec<FlatSpace<T::AccountId, T::BlockNumber>> {
        space_ids.iter()
            .filter_map(|id| Self::require_space(*id).ok())
            .map(|space| space.into())
            .collect()
    }

    fn get_spaces_slice<F: FnMut(&Space<T>) -> bool>(
        start_id: u64,
        limit: u64,
        mut filter: F,
    ) -> Vec<FlatSpace<T::AccountId, T::BlockNumber>> {
        let mut space_id = start_id;
        let mut spaces = Vec::new();

        while spaces.len() < limit as usize && space_id >= FIRST_SPACE_ID {
            if let Some(space) = Self::require_space(space_id).ok() {
                if filter(&space) {
                    spaces.push(space.into());
                }
            }
            space_id = space_id.saturating_sub(1);
        }

        spaces
    }

    pub fn get_spaces(start_id: u64, limit: u64) -> Vec<FlatSpace<T::AccountId, T::BlockNumber>> {
        Self::get_spaces_slice(start_id, limit, |_| true)
    }

    pub fn get_public_spaces(start_id: u64, limit: u64) -> Vec<FlatSpace<T::AccountId, T::BlockNumber>> {
        Self::get_spaces_slice(start_id, limit, |space| space.is_public())
    }

    pub fn get_unlisted_spaces(start_id: u64, limit: u64) -> Vec<FlatSpace<T::AccountId, T::BlockNumber>> {
        Self::get_spaces_slice(start_id, limit, |space| space.is_unlisted())
    }

    pub fn get_space_id_by_handle(handle: Vec<u8>) -> Option<SpaceId> {
        Self::space_id_by_handle(handle)
    }

    pub fn get_space_by_handle(handle: Vec<u8>) -> Option<FlatSpace<T::AccountId, T::BlockNumber>> {
        Self::space_id_by_handle(handle)
            .and_then(|space_id| Self::require_space(space_id).ok())
            .map(|space| space.into())
    }

    fn get_space_ids_by_owner<F: FnMut(&Space<T>) -> bool>(owner: T::AccountId, mut compare_fn: F) -> Vec<SpaceId> {
        Self::space_ids_by_owner(owner)
            .iter()
            .filter_map(|space_id| Self::require_space(*space_id).ok())
            .filter(|space| compare_fn(space))
            .map(|space| space.id)
            .collect()
    }

    pub fn get_public_space_ids_by_owner(owner: T::AccountId) -> Vec<SpaceId> {
        Self::get_space_ids_by_owner(owner, |space| !space.hidden)
    }

    pub fn get_unlisted_space_ids_by_owner(owner: T::AccountId) -> Vec<SpaceId> {
        Self::get_space_ids_by_owner(owner, |space| space.hidden)
    }

    pub fn get_next_space_id() -> SpaceId {
        Self::next_space_id()
    }
}