...
 
Commits (7)
......@@ -13,6 +13,7 @@ jest.mock('../../src/channel/ChannelStore');
jest.mock('../../src/channel/UserModel');
jest.mock('../../src/common/services/features.service');
jest.mock('../../AppStores');
jest.mock('../../src/common/services/boosted-content.service');
/**
* Tests
......
......@@ -38,6 +38,7 @@ jest.mock('../../src/common/components/FeedList', () => 'FeedList');
jest.mock('../../src/capture/CaptureFab', () => 'CaptureFab');
jest.mock('../../src/blogs/BlogCard', () => 'BlogCard');
jest.mock('../../src/common/components/Touchable', () => 'Touchable');
jest.mock('../../src/common/services/boosted-content.service');
/**
* Tests
......
......@@ -14,7 +14,7 @@ import appStores from '../../../AppStores';
jest.mock('../../../src/auth/UserStore');
jest.mock('../../../AppStores');
jest.mock('../../../src/channel/ChannelStore');
jest.mock('../../../src/common/services/boosted-content.service');
jest.mock('TouchableHighlight', () => 'TouchableHighlight');
/**
......
import boostedContentService from "../../../src/common/services/boosted-content.service";
import FeedsService from "../../../src/common/services/feeds.service";
jest.mock('../../../src/common/services/feeds.service');
jest.mock('../../../src/common/services/session.service');
/**
* Tests
*/
describe('Boosted content service', () => {
it('should fetch the boosts from the server', async () => {
const fakeBoosts = [{guid: 1}, {guid: 2}, {guid: 3}];
boostedContentService.feedsService.getEntities.mockResolvedValue(fakeBoosts);
// load the boosts
await boostedContentService.load();
// should fetch the feed
expect(boostedContentService.feedsService.setEndpoint).toBeCalledWith('api/v2/boost/feed');
expect(boostedContentService.feedsService.setOffset).toBeCalledWith(0);
expect(boostedContentService.feedsService.setLimit).toBeCalledWith(12);
expect(boostedContentService.feedsService.fetch).toBeCalled();
// should fetch the boosts entities
expect(boostedContentService.feedsService.getEntities).toBeCalled();
// the boosts should be stored in the boosts property
expect(boostedContentService.boosts).toBe(fakeBoosts);
});
it('should return next boost and start again when the end is reached', () => {
const fakeBoosts = [{guid: 1}, {guid: 2}, {guid: 3}];
boostedContentService.boosts = fakeBoosts;
// next
expect(boostedContentService.fetch()).toBe(fakeBoosts[0]);
// next
expect(boostedContentService.fetch()).toBe(fakeBoosts[1]);
// next
expect(boostedContentService.fetch()).toBe(fakeBoosts[2]);
// start again
expect(boostedContentService.fetch()).toBe(fakeBoosts[0]);
});
});
\ No newline at end of file
......@@ -15,6 +15,7 @@ configure({ adapter: new Adapter() });
jest.mock('react-native-localize');
jest.mock('mobx-react/native', () => require('mobx-react/custom'));
jest.mock('./AppStores');
jest.useFakeTimers();
......
export default {
load: jest.fn(),
fetch: jest.fn(),
}
\ No newline at end of file
const feedService = function() {
this.getEntities = jest.fn();
this.prepend = jest.fn();
this.setInjectBoost = jest.fn().mockImplementation(() => this);
this.setLimit = jest.fn().mockImplementation(() => this);
this.setOffset = jest.fn().mockImplementation(() => this);
this.setEndpoint = jest.fn().mockImplementation(() => this);
this.setParams = jest.fn().mockImplementation(() => this);
this.setAsActivities = jest.fn().mockImplementation(() => this);
this.fetch = jest.fn();
this.fetchLocal = jest.fn();
this.fetchRemoteOrLocal = jest.fn();
this.fetchLocalOrRemote = jest.fn();
}
export default feedService;
\ No newline at end of file
......@@ -2,5 +2,6 @@ export default {
login: jest.fn(),
logout: jest.fn(),
isLoggedIn: jest.fn(),
setInitialScreen: jest.fn()
setInitialScreen: jest.fn(),
onLogin: jest.fn()
}
\ No newline at end of file
import apiService from "./api.service";
import FeedStore from "../stores/FeedStore";
import FeedsService from "./feeds.service";
import sessionService from "./session.service";
/**
* Boosted content service
*/
class BoostedContentService {
offset: number = -1;
feedStore = new FeedStore;
feedsService: FeedsService = new FeedsService;
boosts: Array<ActivityModel> = [];
/**
* Constructor
*/
constructor() {
// TODO: LOAD when session begin
// this.feedStore
// .setLimit(50)
// .setOffset(0)
// .setEndpoint('api/v2/boost/feed')
// .fetch();
// always reload on login or app restart
sessionService.onLogin(this.load);
}
fetch() {
//TODO: IMPLEMENT
/**
* Reload boosts list
*/
load = async() => {
await this.feedsService
.setLimit(12)
.setOffset(0)
.setEndpoint('api/v2/boost/feed')
.fetch();
this.boosts = await this.feedsService.getEntities();
}
/**
* Fetch one boost
*/
fetch() {
this.offset++;
if (this.offset >= this.boosts.length) {
this.offset = 0;
}
return this.boosts[this.offset];
}
}
export default new BoostedContentService();
......@@ -8,6 +8,7 @@ import i18n from './i18n.service';
import connectivityService from './connectivity.service';
import Colors from '../../styles/Colors';
import { toJS } from 'mobx';
import boostedContentService from './boosted-content.service';
/**
* Feed store
......@@ -17,7 +18,12 @@ export default class FeedsService {
/**
* @var {boolean}
*/
asActivities = true;
injectBoost: boolean = false;
/**
* @var {boolean}
*/
asActivities: boolean = true;
/**
* @var {Number}
......@@ -37,19 +43,46 @@ export default class FeedsService {
/**
* @var {Object}
*/
params = {sync: 1}
params: Object = {sync: 1}
/**
* @var {Array}
*/
feed = [];
feed: Array = [];
/**
* Get entities from the current page
*/
async getEntities() {
const feedPage = this.feed.slice(this.offset, this.limit + this.offset);
return await entitiesService.getFromFeed(feedPage, this, this.asActivities);
const end = this.limit + this.offset;
const feedPage = this.feed.slice(this.offset, end);
const result = await entitiesService.getFromFeed(feedPage, this, this.asActivities);
if (!this.injectBoost) return result;
this.injectBoosted(3, result, end);
this.injectBoosted(8, result, end);
this.injectBoosted(16, result, end);
this.injectBoosted(24, result, end);
this.injectBoosted(32, result, end);
this.injectBoosted(40, result, end);
return result;
}
/**
* Inject boost at given position
*
* @param {number} position
* @param {Array<ActivityModel>} entities
* @param {number} end
*/
injectBoosted(position, entities, end) {
if (this.offset <= position && end >= position) {
const boost = boostedContentService.fetch();
if (boost) entities.splice( position + this.offset, 0, boost );
}
}
/**
......@@ -90,6 +123,16 @@ export default class FeedsService {
return this;
}
/**
* Set inject boost
* @param {Array} feed
* @returns {FeedsService}
*/
setInjectBoost(injectBoost): FeedsService {
this.injectBoost = injectBoost;
return this;
}
/**
* Set limit
* @param {integer} limit
......
......@@ -181,6 +181,16 @@ export default class FeedStore {
return this;
}
/**
* Set inject boost
* @param {Array} feed
* @returns {FeedStore}
*/
setInjectBoost(injectBoost): FeedStore {
this.feedsService.setInjectBoost(injectBoost);
return this;
}
/**
* Set the params for the feeds service
* @param {Object} params
......
......@@ -66,46 +66,11 @@ class DiscoveryStore {
this.listenChanges();
this.listStore.getMetadataService()
.setSource('feed/discovery')
.setMedium('feed');
}
/**
* Inject boosts to the feed
* @param {object} feed
* @param {OffsetFeedListStore} list
*/
async injectBoosts(feed, list) {
const start = list.entities.length;
const finish = feed.entities.length + start;
if (finish > 40) return;
await this.insertBoost(3, feed, start, finish);
await this.insertBoost(8, feed, start, finish);
await this.insertBoost(16, feed, start, finish);
await this.insertBoost(24, feed, start, finish);
await this.insertBoost(32, feed, start, finish);
await this.insertBoost(40, feed, start, finish);
}
/**
* Insert a boost in give position
* @param {integer} position
* @param {object} feed
* @param {integer} start
* @param {integer} finish
*/
async insertBoost(position, feed, start, finish) {
if (start <= position && finish >= position) {
try {
const boost = await boostedContentService.fetch();
if (boost) feed.entities.splice( position + start, 0, boost );
} catch (err) {
logService.exception('[DiscoveryStore] insertBoost', err);
}
}
this.listStore
.setInjectBoost(true)
.getMetadataService()
.setSource('feed/discovery')
.setMedium('feed');
}
/**
......
......@@ -43,6 +43,7 @@ class NewsfeedStore {
this.buildStores();
this.feedStore
.setEndpoint(`api/v2/feeds/subscribed/activities`)
.setInjectBoost(true)
.setLimit(12);
}
......@@ -110,11 +111,6 @@ class NewsfeedStore {
try {
feed = await fetchFn(store.list.offset, 12);
// inject boosts
if (featuresService.has('es-feeds')) {
await this.injectBoosts(feed);
}
feed.entities = ActivityModel.createMany(feed.entities);
this.assignRowKeys(feed, store);
store.list.setList(feed, refresh);
......@@ -133,42 +129,6 @@ class NewsfeedStore {
}
}
/**
* Inject boosts to the feed
* @param {object} feed
*/
async injectBoosts(feed) {
const start = this.list.entities.length;
const finish = feed.entities.length + start;
if (finish > 40) return;
await this.insertBoost(3, feed, start, finish);
await this.insertBoost(8, feed, start, finish);
await this.insertBoost(16, feed, start, finish);
await this.insertBoost(24, feed, start, finish);
await this.insertBoost(32, feed, start, finish);
await this.insertBoost(40, feed, start, finish);
}
/**
* Insert a boost in give position
* @param {integer} position
* @param {object} feed
* @param {integer} start
* @param {integer} finish
*/
async insertBoost(position, feed, start, finish) {
if (start <= position && finish >= position) {
try {
const boost = await boostedContentService.fetch();
if (boost) feed.entities.splice( position + start, 0, boost );
} catch (err) {
logService.exception('[NewsfeedStore] insertBoost', err);
}
}
}
/**
* Generate a unique Id for use with list views
* @param {object} feed
......