diff --git a/app/mobile/ios/Databag/Info.plist b/app/mobile/ios/Databag/Info.plist
index 9447df13..c807082d 100644
--- a/app/mobile/ios/Databag/Info.plist
+++ b/app/mobile/ios/Databag/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 1.8
+ 1.9
CFBundleSignature
????
CFBundleVersion
diff --git a/app/mobile/src/context/useCardContext.hook.js b/app/mobile/src/context/useCardContext.hook.js
index 236a5356..4ffcd100 100644
--- a/app/mobile/src/context/useCardContext.hook.js
+++ b/app/mobile/src/context/useCardContext.hook.js
@@ -222,6 +222,17 @@ export function useCardContext() {
}
}
}
+ const setCardChannelTopicMarker = (cardId, channelId, marker) => {
+ let card = cards.current.get(cardId);
+ if (card) {
+ let channel = card.channels.get(channelId);
+ if (channel) {
+ channel.topicMarker = marker;
+ card.channels.set(channelId, channel);
+ cards.current.set(cardId, card);
+ }
+ }
+ }
const setCardChannelBlocked = (cardId, channelId, blocked) => {
let card = cards.current.get(cardId);
if (card) {
@@ -512,6 +523,12 @@ export function useCardContext() {
setCardChannelSyncRevision(cardId, channelId, revision);
updateState({ cards: cards.current });
},
+ setTopicMarker: async (cardId, channelId, marker) => {
+ const { guid } = session.current;
+ await store.actions.setCardChannelItemTopicMarker(guid, cardId, channelId, marker);
+ setCardChannelTopicMarker(cardId, channelId, marker);
+ updateState({ cards: cards.current });
+ },
setChannelBlocked: async (cardId, channelId) => {
const { guid } = session.current;
await store.actions.setCardChannelItemBlocked(guid, cardId, channelId);
@@ -556,9 +573,9 @@ export function useCardContext() {
const { detail, profile } = getCardEntry(cardId);
return await getContactChannelTopic(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId);
},
- getChannelTopics: async (cardId, channelId, revision) => {
+ getChannelTopics: async (cardId, channelId, revision, count, begin, end) => {
const { detail, profile } = getCardEntry(cardId);
- return await getContactChannelTopics(profile.node, `${profile.guid}.${detail.token}`, channelId, revision);
+ return await getContactChannelTopics(profile.node, `${profile.guid}.${detail.token}`, channelId, revision, count, begin, end);
},
getChannelTopicAssetUrl: (cardId, channelId, topicId, assetId) => {
const { detail, profile } = getCardEntry(cardId);
diff --git a/app/mobile/src/context/useChannelContext.hook.js b/app/mobile/src/context/useChannelContext.hook.js
index 1e0d3ae1..afd3f852 100644
--- a/app/mobile/src/context/useChannelContext.hook.js
+++ b/app/mobile/src/context/useChannelContext.hook.js
@@ -108,6 +108,13 @@ export function useChannelContext() {
channels.current.set(channelId, channel);
}
}
+ const setChannelTopicMarker = (channelId, marker) => {
+ let channel = channels.current.get(channelId);
+ if (channel) {
+ channel.topicMarker = marker;
+ channels.current.set(channelId, channel);
+ }
+ }
const setChannelBlocked = (channelId, blocked) => {
let channel = channels.current.get(channelId);
if (channel) {
@@ -213,6 +220,12 @@ export function useChannelContext() {
setChannelSyncRevision(channelId, revision);
updateState({ channels: channels.current });
},
+ setTopicMarker: async (channelId, marker) => {
+ const { guid } = session.current;
+ await store.actions.setChannelItemTopicMarker(guid, channelId, marker);
+ setChannelTopicMarker(channelId, marker);
+ updateState({ channels: channels.current });
+ },
setBlocked: async (channelId) => {
const { guid } = session.current;
await store.actions.setChannelItemBlocked(guid, channelId);
@@ -257,9 +270,9 @@ export function useChannelContext() {
const { server, appToken } = session.current;
return await getChannelTopic(server, appToken, channelId, topicId);
},
- getTopics: async (channelId, revision) => {
+ getTopics: async (channelId, revision, count, begin, end) => {
const { server, appToken } = session.current;
- return await getChannelTopics(server, appToken, channelId, revision);
+ return await getChannelTopics(server, appToken, channelId, revision, count, begin, end);
},
getTopicAssetUrl: (channelId, topicId, assetId) => {
const { server, appToken } = session.current;
diff --git a/app/mobile/src/context/useConversationContext.hook.js b/app/mobile/src/context/useConversationContext.hook.js
index a6137a07..659f484b 100644
--- a/app/mobile/src/context/useConversationContext.hook.js
+++ b/app/mobile/src/context/useConversationContext.hook.js
@@ -32,8 +32,9 @@ export function useConversationContext() {
const channel = useContext(ChannelContext);
const profile = useContext(ProfileContext);
const topics = useRef(null);
- const revision = useRef(0);
+ const revision = useRef(null);
const force = useRef(false);
+ const more = useRef(false);
const detailRevision = useRef(0);
const syncing = useRef(false);
const conversationId = useRef(null);
@@ -112,11 +113,11 @@ export function useConversationContext() {
}
return await channel.actions.getTopic(channelId, topicId);
}
- const getTopics = async (cardId, channelId, revision) => {
+ const getTopics = async (cardId, channelId, revision, begin, end) => {
if (cardId) {
- return await card.actions.getChannelTopics(cardId, channelId, revision);
+ return await card.actions.getChannelTopics(cardId, channelId, revision, 16, begin, end);
}
- return await channel.actions.getTopics(channelId, revision)
+ return await channel.actions.getTopics(channelId, revision, 16, begin, end)
}
const getTopicAssetUrl = (cardId, channelId, assetId) => {
if (cardId) {
@@ -166,6 +167,12 @@ export function useConversationContext() {
}
return await channel.actions.setSyncRevision(channelId, revision);
}
+ const setTopicMarker = async (cardId, channelId, marker) => {
+ if (cardId) {
+ return await card.actions.setTopicMarker(cardId, channelId, marker);
+ }
+ return await channel.actions.setTopicMarker(channelId, marker);
+ }
useEffect(() => {
if (conversationId.current) {
@@ -189,31 +196,46 @@ export function useConversationContext() {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
const channelItem = getChannel(cardId, channelId);
- if (channelItem && (channelItem.revision !== revision.current || force.current)) {
+
+ if (channelItem && (channelItem.revision !== revision.current || force.current || more.current)) {
syncing.current = true;
try {
- // set channel details
- if (detailRevision.current != channelItem.detailRevision) {
- if (curView === setView.current) {
- await setChannel(channelItem);
- detailRevision.current = channelItem.detailRevision;
- }
- }
- // initial load from store
+ // sync from server
+ let res;
if (!topics.current) {
topics.current = new Map();
const items = await getTopicItems(cardId, channelId);
items.forEach(item => {
topics.current.set(item.topicId, item);
});
+ await setChannel(channelItem);
+ detailRevision.current = channelItem.detailRevision;
+ }
+ else if (detailRevision.current != channelItem.detailRevision) {
+ await setChannel(channelItem);
+ detailRevision.current = channelItem.detailRevision;
+ }
+ else if (more.current) {
+ more.current = false;
+ res = await getTopics(cardId, channelId, null, null, channelItem.topicMarker)
+ }
+ else if (channelItem.topicRevision !== channelItem.syncRevision || force.current) {
+ force.current = false;
+ res = await getTopics(cardId, channelId, channelItem.syncRevision, channelItem.topicMarker)
+ }
+ else {
+ if (cardId) {
+ card.actions.setChannelReadRevision(cardId, channelId, revision.current);
+ }
+ else {
+ channel.actions.setReadRevision(channelId, channelItem.revision);
+ }
+ revision.current = channelItem.revision;
}
- // sync from server
- if (channelItem.topicRevision != channelItem.syncRevision || force.current) {
- force.current = false;
- const res = await getTopics(cardId, channelId, channelItem.syncRevision)
+ if (res?.topics) {
for (const topic of res.topics) {
if (!topic.data) {
topics.current.delete(topic.id);
@@ -238,18 +260,16 @@ export function useConversationContext() {
}
}
}
+ }
+
+ if (res?.marker) {
+ await setTopicMarker(cardId, channelId, res.marker);
+ }
+ if (res?.revision) {
await setSyncRevision(cardId, channelId, res.revision);
}
- // update revision
- revision.current = channelItem.revision;
if (curView == setView.current) {
- if (cardId) {
- card.actions.setChannelReadRevision(cardId, channelId, revision.current);
- }
- else {
- channel.actions.setReadRevision(channelId, revision.current);
- }
updateState({ topics: topics.current, init: true, error: false });
}
@@ -637,6 +657,12 @@ export function useConversationContext() {
}
}
},
+ loadMore: () => {
+ if (conversationId.current) {
+ more.current = true;
+ sync();
+ }
+ },
resync: () => {
if (conversationId.current) {
force.current = true;
diff --git a/app/mobile/src/context/useStoreContext.hook.js b/app/mobile/src/context/useStoreContext.hook.js
index 8eb86e85..0ffacff6 100644
--- a/app/mobile/src/context/useStoreContext.hook.js
+++ b/app/mobile/src/context/useStoreContext.hook.js
@@ -1,7 +1,7 @@
import { useEffect, useState, useRef, useContext } from 'react';
import SQLite from "react-native-sqlite-storage";
-const DATABAG_DB = 'databag_v081.db';
+const DATABAG_DB = 'db_v090.db';
export function useStoreContext() {
const [state, setState] = useState({});
@@ -12,10 +12,10 @@ export function useStoreContext() {
}
const initSession = async (guid) => {
- await db.current.executeSql(`CREATE TABLE IF NOT EXISTS channel_${guid} (channel_id text, revision integer, detail_revision integer, topic_revision integer, blocked integer, sync_revision integer, detail text, unsealed_detail text, summary text, unsealed_summary text, offsync integer, read_revision integer, unique(channel_id))`);
+ await db.current.executeSql(`CREATE TABLE IF NOT EXISTS channel_${guid} (channel_id text, revision integer, detail_revision integer, topic_revision integer, topic_marker integer, blocked integer, sync_revision integer, detail text, unsealed_detail text, summary text, unsealed_summary text, offsync integer, read_revision integer, unique(channel_id))`);
await db.current.executeSql(`CREATE TABLE IF NOT EXISTS channel_topic_${guid} (channel_id text, topic_id text, revision integer, detail_revision integer, blocked integer, detail text, unsealed_detail text, unique(channel_id, topic_id))`);
await db.current.executeSql(`CREATE TABLE IF NOT EXISTS card_${guid} (card_id text, revision integer, detail_revision integer, profile_revision integer, detail text, profile text, notified_view integer, notified_article integer, notified_profile integer, notified_channel integer, offsync integer, blocked integer, unique(card_id))`);
- await db.current.executeSql(`CREATE TABLE IF NOT EXISTS card_channel_${guid} (card_id text, channel_id text, revision integer, detail_revision integer, topic_revision integer, sync_revision integer, detail text, unsealed_detail text, summary text, unsealed_summary text, offsync integer, blocked integer, read_revision integer, unique(card_id, channel_id))`);
+ await db.current.executeSql(`CREATE TABLE IF NOT EXISTS card_channel_${guid} (card_id text, channel_id text, revision integer, detail_revision integer, topic_revision integer, topic_marker integer, sync_revision integer, detail text, unsealed_detail text, summary text, unsealed_summary text, offsync integer, blocked integer, read_revision integer, unique(card_id, channel_id))`);
await db.current.executeSql(`CREATE TABLE IF NOT EXISTS card_channel_topic_${guid} (card_id text, channel_id text, topic_id text, revision integer, detail_revision integer, blocked integer, detail text, unsealed_detail text, unique(card_id, channel_id, topic_id))`);
}
@@ -212,6 +212,9 @@ export function useStoreContext() {
setChannelItemSyncRevision: async (guid, channelId, revision) => {
await db.current.executeSql(`UPDATE channel_${guid} set sync_revision=? where channel_id=?`, [revision, channelId]);
},
+ setChannelItemTopicMarker: async (guid, channelId, marker) => {
+ await db.current.executeSql(`UPDATE channel_${guid} set topic_marker=? where channel_id=?`, [marker, channelId]);
+ },
setChannelItemBlocked: async (guid, channelId) => {
await db.current.executeSql(`UPDATE channel_${guid} set blocked=? where channel_id=?`, [1, channelId]);
},
@@ -242,13 +245,14 @@ export function useStoreContext() {
};
},
getChannelItems: async (guid) => {
- const values = await getAppValues(db.current, `SELECT channel_id, read_revision, revision, sync_revision, blocked, detail_revision, topic_revision, detail, unsealed_detail, summary, unsealed_summary FROM channel_${guid}`, []);
+ const values = await getAppValues(db.current, `SELECT channel_id, read_revision, revision, sync_revision, blocked, detail_revision, topic_revision, topic_marker, detail, unsealed_detail, summary, unsealed_summary FROM channel_${guid}`, []);
return values.map(channel => ({
channelId: channel.channel_id,
revision: channel.revision,
readRevision: channel.read_revision,
detailRevision: channel.detail_revision,
topicRevision: channel.topic_revision,
+ topicMarker: channel.topic_marker,
syncRevision: channel.sync_revision,
blocked: channel.blocked,
detail: decodeObject(channel.detail),
@@ -311,6 +315,9 @@ export function useStoreContext() {
setCardChannelItemSyncRevision: async (guid, cardId, channelId, revision) => {
await db.current.executeSql(`UPDATE card_channel_${guid} set sync_revision=? where card_id=? and channel_id=?`, [revision, cardId, channelId]);
},
+ setCardChannelItemTopicMarker: async (guid, cardId, channelId, marker) => {
+ await db.current.executeSql(`UPDATE card_channel_${guid} set topic_marker=? where card_id=? and channel_id=?`, [marker, cardId, channelId]);
+ },
setCardChannelItemDetail: async (guid, cardId, channelId, revision, detail) => {
await db.current.executeSql(`UPDATE card_channel_${guid} set detail_revision=?, detail=?, unsealed_detail=null where card_id=? and channel_id=?`, [revision, encodeObject(detail), cardId, channelId]);
},
@@ -335,7 +342,7 @@ export function useStoreContext() {
};
},
getCardChannelItems: async (guid) => {
- const values = await getAppValues(db.current, `SELECT card_id, channel_id, read_revision, sync_revision, revision, blocked, detail_revision, topic_revision, detail, unsealed_detail, summary, unsealed_summary FROM card_channel_${guid}`, []);
+ const values = await getAppValues(db.current, `SELECT card_id, channel_id, read_revision, sync_revision, revision, blocked, detail_revision, topic_revision, topic_marker, detail, unsealed_detail, summary, unsealed_summary FROM card_channel_${guid}`, []);
return values.map(channel => ({
cardId: channel.card_id,
channelId: channel.channel_id,
@@ -343,6 +350,7 @@ export function useStoreContext() {
readRevision: channel.read_revision,
detailRevision: channel.detail_revision,
topicRevision: channel.topic_revision,
+ topicMarker: channel.topic_marker,
syncRevision: channel.sync_revision,
detail: decodeObject(channel.detail),
unsealedDetail: decodeObject(channel.unsealed_detail),
diff --git a/app/mobile/src/session/conversation/Conversation.jsx b/app/mobile/src/session/conversation/Conversation.jsx
index a610e037..6a195693 100644
--- a/app/mobile/src/session/conversation/Conversation.jsx
+++ b/app/mobile/src/session/conversation/Conversation.jsx
@@ -24,7 +24,12 @@ export function ConversationHeader({ closeConversation, openDetails, state, acti
- { state.subject }
+ { state.more && !state.latched && (
+
+ )}
+ { (!state.more || state.latched) && (
+ { state.subject }
+ )}
{ state.error && (
@@ -94,10 +99,12 @@ export function ConversationBody({ state, actions }) {
contentContainerStyle={styles.topics}
ref={ref}
data={state.topics}
+ onEndReached={actions.loadMore}
onMomentumScrollEnd={ Platform.OS === 'ios' ? noop : actions.unlatch }
onScrollBeginDrag={ Platform.OS !== 'ios' ? noop : actions.unlatch }
maintainVisibleContentPosition={ state.latched ? null : { minIndexForVisibile: 2, } }
inverted={true}
+ initialNumToRender={16}
renderItem={({item}) => actions.setFocus(item.topicId)} hosting={state.host == null}
sealed={state.sealed} sealKey={state.sealKey}
diff --git a/app/mobile/src/session/conversation/useConversation.hook.js b/app/mobile/src/session/conversation/useConversation.hook.js
index 8cb4a32d..3df8f641 100644
--- a/app/mobile/src/session/conversation/useConversation.hook.js
+++ b/app/mobile/src/session/conversation/useConversation.hook.js
@@ -25,6 +25,7 @@ export function useConversation() {
locked: false,
sealKey: null,
delayed: false,
+ more: false,
});
const init = useRef(true);
@@ -129,6 +130,15 @@ export function useConversation() {
blockTopic: async (topicId) => {
await conversation.actions.blockTopic(topicId);
},
+ loadMore: async () => {
+ if (!state.more) {
+ updateState({ more: true });
+ setTimeout(() => {
+ updateState({ more: false });
+ }, 1000);
+ await conversation.actions.loadMore();
+ }
+ },
reportTopic: async (topicId) => {
await conversation.actions.addTopicReport(topicId);
},