Merge branch 'KoalaSat:main' into main

This commit is contained in:
iiaannn 2023-03-01 10:32:47 +08:00 committed by GitHub
commit 8cdf59ce93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 198 additions and 32 deletions

View File

@ -139,8 +139,8 @@ android {
applicationId "com.nostros"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 56
versionName "v0.3.0.9-alpha"
versionCode 57
versionName "v0.3.1.0-alpha"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {

View File

@ -75,10 +75,12 @@ public class Event {
hideGroupMessage(database);
} else if (kind.equals("44")) {
muteUser(database);
} else if (kind.equals("1002")) {
saveRelays(database);
} else if (kind.equals("9735")) {
saveZap(database);
} else if (kind.equals("10001")) {
saveListPin(database);
} else if (kind.equals("10001") || kind.equals("30001")) {
saveList(database);
}
} catch (JSONException e) {
e.printStackTrace();
@ -254,10 +256,18 @@ public class Event {
database.replace("nostros_notes", null, values);
}
protected void saveListPin(SQLiteDatabase database) {
protected void saveList(SQLiteDatabase database) {
String query = "SELECT created_at FROM nostros_lists WHERE pubkey = ? AND kind = ?";
@SuppressLint("Recycle") Cursor cursor = database.rawQuery(query, new String[] {pubkey, kind});
JSONArray dTags = filterTags("d");
String listTag = "";
if (dTags.length() > 0) {
try {
listTag = dTags.getJSONArray(0).getString(1);
} catch (JSONException e) { }
}
ContentValues values = new ContentValues();
values.put("id", id);
values.put("content", content);
@ -266,6 +276,7 @@ public class Event {
values.put("pubkey", pubkey);
values.put("sig", sig);
values.put("tags", tags.toString());
values.put("list_tag", listTag);
if (cursor.getCount() == 0) {
database.insert("nostros_lists", null, values);
} else if (cursor.moveToFirst()) {

View File

@ -223,6 +223,9 @@ public class DatabaseModule {
" );");
database.execSQL("CREATE INDEX nostros_nostros_list_index ON nostros_lists(kind, pubkey);");
} catch (SQLException e) { }
try {
database.execSQL("ALTER TABLE nostros_lists ADD COLUMN list_tag TEXT;");
} catch (SQLException e) { }
}
public void saveEvent(JSONObject data, String userPubKey, String relayUrl) throws JSONException {

View File

@ -12,7 +12,12 @@ import { relayToColor } from '../../Functions/NativeFunctions'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { formatId } from '../../Functions/RelayFunctions/Users'
import { UserContext } from '../../Contexts/UserContext'
import { addBookmarkList, removeBookmarkList } from '../../Functions/RelayFunctions/Lists'
import {
addBookmarkList,
addList,
removeBookmarkList,
removeList,
} from '../../Functions/RelayFunctions/Lists'
interface NoteActionsProps {
bottomSheetRef: React.RefObject<RBSheet>
@ -20,13 +25,14 @@ interface NoteActionsProps {
export const NoteActions: React.FC<NoteActionsProps> = ({ bottomSheetRef }) => {
const theme = useTheme()
const { reloadLists, publicBookmarks, publicKey, privateKey, privateBookmarks } =
const { reloadLists, publicBookmarks, publicKey, privateKey, privateBookmarks, mutedEvents } =
React.useContext(UserContext)
const { database, displayNoteDrawer, relayColouring } = React.useContext(AppContext)
const { relayPool, setDisplayrelayDrawer, lastEventId } = React.useContext(RelayPoolContext)
const [note, setNote] = React.useState<Note>()
const [relays, setRelays] = React.useState<NoteRelay[]>([])
const [bookmarked, setBookmarked] = React.useState<boolean>(false)
const [isMuted, setIsMuted] = React.useState<boolean>()
const bottomSheetShareRef = React.useRef<RBSheet>(null)
const bottomBookmarkRef = React.useRef<RBSheet>(null)
@ -37,6 +43,11 @@ export const NoteActions: React.FC<NoteActionsProps> = ({ bottomSheetRef }) => {
{
kinds: [10001],
authors: [publicKey],
limit: 1,
},
{
kinds: [30001],
authors: [publicKey],
},
])
}
@ -45,14 +56,15 @@ export const NoteActions: React.FC<NoteActionsProps> = ({ bottomSheetRef }) => {
React.useEffect(() => {
reloadLists()
loadNote()
}, [displayNoteDrawer, lastEventId, bookmarked])
}, [displayNoteDrawer, lastEventId, bookmarked, isMuted])
React.useEffect(() => {
if (note) {
const allBookmarks = [...publicBookmarks, ...privateBookmarks]
setBookmarked(allBookmarks.find((id) => id === note.id) !== undefined)
setIsMuted(mutedEvents.find((id) => id === note.id) !== undefined)
}
}, [publicBookmarks, note])
}, [publicBookmarks, privateBookmarks, note, mutedEvents])
const loadNote: () => void = () => {
if (database && displayNoteDrawer) {
@ -77,6 +89,17 @@ export const NoteActions: React.FC<NoteActionsProps> = ({ bottomSheetRef }) => {
}
}
const changeMute: () => void = () => {
if (relayPool && database && publicKey && note?.id) {
if (isMuted) {
removeList(relayPool, database, publicKey, note.id, 'mute')
} else {
addList(relayPool, database, publicKey, note.id, 'mute')
}
setIsMuted(!isMuted)
}
}
const bottomSheetStyles = React.useMemo(() => {
return {
container: {
@ -107,6 +130,17 @@ export const NoteActions: React.FC<NoteActionsProps> = ({ bottomSheetRef }) => {
/>
<Text>{t('noteActions.copy')}</Text>
</View>
<View style={styles.actionButton}>
<IconButton
icon={isMuted ? 'volume-off' : 'volume-high'}
iconColor={isMuted ? theme.colors.error : theme.colors.onSecondaryContainer}
size={28}
onPress={changeMute}
/>
<Text style={isMuted ? { color: theme.colors.error } : {}}>
{t(isMuted ? 'noteActions.mutedThread' : 'noteActions.muteThread')}
</Text>
</View>
<View style={styles.actionButton}>
<IconButton
icon={bookmarked ? 'bookmark-check' : 'bookmark-multiple-outline'}
@ -115,6 +149,8 @@ export const NoteActions: React.FC<NoteActionsProps> = ({ bottomSheetRef }) => {
/>
<Text>{t(bookmarked ? 'noteActions.bookmarked' : 'noteActions.bookmark')}</Text>
</View>
</View>
<View style={styles.mainLayout}>
<View style={styles.actionButton}>
<IconButton
icon='eye'
@ -126,8 +162,6 @@ export const NoteActions: React.FC<NoteActionsProps> = ({ bottomSheetRef }) => {
/>
<Text>{t('noteActions.view')}</Text>
</View>
</View>
<View style={styles.mainLayout}>
<View style={styles.actionButton}>
<IconButton
icon='share-variant-outline'

View File

@ -26,6 +26,7 @@ export interface UserContextProps {
reloadLists: () => void
publicBookmarks: string[]
privateBookmarks: string[]
mutedEvents: string[]
logout: () => void
name?: string
setName: (value: string) => void
@ -62,6 +63,7 @@ export const initialUserContext: UserContextProps = {
setNip05: () => {},
publicBookmarks: [],
privateBookmarks: [],
mutedEvents: [],
}
export const UserContextProvider = ({ children }: UserContextProviderProps): JSX.Element => {
@ -81,6 +83,7 @@ export const UserContextProvider = ({ children }: UserContextProviderProps): JSX
const [validNip05, setValidNip05] = useState<boolean>()
const [publicBookmarks, setPublicBookmarks] = useState<string[]>([])
const [privateBookmarks, setPrivateBookmarks] = useState<string[]>([])
const [mutedEvents, setMutedEvents] = useState<string[]>([])
const reloadUser: () => void = () => {
if (database && publicKey) {
@ -109,6 +112,12 @@ export const UserContextProvider = ({ children }: UserContextProviderProps): JSX
setPrivateBookmarks(privateList.map((tag) => tag[1]))
}
})
getList(database, 30001, publicKey, 'mute').then((result) => {
if (result) {
const eTags = getETags(result)
setMutedEvents(eTags.map((tag) => tag[1]))
}
})
}
}
@ -204,6 +213,7 @@ export const UserContextProvider = ({ children }: UserContextProviderProps): JSX
reloadLists,
publicBookmarks,
privateBookmarks,
mutedEvents,
logout,
name,
setName,

View File

@ -13,9 +13,13 @@ export const getList: (
db: QuickSQLiteConnection,
kind: number,
pubKey: string,
) => Promise<List> = async (db, kind, pubKey) => {
const notesQuery = 'SELECT * FROM nostros_lists WHERE kind = ? AND pubkey = ?;'
const resultSet = await db.execute(notesQuery, [kind, pubKey])
tag?: string,
) => Promise<List> = async (db, kind, pubKey, tag) => {
let notesQuery = 'SELECT * FROM nostros_lists WHERE kind = ? AND pubkey = ?'
if (tag) {
notesQuery += ' AND list_tag = ?'
}
const resultSet = await db.execute(notesQuery, [kind, pubKey, tag])
const items: object[] = getItems(resultSet)
const relays: List[] = items.map((object) => databaseToEntity(object))
return relays[0]

View File

@ -238,14 +238,14 @@ export const getRepliesCount: (
return item['COUNT(*)'] ?? 0
}
export const getNotificationsCount: (
export const getNotificationsIds: (
db: QuickSQLiteConnection,
pubKey: string,
since: number,
) => Promise<number> = async (db, pubKey, notificationSeenAt) => {
) => Promise<string[]> = async (db, pubKey, notificationSeenAt) => {
const repliesQuery = `
SELECT
COUNT(*)
id
FROM nostros_notes
WHERE (nostros_notes.reply_event_id IN (
SELECT nostros_notes.id FROM nostros_notes WHERE pubkey = '${pubKey}'
@ -254,9 +254,10 @@ export const getNotificationsCount: (
AND created_at > ${notificationSeenAt};
`
const resultSet = db.execute(repliesQuery)
const item: { 'COUNT(*)': number } = resultSet?.rows?.item(0)
const items: Note[] = getItems(resultSet) as Note[]
const ids: string[] = items.map((item) => item.id ?? '')
return item['COUNT(*)'] ?? 0
return ids
}
export const getRepostCount: (

View File

@ -41,6 +41,29 @@ export const addBookmarkList: (
}
relayPool?.sendEvent(event)
}
export const addList: (
relayPool: RelayPool,
database: QuickSQLiteConnection,
publicKey: string,
eventId: string,
tag: string,
) => void = async (relayPool, database, publicKey, eventId, tag) => {
if (!eventId || eventId === '') return
const result = await getList(database, 30001, publicKey, tag)
let tags: string[][] = result?.tags ?? [['d', tag]]
const content: string = result?.content ?? ''
tags = [...tags, ['e', eventId]]
const event: Event = {
content,
created_at: getUnixTime(new Date()),
kind: 30001,
pubkey: publicKey,
tags,
}
relayPool?.sendEvent(event)
}
export const removeBookmarkList: (
relayPool: RelayPool,
@ -71,3 +94,27 @@ export const removeBookmarkList: (
relayPool?.sendEvent(event)
}
}
export const removeList: (
relayPool: RelayPool,
database: QuickSQLiteConnection,
publicKey: string,
eventId: string,
tag: string,
) => void = async (relayPool, database, publicKey, eventId, tag) => {
if (!eventId || eventId === '') return
const result = await getList(database, 30001, publicKey, tag)
if (result) {
const content: string = result?.content ?? ''
const tags = result.tags.filter((tag) => tag[1] !== eventId)
const event: Event = {
content,
created_at: getUnixTime(new Date()),
kind: 30001,
pubkey: publicKey,
tags,
}
relayPool?.sendEvent(event)
}
}

View File

@ -106,7 +106,9 @@
"share": "Teilen",
"view": "Ansehen",
"bookmarked": "Bookmarked",
"bookmark": "Bookmark"
"bookmark": "Bookmark",
"mutedThread": "Thread muted",
"muteThread": "Mute thread"
},
"noteCard": {
"notifications": {

View File

@ -106,7 +106,9 @@
"share": "Share",
"view": "View",
"bookmarked": "Bookmarked",
"bookmark": "Bookmark"
"bookmark": "Bookmark",
"mutedThread": "Thread muted",
"muteThread": "Mute thread"
},
"noteCard": {
"notifications": {

View File

@ -127,7 +127,9 @@
"share": "Compartir",
"view": "Ver",
"bookmarked": "Marcado",
"bookmark": "Marcar"
"bookmark": "Marcar",
"mutedThread": "Hilo silenciado",
"muteThread": "Silenciar hilo"
},
"noteCard": {
"notifications": {

View File

@ -134,7 +134,9 @@
"share": "Partager",
"view": "View",
"bookmarked": "Bookmarked",
"bookmark": "Bookmark"
"bookmark": "Bookmark",
"mutedThread": "Thread muted",
"muteThread": "Mute thread"
},
"noteCard": {
"notifications": {

View File

@ -127,7 +127,9 @@
"share": "Поделиться",
"view": "View",
"bookmarked": "Bookmarked",
"bookmark": "Bookmark"
"bookmark": "Bookmark",
"mutedThread": "Thread muted",
"muteThread": "Mute thread"
},
"noteCard": {
"notifications": {

View File

@ -126,7 +126,9 @@
"share": "分享",
"view": "查看",
"bookmarked": "已收藏",
"bookmark": "收藏"
"bookmark": "收藏",
"mutedThread": "Thread muted",
"muteThread": "Mute thread"
},
"noteCard": {
"notifications": {

View File

@ -79,6 +79,7 @@ export const BookmarksFeed: React.FC<BookmarksFeedProps> = ({
useEffect(() => {
if (pageSize > initialPageSize) {
loadNotes()
reloadLists()
}
}, [pageSize])
@ -99,6 +100,11 @@ export const BookmarksFeed: React.FC<BookmarksFeedProps> = ({
{
kinds: [10001],
authors: [publicKey],
limit: 1,
},
{
kinds: [30001],
authors: [publicKey],
},
{
kinds: [Kind.Text],

View File

@ -8,7 +8,7 @@ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityI
import { UserContext } from '../../Contexts/UserContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { getMentionNotes, getNotificationsCount } from '../../Functions/DatabaseFunctions/Notes'
import { getMentionNotes, getNotificationsIds } from '../../Functions/DatabaseFunctions/Notes'
import { AppContext } from '../../Contexts/AppContext'
import { StyleSheet } from 'react-native'
import RBSheet from 'react-native-raw-bottom-sheet'
@ -26,7 +26,7 @@ export const HomePage: React.FC = () => {
const theme = useTheme()
const { t } = useTranslation('common')
const { language, setPushedTab } = React.useContext(AppContext)
const { privateKey, publicKey } = React.useContext(UserContext)
const { privateKey, publicKey, mutedEvents } = React.useContext(UserContext)
const { database, notificationSeenAt, clipboardNip21, setClipboardNip21, refreshBottomBarAt } =
useContext(AppContext)
const { relayPool, lastEventId } = useContext(RelayPoolContext)
@ -43,7 +43,13 @@ export const HomePage: React.FC = () => {
useEffect(() => {
if (publicKey && database && relayPool) {
getNotificationsCount(database, publicKey, notificationSeenAt).then(setNewNotifications)
getNotificationsIds(database, publicKey, notificationSeenAt).then((results) => {
const unmutedThreads = results.filter((id) => {
if (!id ?? id === '') return false
return !mutedEvents.includes(id)
})
setNewNotifications(unmutedThreads.length)
})
getUserGroupMessagesCount(database, publicKey).then(setNewGroupMessages)
getDirectMessagesCount(database, publicKey).then(setNewdirectMessages)
subscribe()

View File

@ -22,12 +22,13 @@ import { useFocusEffect } from '@react-navigation/native'
import { getUnixTime } from 'date-fns'
import { Config } from '../../Functions/DatabaseFunctions/Config'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { getETags } from '../../Functions/RelayFunctions/Events'
export const NotificationsFeed: React.FC = () => {
const theme = useTheme()
const { t } = useTranslation('common')
const { database, setNotificationSeenAt, pushedTab } = useContext(AppContext)
const { publicKey } = useContext(UserContext)
const { publicKey, reloadLists, mutedEvents } = useContext(UserContext)
const initialPageSize = 10
const { lastEventId, relayPool } = useContext(RelayPoolContext)
const [pageSize, setPageSize] = useState<number>(initialPageSize)
@ -53,9 +54,14 @@ export const NotificationsFeed: React.FC = () => {
useEffect(() => {
loadNotes()
reloadLists()
setRefreshing(false)
}, [lastEventId])
useEffect(() => {
if (mutedEvents.length > 0) loadNotes()
}, [mutedEvents])
useEffect(() => {
if (pageSize > initialPageSize) {
subscribeNotes()
@ -93,13 +99,22 @@ export const NotificationsFeed: React.FC = () => {
'#e': [publicKey],
limit: pageSize,
},
{
kinds: [30001],
authors: [publicKey],
},
])
}
const loadNotes: () => void = () => {
if (database && publicKey) {
getMentionNotes(database, publicKey, pageSize).then(async (notes) => {
setNotes(notes)
const unmutedThreads = notes.filter((note) => {
if (!note?.id) return false
const eTags = getETags(note)
return !eTags.some((tag) => mutedEvents.includes(tag[1]))
})
setNotes(unmutedThreads)
setRefreshing(false)
if (notes.length > 0) {
const notedIds = notes.map((note) => note.id ?? '')

View File

@ -62,9 +62,24 @@ export const ProfileLoadPage: React.FC = () => {
])
relayPool?.subscribe('profile-load-others', [
{
kinds: [Kind.ChannelCreation, Kind.ChannelMetadata, 1002, 10001],
kinds: [Kind.ChannelCreation, Kind.ChannelMetadata],
authors: [publicKey],
},
{
kinds: [1002],
authors: [publicKey],
limit: 1,
},
{
kinds: [10001],
authors: [publicKey],
limit: 1,
},
{
kinds: [3001],
authors: [publicKey],
limit: 1,
},
])
} else {
setTimeout(() => loadMeta(), 1000)

View File

@ -12,6 +12,7 @@ import { getETags } from '../../../Functions/RelayFunctions/Events'
import { getList } from '../../../Functions/DatabaseFunctions/Lists'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { useTranslation } from 'react-i18next'
import { RelayFilters } from '../../../lib/nostr/RelayPool/intex'
interface BookmarksFeedProps {
publicKey: string
@ -50,10 +51,11 @@ export const BookmarksFeed: React.FC<BookmarksFeedProps> = ({
const subscribe: () => Promise<void> = async () => {
if (database) {
const filters = [
const filters: RelayFilters[] = [
{
kinds: [10001],
authors: [publicKey],
limit: 1,
},
]
getList(database, 10001, publicKey).then((result) => {