mirror of
https://github.com/balzack/databag.git
synced 2025-04-23 10:05:19 +00:00
building details screen
This commit is contained in:
parent
f551e6b5f5
commit
03f64e8257
@ -2018,9 +2018,9 @@ SPEC CHECKSUMS:
|
||||
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
|
||||
FBLazyVector: 38bb611218305c3bc61803e287b8a81c6f63b619
|
||||
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
|
||||
glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f
|
||||
glog: 69ef571f3de08433d766d614c73a9838a06bf7eb
|
||||
hermes-engine: 3b6e0717ca847e2fc90a201e59db36caf04dee88
|
||||
RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47
|
||||
RCT-Folly: 4464f4d875961fce86008d45f4ecf6cef6de0740
|
||||
RCTDeprecation: 34cbf122b623037ea9facad2e92e53434c5c7422
|
||||
RCTRequired: 24c446d7bcd0f517d516b6265d8df04dc3eb1219
|
||||
RCTTypeSafety: ef5e91bd791abd3a99b2c75fd565791102a66352
|
||||
@ -2094,7 +2094,7 @@ SPEC CHECKSUMS:
|
||||
RNVectorIcons: 6382277afab3c54658e9d555ee0faa7a37827136
|
||||
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
||||
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
|
||||
Yoga: 2a45d7e59592db061217551fd3bbe2dd993817ae
|
||||
Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8
|
||||
|
||||
PODFILE CHECKSUM: 8461018d8deceb200962c829584af7c2eb345c80
|
||||
|
||||
|
@ -43,7 +43,7 @@ export function Conversation({close, openDetails, wide}: {close: ()=>void, openD
|
||||
const scale = useAnimatedValue(0)
|
||||
|
||||
const alertParams = {
|
||||
title: state.strings.error,
|
||||
title: state.strings.operationFailed,
|
||||
prompt: state.strings.tryAgain,
|
||||
cancel: {
|
||||
label: state.strings.close,
|
||||
|
@ -23,6 +23,8 @@ export const styles = StyleSheet.create({
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
flexGrow: 1,
|
||||
textAlign: 'center',
|
||||
},
|
||||
close: {
|
||||
width: 32,
|
||||
@ -39,4 +41,37 @@ export const styles = StyleSheet.create({
|
||||
width: '100%',
|
||||
height: 2,
|
||||
},
|
||||
info: {
|
||||
width: '80%',
|
||||
},
|
||||
subject: {
|
||||
width: '100%',
|
||||
height: 52,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
paddingRight: 4,
|
||||
marginTop: 16,
|
||||
borderRadius: 8,
|
||||
},
|
||||
input: {
|
||||
flexGrow: 1,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
underline: {
|
||||
display: 'none',
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
},
|
||||
inputControl: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
backgroundColor: 'yellow',
|
||||
},
|
||||
members: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
});
|
||||
|
@ -1,11 +1,38 @@
|
||||
import React from 'react';
|
||||
import { SafeAreaView, View } from 'react-native';
|
||||
import {Divider, IconButton, Text} from 'react-native-paper';
|
||||
import React, { useState } from 'react';
|
||||
import { SafeAreaView, ScrollView, View } from 'react-native';
|
||||
import {Surface, Divider, IconButton, Text, TextInput} from 'react-native-paper';
|
||||
import {styles} from './Details.styled';
|
||||
import {useDetails} from './useDetails.hook';
|
||||
import { Confirm } from '../confirm/Confirm';
|
||||
|
||||
export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}) {
|
||||
const { state, actions } = useDetails();
|
||||
const [alert, setAlert] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const alertParams = {
|
||||
title: state.strings.operationFailer,
|
||||
prompt: state.strings.tryAgain,
|
||||
cancel: {
|
||||
label: state.strings.close,
|
||||
action: () => {
|
||||
setAlert(false);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const saveSubject = async () => {
|
||||
if (!saving) {
|
||||
setSaving(true);
|
||||
try {
|
||||
await actions.saveSubject();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setAlert(true);
|
||||
}
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.details}>
|
||||
@ -21,6 +48,38 @@ export function Details({close, closeAll}: {close: ()=>void, closeAll: ()=>void}
|
||||
)}
|
||||
</SafeAreaView>
|
||||
<Divider style={styles.divider} />
|
||||
<View style={styles.info}>
|
||||
{ state.host && (
|
||||
<Surface style={styles.subject} elevation={4}>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
underlineStyle={styles.underline}
|
||||
mode="flat"
|
||||
autoCapitalize="none"
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
value={state.editSubject}
|
||||
label={state.strings.subject}
|
||||
disabled={state.locked}
|
||||
left={<TextInput.Icon style={styles.icon} icon="label-outline" />}
|
||||
onChangeText={value => actions.setEditSubject(value)}
|
||||
/>
|
||||
{ state.subject !== state.editSubject && (
|
||||
<IconButton style={styles.icon} icon="undo-variant" onPress={actions.undoSubject} />
|
||||
)}
|
||||
{ state.subject !== state.editSubject && (
|
||||
<IconButton style={styles.icon} icon="content-save-outline" loading={saving} onPress={saveSubject} />
|
||||
)}
|
||||
</Surface>
|
||||
)}
|
||||
</View>
|
||||
<ScrollView style={styles.members}>
|
||||
|
||||
</ScrollView>
|
||||
<Confirm show={alert} params={alertParams} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
// input if host and unsealed
|
||||
// text otherwise
|
||||
|
@ -1,19 +1,164 @@
|
||||
import {useState, useContext, useEffect, useRef} from 'react';
|
||||
import {DisplayContext} from '../context/DisplayContext';
|
||||
import {ContextType} from '../context/ContextType';
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import { AppContext } from '../context/AppContext'
|
||||
import { DisplayContext } from '../context/DisplayContext'
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { FocusDetail, Card, Profile } from 'databag-client-sdk';
|
||||
|
||||
export function useDetails() {
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType
|
||||
const app = useContext(AppContext) as ContextType
|
||||
const [state, setState] = useState({
|
||||
cardId: null as null | string,
|
||||
channelId: '',
|
||||
detail: undefined as undefined | FocusDetail,
|
||||
access: false,
|
||||
host: false,
|
||||
sealed: false,
|
||||
locked: false,
|
||||
strings: display.state.strings,
|
||||
});
|
||||
timeFormat: display.state.timeFormat,
|
||||
dateFormat: display.state.dateFormat,
|
||||
subject: '',
|
||||
editSubject: '',
|
||||
created: '',
|
||||
profile: null as null | Profile,
|
||||
cards: [] as Card[],
|
||||
hostCard: null as null | Card,
|
||||
channelCards: [] as Card[],
|
||||
unknownContacts: 0,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updateState = (value: any) => {
|
||||
setState(s => ({...s, ...value}));
|
||||
};
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
|
||||
const getTimestamp = (created: number) => {
|
||||
const now = Math.floor((new Date()).getTime() / 1000)
|
||||
const date = new Date(created * 1000);
|
||||
const offset = now - created;
|
||||
if(offset < 43200) {
|
||||
if (state.timeFormat === '12h') {
|
||||
return date.toLocaleTimeString("en-US", {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
else {
|
||||
return date.toLocaleTimeString("en-GB", {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
}
|
||||
else if (offset < 31449600) {
|
||||
if (state.dateFormat === 'mm/dd') {
|
||||
return date.toLocaleDateString("en-US", {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
else {
|
||||
return date.toLocaleDateString("en-GB", {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state.dateFormat === 'mm/dd') {
|
||||
return date.toLocaleDateString("en-US");
|
||||
}
|
||||
else {
|
||||
return date.toLocaleDateString("en-GB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const { strings, timeFormat, dateFormat } = display.state;
|
||||
updateState({ strings, timeFormat, dateFormat });
|
||||
}, [display.state]);
|
||||
|
||||
useEffect(() => {
|
||||
const hostCard = state.cards.find(entry => entry.cardId == state.cardId);
|
||||
const profileRemoved = state.detail?.members ? state.detail.members.filter(member => state.profile?.guid != member.guid) : [];
|
||||
const contactCards = profileRemoved.map(member => state.cards.find(card => card.guid === member.guid));
|
||||
const channelCards = contactCards.filter(member => Boolean(member));
|
||||
const unknownContacts = contactCards.length - channelCards.length;
|
||||
updateState({ hostCard, channelCards, unknownContacts });
|
||||
}, [state.detail, state.cards, state.profile, state.cardId]);
|
||||
|
||||
useEffect(() => {
|
||||
const focus = app.state.focus;
|
||||
const { contact, identity } = app.state.session || { };
|
||||
if (focus && contact && identity) {
|
||||
const setCards = (cards: Card[]) => {
|
||||
const filtered = cards.filter(card => !card.blocked);
|
||||
const sorted = filtered.sort((a, b) => {
|
||||
if (a.handle > b.handle) {
|
||||
return 1;
|
||||
} else if (a.handle < b.handle) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
updateState({ cards: sorted });
|
||||
}
|
||||
const setProfile = (profile: Profile) => {
|
||||
updateState({ profile });
|
||||
}
|
||||
const setDetail = (focused: { cardId: string | null, channelId: string, detail: FocusDetail | null }) => {
|
||||
const detail = focused ? focused.detail : null;
|
||||
const cardId = focused.cardId;
|
||||
const channelId = focused.channelId;
|
||||
const access = Boolean(detail);
|
||||
const sealed = detail?.sealed;
|
||||
const locked = detail?.locked;
|
||||
const host = cardId == null;
|
||||
const subject = detail?.data?.subject ? detail.data.subject : '';
|
||||
const created = detail?.created ? getTimestamp(detail.created) : '';
|
||||
updateState({ detail, editSubject: subject, subject, channelId, cardId, access, sealed, locked, host, created });
|
||||
}
|
||||
focus.addDetailListener(setDetail);
|
||||
contact.addCardListener(setCards);
|
||||
identity.addProfileListener(setProfile);
|
||||
return () => {
|
||||
focus.removeDetailListener(setDetail);
|
||||
contact.removeCardListener(setCards);
|
||||
identity.removeProfileListener(setProfile);
|
||||
}
|
||||
}
|
||||
}, [app.state.focus, state.timeFormat, state.dateFormat]);
|
||||
|
||||
const actions = {
|
||||
};
|
||||
remove: async () => {
|
||||
const content = app.state.session.getContent()
|
||||
await content.removeChannel(state.channelId);
|
||||
app.actions.clearFocus();
|
||||
},
|
||||
leave: async () => {
|
||||
const content = app.state.session.getContent()
|
||||
await content.leaveChannel(state.cardId, state.channelId);
|
||||
app.actions.clearFocus();
|
||||
},
|
||||
block: async () => {
|
||||
const content = app.state.session.getContent();
|
||||
await content.setBlockedChannel(state.cardId, state.channelId, true);
|
||||
app.actions.clearFocus();
|
||||
},
|
||||
report: async () => {
|
||||
const content = app.state.session.getContent();
|
||||
await content.flagChannel(state.cardId, state.channelId);
|
||||
},
|
||||
setMember: async (cardId: string) => {
|
||||
const content = app.state.session.getContent();
|
||||
await content.setChannelCard(state.channelId, cardId);
|
||||
},
|
||||
clearMember: async (cardId: string) => {
|
||||
const content = app.state.session.getContent();
|
||||
await content.clearChannelCard(state.channelId, cardId);
|
||||
},
|
||||
setEditSubject: (editSubject: string) => {
|
||||
updateState({ editSubject });
|
||||
},
|
||||
undoSubject: () => {
|
||||
updateState({ editSubject: state.subject });
|
||||
},
|
||||
saveSubject: async () => {
|
||||
const content = app.state.session.getContent()
|
||||
await content.setChannelSubject(state.channelId, state.sealed ? 'sealed' : 'superbasic', { subject: state.editSubject });
|
||||
},
|
||||
}
|
||||
|
||||
return {state, actions};
|
||||
return { state, actions }
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ export function Settings({showLogout}: {showLogout: boolean}) {
|
||||
const [authMessage, setAuthMessage] = useState('');
|
||||
|
||||
const alertParams = {
|
||||
title: state.strings.error,
|
||||
title: state.strings.operationFailed,
|
||||
prompt: state.strings.tryAgain,
|
||||
cancel: {
|
||||
label: state.strings.close,
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user