commit
00d21265cb
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
|
@ -15,17 +15,21 @@ import DiscoverJsx from 'src/components/pages/discover';
|
||||||
import SearchJsx from 'src/components/pages/discover/search';
|
import SearchJsx from 'src/components/pages/discover/search';
|
||||||
import ViewHashtagJsx from 'src/components/pages/discover/view-hashtag';
|
import ViewHashtagJsx from 'src/components/pages/discover/view-hashtag';
|
||||||
import NotificationsJsx from 'src/components/pages/profile/notifications';
|
import NotificationsJsx from 'src/components/pages/profile/notifications';
|
||||||
|
import UserListJsx from "src/components/pages/user-list.js";
|
||||||
|
import SettingsJsx from "src/components/pages/profile/settings.js";
|
||||||
|
|
||||||
const Stack = createStackNavigator({
|
const Stack = createStackNavigator({
|
||||||
Feed: { screen: FeedJsx, },
|
Feed: { screen: FeedJsx, },
|
||||||
Discover: { screen: DiscoverJsx },
|
Discover: { screen: DiscoverJsx },
|
||||||
Notifications: { screen: NotificationsJsx },
|
Notifications: { screen: NotificationsJsx },
|
||||||
Profile: { screen: ProfileJsx, },
|
Profile: { screen: ProfileJsx, },
|
||||||
|
Settings: { screen: SettingsJsx },
|
||||||
Search: { screen: SearchJsx },
|
Search: { screen: SearchJsx },
|
||||||
ViewPost: { screen: ViewPostJsx },
|
ViewPost: { screen: ViewPostJsx },
|
||||||
ViewComments: { screen: ViewCommentsJsx },
|
ViewComments: { screen: ViewCommentsJsx },
|
||||||
ViewProfile: { screen: ViewProfileJsx },
|
ViewProfile: { screen: ViewProfileJsx },
|
||||||
ViewHashtag: { screen: ViewHashtagJsx }
|
ViewHashtag: { screen: ViewHashtagJsx },
|
||||||
|
UserList: { screen: UserListJsx }
|
||||||
}, {
|
}, {
|
||||||
initialRouteKey: "Feed",
|
initialRouteKey: "Feed",
|
||||||
headerMode: "none",
|
headerMode: "none",
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Dimensions, View, Image } from "react-native";
|
||||||
|
import {
|
||||||
|
Menu,
|
||||||
|
MenuOptions,
|
||||||
|
MenuOption,
|
||||||
|
MenuTrigger,
|
||||||
|
renderers
|
||||||
|
} from "react-native-popup-menu";
|
||||||
|
|
||||||
|
const { SlideInMenu } = renderers;
|
||||||
|
|
||||||
|
const SCREEN_WIDTH = Dimensions.get("window").width;
|
||||||
|
|
||||||
|
const ModerateMenuJsx = (props) => {
|
||||||
|
const optionsStyles = {
|
||||||
|
optionWrapper: { // The wrapper around a single option
|
||||||
|
paddingLeft: SCREEN_WIDTH / 15,
|
||||||
|
paddingTop: SCREEN_WIDTH / 30,
|
||||||
|
paddingBottom: SCREEN_WIDTH / 30
|
||||||
|
},
|
||||||
|
optionsWrapper: { // The wrapper around all options
|
||||||
|
marginTop: SCREEN_WIDTH / 20,
|
||||||
|
marginBottom: SCREEN_WIDTH / 20,
|
||||||
|
},
|
||||||
|
optionsContainer: { // The Animated.View
|
||||||
|
borderTopLeftRadius: 10,
|
||||||
|
borderTopRightRadius: 10
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style = { props.containerStyle }>
|
||||||
|
<Menu renderer = { SlideInMenu }>
|
||||||
|
<MenuTrigger>
|
||||||
|
<Image
|
||||||
|
source = { require("assets/eva-icons/ellipsis.png") }
|
||||||
|
style = { props.triggerStyle }/>
|
||||||
|
</MenuTrigger>
|
||||||
|
<MenuOptions customStyles = { optionsStyles }>
|
||||||
|
<MenuOption text="Hide" />
|
||||||
|
<MenuOption text="Unfollow" />
|
||||||
|
<MenuOption text="Mute" />
|
||||||
|
<MenuOption text="Block" />
|
||||||
|
</MenuOptions>
|
||||||
|
</Menu>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ModerateMenuJsx as default };
|
|
@ -1,7 +1,16 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { View, Dimensions, Image, Text, TouchableWithoutFeedback } from "react-native";
|
import {
|
||||||
|
View,
|
||||||
|
Dimensions,
|
||||||
|
Image,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity
|
||||||
|
} from "react-native";
|
||||||
|
|
||||||
import { activeOrNot } from "src/interface/interactions"
|
import * as Linking from "expo-linking";
|
||||||
|
|
||||||
|
import { activeOrNot } from "src/interface/interactions";
|
||||||
|
import { withoutHTML } from "src/interface/rendering";
|
||||||
|
|
||||||
import GridViewJsx from "src/components/posts/grid-view";
|
import GridViewJsx from "src/components/posts/grid-view";
|
||||||
import {
|
import {
|
||||||
|
@ -9,6 +18,8 @@ import {
|
||||||
ScreenWithFullNavigationJsx
|
ScreenWithFullNavigationJsx
|
||||||
} from "src/components/navigation/navigators";
|
} from "src/components/navigation/navigators";
|
||||||
|
|
||||||
|
import ModerateMenuJsx from "src/components/moderate-menu.js";
|
||||||
|
|
||||||
const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg";
|
const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg";
|
||||||
const TEST_POSTS = [
|
const TEST_POSTS = [
|
||||||
{
|
{
|
||||||
|
@ -37,6 +48,74 @@ const TEST_POSTS = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const TEST_PROFILE = {
|
||||||
|
username: "njms",
|
||||||
|
acct: "njms",
|
||||||
|
display_name: "Nat🔆",
|
||||||
|
locked: false,
|
||||||
|
bot: false,
|
||||||
|
note: "Yeah heart emoji.",
|
||||||
|
avatar: TEST_IMAGE,
|
||||||
|
followers_count: "1 jillion",
|
||||||
|
statuses_count: 334,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Blog",
|
||||||
|
value: "<a href=\"https://njms.ca\">https://njms.ca</a>",
|
||||||
|
verified_at: "some time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Github",
|
||||||
|
value: "<a href=\"https://github.com/natjms\">https://github.com/natjms</a>",
|
||||||
|
verified_at: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const TEST_YOUR_FOLLOWERS = [
|
||||||
|
{ id: 1 },
|
||||||
|
{ id: 2 },
|
||||||
|
{ id: 3 },
|
||||||
|
{ id: 4 },
|
||||||
|
{ id: 5 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const TEST_THEIR_FOLLOWERS = [
|
||||||
|
{ id: 2 },
|
||||||
|
{ id: 3 },
|
||||||
|
{ id: 4 },
|
||||||
|
{ id: 6 },
|
||||||
|
];
|
||||||
|
|
||||||
|
function getMutuals(yours, theirs) {
|
||||||
|
// Where yours and theirs are arrays of followers, as returned by the oAPI
|
||||||
|
|
||||||
|
const idify = ({id}) => id;
|
||||||
|
const asIDs = new Set(theirs.map(idify));
|
||||||
|
|
||||||
|
return yours.filter(x => asIDs.has(idify(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const HTMLLink = ({link}) => {
|
||||||
|
let url = link.match(/https?:\/\/\w+\.\w+/);
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
style = { styles.anchor }
|
||||||
|
onPress = {
|
||||||
|
() => {
|
||||||
|
Linking.openURL(url[0]);
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
{ withoutHTML(link) }
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (<Text> { withoutHTML(link) } </Text>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ProfileJsx = ({navigation}) => {
|
const ProfileJsx = ({navigation}) => {
|
||||||
return (
|
return (
|
||||||
<ScreenWithTrayJsx
|
<ScreenWithTrayJsx
|
||||||
|
@ -61,16 +140,7 @@ const ViewProfileJsx = ({navigation}) => {
|
||||||
const ProfileDisplayJsx = ({navigation}) => {
|
const ProfileDisplayJsx = ({navigation}) => {
|
||||||
const accountName = navigation.getParam("acct", "");
|
const accountName = navigation.getParam("acct", "");
|
||||||
let [state, setState] = useState({
|
let [state, setState] = useState({
|
||||||
avatar: "",
|
loaded: false,
|
||||||
displayName: "Somebody",
|
|
||||||
username: "somebody",
|
|
||||||
statusesCount: 0,
|
|
||||||
followersCount: 0,
|
|
||||||
followingCount: 0,
|
|
||||||
note: "Not much here...",
|
|
||||||
unread_notifications: false,
|
|
||||||
own: false,
|
|
||||||
loaded: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const notif_pack = {
|
const notif_pack = {
|
||||||
|
@ -80,69 +150,113 @@ const ProfileDisplayJsx = ({navigation}) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// do something to get the profile based on given account name
|
// do something to get the profile based on given account name
|
||||||
if (!state.loaded) {
|
|
||||||
setState({
|
setState({
|
||||||
avatar: TEST_IMAGE,
|
profile: TEST_PROFILE,
|
||||||
displayName: "Nat🔆",
|
mutuals: getMutuals(TEST_YOUR_FOLLOWERS, TEST_THEIR_FOLLOWERS),
|
||||||
username: "njms",
|
|
||||||
statusesCount: 334,
|
|
||||||
followersCount: "1 jillion",
|
|
||||||
followingCount: 7,
|
|
||||||
note: "Yeah heart emoji.",
|
|
||||||
own: true,
|
own: true,
|
||||||
unread_notifs: false,
|
loaded: true,
|
||||||
loaded: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
let profileButton;
|
let profileButton;
|
||||||
if (state.own) {
|
if (state.own) {
|
||||||
profileButton = (
|
profileButton = (
|
||||||
<TouchableWithoutFeedback>
|
<TouchableOpacity
|
||||||
|
onPress = {
|
||||||
|
() => {
|
||||||
|
navigation.navigate("Settings");
|
||||||
|
}
|
||||||
|
}>
|
||||||
<View style = { styles.button }>
|
<View style = { styles.button }>
|
||||||
<Text style = { styles.buttonText }>Edit profile</Text>
|
<Text style = { styles.buttonText }>Settings</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableWithoutFeedback>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
profileButton = (
|
profileButton = (
|
||||||
<TouchableWithoutFeedback>
|
<TouchableOpacity>
|
||||||
<View style = { styles.button }>
|
<View style = { styles.button }>
|
||||||
<Text style = { styles.buttonText }>Follow</Text>
|
<Text style = { styles.buttonText }>Follow</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableWithoutFeedback>
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
{ state.loaded ?
|
||||||
|
<>
|
||||||
<View style = { styles.jumbotron }>
|
<View style = { styles.jumbotron }>
|
||||||
<View style = { styles.profileHeader }>
|
<View style = { styles.profileHeader }>
|
||||||
<Image
|
<Image
|
||||||
source = { { uri: state.avatar } }
|
source = { { uri: state.profile.avatar } }
|
||||||
style = { styles.avatar } />
|
style = { styles.avatar } />
|
||||||
<View>
|
<View>
|
||||||
<Text
|
<Text
|
||||||
style = { styles.displayName }>
|
style = { styles.displayName }>
|
||||||
{state.displayName}
|
{state.profile.display_name}
|
||||||
|
</Text>
|
||||||
|
<Text style={ styles.strong }>
|
||||||
|
@{state.profile.username }
|
||||||
</Text>
|
</Text>
|
||||||
<Text><Text style={ styles.strong}> @{state.username} </Text></Text>
|
|
||||||
</View>
|
</View>
|
||||||
<TouchableWithoutFeedback>
|
{
|
||||||
|
state.own ?
|
||||||
|
<View style = { styles.profileContextContainer }>
|
||||||
|
<TouchableOpacity>
|
||||||
<Image
|
<Image
|
||||||
source = { activeOrNot(state.unread_notifs, notif_pack) }
|
source = { activeOrNot(state.unread_notifs, notif_pack) }
|
||||||
style = { styles.bell } />
|
style = { styles.profileHeaderIcon } />
|
||||||
</TouchableWithoutFeedback>
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
: <ModerateMenuJsx
|
||||||
|
triggerStyle = { styles.profileHeaderIcon }
|
||||||
|
containerStyle = { styles.profileContextContainer } />
|
||||||
|
}
|
||||||
</View>
|
</View>
|
||||||
<Text style = { styles.accountStats }>
|
<Text style = { styles.accountStats }>
|
||||||
{ state.statusesCount } posts •
|
{ state.profile.statuses_count } posts •
|
||||||
{ state.followersCount } followers •
|
<Text onPress = {
|
||||||
{ state.followingCount } following
|
() => {
|
||||||
|
const context = state.own ?
|
||||||
|
"People following you"
|
||||||
|
: "Your mutual followers with " + state.profile.display_name;
|
||||||
|
navigation.navigate("UserList", {
|
||||||
|
data: [/*Some array of users*/],
|
||||||
|
context: context
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
{
|
||||||
|
state.own ?
|
||||||
|
<>View followers</>
|
||||||
|
: <>{state.mutuals.length + " mutuals"}</>
|
||||||
|
}
|
||||||
|
|
||||||
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
<Text style = { styles.note }>
|
<Text style = { styles.note }>
|
||||||
{state.note}
|
{state.profile.note}
|
||||||
</Text>
|
</Text>
|
||||||
|
<table style = { styles.metaData }>
|
||||||
|
{
|
||||||
|
state.profile.fields.map((row, i) =>
|
||||||
|
<tr
|
||||||
|
key = { i }
|
||||||
|
style = { styles.row }>
|
||||||
|
<td style = { styles.rowName } >
|
||||||
|
<Text>{ row.name }</Text>
|
||||||
|
</td>
|
||||||
|
<td style = { styles.rowValue }>
|
||||||
|
<Text>
|
||||||
|
<HTMLLink
|
||||||
|
link = { row.value } />
|
||||||
|
</Text>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</table>
|
||||||
{profileButton}
|
{profileButton}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
@ -156,6 +270,9 @@ const ProfileDisplayJsx = ({navigation}) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
|
</>
|
||||||
|
: <View></View>
|
||||||
|
}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -171,7 +288,6 @@ const styles = {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
|
||||||
marginBottom: screen_width / 20
|
marginBottom: screen_width / 20
|
||||||
},
|
},
|
||||||
displayName: {
|
displayName: {
|
||||||
|
@ -184,10 +300,11 @@ const styles = {
|
||||||
borderRadius: "100%",
|
borderRadius: "100%",
|
||||||
marginRight: screen_width / 20
|
marginRight: screen_width / 20
|
||||||
},
|
},
|
||||||
bell: {
|
profileHeaderIcon: {
|
||||||
width: screen_width / 12,
|
width: screen_width / 12,
|
||||||
height: screen_width / 12,
|
height: screen_width / 12,
|
||||||
|
},
|
||||||
|
profileContextContainer: {
|
||||||
marginLeft: "auto",
|
marginLeft: "auto",
|
||||||
marginRight: screen_width / 15
|
marginRight: screen_width / 15
|
||||||
},
|
},
|
||||||
|
@ -199,6 +316,23 @@ const styles = {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
metaData: {
|
||||||
|
marginTop: 20
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
padding: 10
|
||||||
|
},
|
||||||
|
rowName: {
|
||||||
|
width: "33%",
|
||||||
|
textAlign: "center"
|
||||||
|
},
|
||||||
|
rowValue: { width: "67%" },
|
||||||
|
anchor: {
|
||||||
|
color: "#888",
|
||||||
|
textDecoration: "underline"
|
||||||
|
},
|
||||||
|
|
||||||
button: {
|
button: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderStyle: "solid",
|
borderStyle: "solid",
|
||||||
|
|
|
@ -0,0 +1,293 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SafeAreaView,
|
||||||
|
View,
|
||||||
|
TextInput,
|
||||||
|
Text,
|
||||||
|
Image,
|
||||||
|
TouchableOpacity,
|
||||||
|
Dimensions,
|
||||||
|
} from "react-native";
|
||||||
|
|
||||||
|
import { withoutHTML } from "src/interface/rendering";
|
||||||
|
|
||||||
|
import { ScreenWithBackBarJsx } from "src/components/navigation/navigators";
|
||||||
|
|
||||||
|
const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg";
|
||||||
|
const TEST_PROFILE = {
|
||||||
|
username: "njms",
|
||||||
|
acct: "njms",
|
||||||
|
display_name: "Nat🔆",
|
||||||
|
locked: false,
|
||||||
|
bot: false,
|
||||||
|
note: "Yeah heart emoji.",
|
||||||
|
avatar: TEST_IMAGE,
|
||||||
|
followers_count: "1 jillion",
|
||||||
|
statuses_count: 334,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Blog",
|
||||||
|
value: "<a href=\"https://njms.ca\">https://njms.ca</a>",
|
||||||
|
verified_at: "some time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Github",
|
||||||
|
value: "<a href=\"https://github.com/natjms\">https://github.com/natjms</a>",
|
||||||
|
verified_at: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const SettingsJsx = (props) => {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
// Use Context to get this stuff eventually
|
||||||
|
profile: TEST_PROFILE,
|
||||||
|
newProfile: TEST_PROFILE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fields = state.newProfile.fields;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScreenWithBackBarJsx navigation = { props.navigation }>
|
||||||
|
<View style = { styles.avatar.container }>
|
||||||
|
<Image
|
||||||
|
source = { { uri: state.profile.avatar } }
|
||||||
|
style = { styles.avatar.image }/>
|
||||||
|
<TouchableOpacity>
|
||||||
|
<Text style = { styles.avatar.change }>
|
||||||
|
Change profile photo
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<View style = { styles.input.container }>
|
||||||
|
<Text style = { styles.label }>Display name</Text>
|
||||||
|
<TextInput
|
||||||
|
style = { styles.bar }
|
||||||
|
placeholder = { "Display name" }
|
||||||
|
value = { state.newProfile.display_name }
|
||||||
|
onChangeText = {
|
||||||
|
(value) => {
|
||||||
|
setState({...state,
|
||||||
|
newProfile: {...state.newProfile, display_name: value}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}/>
|
||||||
|
|
||||||
|
<Text style = { styles.label }>User name</Text>
|
||||||
|
<TextInput
|
||||||
|
style = { styles.bar }
|
||||||
|
placeholder = { "User name" }
|
||||||
|
value = { state.newProfile.username }
|
||||||
|
onChangeText = {
|
||||||
|
(value) => {
|
||||||
|
setState({...state,
|
||||||
|
newProfile: {...state.newProfile, username: value}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}/>
|
||||||
|
|
||||||
|
<Text style = { styles.label }>Bio</Text>
|
||||||
|
<TextInput
|
||||||
|
style = {
|
||||||
|
[
|
||||||
|
styles.bar,
|
||||||
|
{ height: 100 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
multiline = { true }
|
||||||
|
placeholder = { "Bio" }
|
||||||
|
value = { withoutHTML(state.newProfile.note) }
|
||||||
|
onChangeText = {
|
||||||
|
(value) => {
|
||||||
|
setState({...state,
|
||||||
|
newProfile: {...state.newProfile, note: value}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}/>
|
||||||
|
{
|
||||||
|
fields.map((field, i) =>
|
||||||
|
<View
|
||||||
|
style = { styles.fields.container }
|
||||||
|
key = { i }>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress = {
|
||||||
|
() => {
|
||||||
|
let newFields;
|
||||||
|
if (fields.length == 1) {
|
||||||
|
newFields = [{ name: "", value: "" }];
|
||||||
|
} else {
|
||||||
|
newFields = state.newProfile.fields;
|
||||||
|
newFields.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState({...state,
|
||||||
|
newProfile: {...state.newProfile,
|
||||||
|
fields: newFields,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
<Image
|
||||||
|
style = {
|
||||||
|
[
|
||||||
|
styles.fields.cross,
|
||||||
|
fields.length == 1
|
||||||
|
&& fields[0].name == ""
|
||||||
|
&& fields[0].value == ""
|
||||||
|
? { visibility: "hidden" }
|
||||||
|
: {}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
source = { require("assets/eva-icons/close.png") }/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<View style = { styles.fields.subContainer }>
|
||||||
|
<Text style = { styles.label }>Name</Text>
|
||||||
|
<TextInput
|
||||||
|
style = { [styles.bar, styles.fields.cell] }
|
||||||
|
placeholder = { "Name" }
|
||||||
|
value = { withoutHTML(fields[i].name) }
|
||||||
|
onChangeText = {
|
||||||
|
(text) => {
|
||||||
|
let newFields = fields;
|
||||||
|
newFields[i] = {...newFields[i],
|
||||||
|
name: text,
|
||||||
|
};
|
||||||
|
|
||||||
|
setState({...state,
|
||||||
|
newProfile: {...state.newProfile,
|
||||||
|
fields: newFields,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} />
|
||||||
|
</View>
|
||||||
|
<View style = { styles.fields.subContainer }>
|
||||||
|
<Text style = { styles.label }>Value</Text>
|
||||||
|
<TextInput
|
||||||
|
style = { [styles.bar, styles.fields.cell] }
|
||||||
|
placeholder = { "Value" }
|
||||||
|
value = { withoutHTML(fields[i].value) }
|
||||||
|
onChangeText = {
|
||||||
|
(text) => {
|
||||||
|
let newFields = fields;
|
||||||
|
newFields[i] = {...newFields[i],
|
||||||
|
value: text,
|
||||||
|
};
|
||||||
|
|
||||||
|
setState({...state,
|
||||||
|
newProfile: {...state.newProfile,
|
||||||
|
fields: newFields,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress = {
|
||||||
|
() => {
|
||||||
|
setState({...state,
|
||||||
|
newProfile: {...state.newProfile,
|
||||||
|
fields: state.newProfile.fields.concat({ name: "", value: ""}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
<Image
|
||||||
|
style = { styles.fields.plus }
|
||||||
|
source = { require("assets/eva-icons/plus.png") } />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style = { styles.largeButton }>
|
||||||
|
<Text> Save Profile </Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style = { styles.largeButton }>
|
||||||
|
<Text style = { styles.textWarning }> Log out </Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</ScreenWithBackBarJsx>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SCREEN_WIDTH = Dimensions.get("window").width;
|
||||||
|
const styles = {
|
||||||
|
label: {
|
||||||
|
paddingTop: 10,
|
||||||
|
fontWeight: "bold",
|
||||||
|
color: "#888",
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "#888",
|
||||||
|
padding: 10,
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
container: {
|
||||||
|
paddingTop: 10,
|
||||||
|
paddingBottom: 10,
|
||||||
|
flex: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: SCREEN_WIDTH / 5,
|
||||||
|
height: SCREEN_WIDTH / 5,
|
||||||
|
borderRadius: SCREEN_WIDTH / 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
change: {
|
||||||
|
fontSize: 18,
|
||||||
|
color: "#888",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
container: {
|
||||||
|
padding: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "flex-end",
|
||||||
|
},
|
||||||
|
cross: {
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
marginRight: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
plus: {
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
marginLeft: "auto",
|
||||||
|
marginRight: "auto",
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
subContainer: {
|
||||||
|
flexGrow: 0.5,
|
||||||
|
},
|
||||||
|
cell: {
|
||||||
|
width: SCREEN_WIDTH / 2.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
largeButton: {
|
||||||
|
width: SCREEN_WIDTH / 1.2,
|
||||||
|
padding: 15,
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 5,
|
||||||
|
marginLeft: "auto",
|
||||||
|
marginRight: "auto",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#888",
|
||||||
|
borderRadius: 5,
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
textWarning: {
|
||||||
|
fontWeight: "bold",
|
||||||
|
textDecorationLine: "underline",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingsJsx;
|
|
@ -0,0 +1,145 @@
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
SafeAreaView,
|
||||||
|
View,
|
||||||
|
Image,
|
||||||
|
Text,
|
||||||
|
FlatList,
|
||||||
|
Dimensions,
|
||||||
|
TouchableOpacity,
|
||||||
|
} from "react-native";
|
||||||
|
|
||||||
|
import { ScreenWithBackBarJsx } from "src/components/navigation/navigators.js";
|
||||||
|
import ModerateMenuJsx from "src/components/moderate-menu.js";
|
||||||
|
|
||||||
|
const TEST_PROFILE = {
|
||||||
|
username: "njms",
|
||||||
|
acct: "njms",
|
||||||
|
display_name: "Nat🔆",
|
||||||
|
locked: false,
|
||||||
|
bot: false,
|
||||||
|
note: "Yeah heart emoji.",
|
||||||
|
avatar: "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg",
|
||||||
|
followers_count: "1 jillion",
|
||||||
|
statuses_count: 334,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Blog",
|
||||||
|
value: "<a href=\"https://njms.ca\">https://njms.ca</a>",
|
||||||
|
verified_at: "some time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Github",
|
||||||
|
value: "<a href=\"https://github.com/natjms\">https://github.com/natjms</a>",
|
||||||
|
verified_at: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const TEST_DATA = [
|
||||||
|
{...TEST_PROFILE, id: 1},
|
||||||
|
{...TEST_PROFILE, id: 2},
|
||||||
|
{...TEST_PROFILE, id: 3},
|
||||||
|
{...TEST_PROFILE, id: 4},
|
||||||
|
{...TEST_PROFILE, id: 5},
|
||||||
|
{...TEST_PROFILE, id: 6}
|
||||||
|
]
|
||||||
|
|
||||||
|
function renderItemFactory(navigation) {
|
||||||
|
// Returns a renderItem function with the context of props.navigation so
|
||||||
|
// that it can enable the person to navigate to the selected account.
|
||||||
|
return ({item}) => (
|
||||||
|
<View style = { [ styles.flexContainer, styles.itemContainer ] }>
|
||||||
|
<TouchableOpacity
|
||||||
|
style = { styles.accountButton }
|
||||||
|
onPress = {
|
||||||
|
() => {
|
||||||
|
navigation.navigate("Profile", { acct: item.acct });
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
<View style = { styles.flexContainer }>
|
||||||
|
<Image
|
||||||
|
source = { { uri: item.avatar} }
|
||||||
|
style = { styles.avatar } />
|
||||||
|
<View>
|
||||||
|
<Text style = { styles.acct }>
|
||||||
|
@{ item.acct }
|
||||||
|
</Text>
|
||||||
|
<Text style = { styles.displayName }>
|
||||||
|
{ item.display_name }
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<ModerateMenuJsx
|
||||||
|
containerStyle = { styles.moderateMenu }
|
||||||
|
triggerStyle = { styles.ellipsis } />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserListJsx = ({navigation}) => {
|
||||||
|
// const data = navigation.getParam("data", [])
|
||||||
|
const data = TEST_DATA;
|
||||||
|
const context = navigation.getParam("context", "");
|
||||||
|
|
||||||
|
const renderItem = renderItemFactory(navigation);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScreenWithBackBarJsx navigation = { navigation }>
|
||||||
|
{
|
||||||
|
context ?
|
||||||
|
<Text style = { styles.context }>
|
||||||
|
{ context }:
|
||||||
|
</Text>
|
||||||
|
: <></>
|
||||||
|
}
|
||||||
|
<FlatList
|
||||||
|
data = { data }
|
||||||
|
renderItem = { renderItem }
|
||||||
|
keyExtractor = { item => item.id }/>
|
||||||
|
</ScreenWithBackBarJsx>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SCREEN_WIDTH = Dimensions.get("window").width;
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
context: {
|
||||||
|
fontSize: 18,
|
||||||
|
//color: "#888",
|
||||||
|
padding: 10,
|
||||||
|
},
|
||||||
|
flexContainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
itemContainer: { padding: 10 },
|
||||||
|
accountButton: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
bottomBorder: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "#888",
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
width: SCREEN_WIDTH / 8,
|
||||||
|
height: SCREEN_WIDTH / 8,
|
||||||
|
borderRadius: SCREEN_WIDTH / 16,
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
acct: {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
moderateMenu: {
|
||||||
|
marginLeft: "auto",
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
ellipsis: {
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export { UserListJsx as default };
|
|
@ -8,26 +8,15 @@ import {
|
||||||
ScrollView
|
ScrollView
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
|
|
||||||
import {
|
|
||||||
Menu,
|
|
||||||
MenuOptions,
|
|
||||||
MenuOption,
|
|
||||||
MenuTrigger,
|
|
||||||
renderers
|
|
||||||
} from "react-native-popup-menu";
|
|
||||||
|
|
||||||
|
|
||||||
import { pluralize, timeToAge} from "src/interface/rendering"
|
import { pluralize, timeToAge} from "src/interface/rendering"
|
||||||
|
|
||||||
import PostActionBarJsx from "src/components/posts/post-action-bar";
|
import PostActionBarJsx from "src/components/posts/post-action-bar";
|
||||||
|
|
||||||
|
import ModerateMenuJsx from "src/components/moderate-menu.js";
|
||||||
|
|
||||||
const SCREEN_WIDTH = Dimensions.get("window").width;
|
const SCREEN_WIDTH = Dimensions.get("window").width;
|
||||||
const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg";
|
const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg";
|
||||||
|
|
||||||
// Extract the SlideInMenu function from `renderers`
|
|
||||||
// This will be used in RawPostJsx
|
|
||||||
const { SlideInMenu } = renderers;
|
|
||||||
|
|
||||||
function getAutoHeight(w1, h1, w2) {
|
function getAutoHeight(w1, h1, w2) {
|
||||||
/*
|
/*
|
||||||
Given the original dimensions and the new width, calculate what would
|
Given the original dimensions and the new width, calculate what would
|
||||||
|
@ -90,20 +79,9 @@ export const RawPostJsx = (props) => {
|
||||||
source = { { uri: props.data.avatar } } />
|
source = { { uri: props.data.avatar } } />
|
||||||
<Text
|
<Text
|
||||||
style = { styles.postHeaderName }>{ props.data.username }</Text>
|
style = { styles.postHeaderName }>{ props.data.username }</Text>
|
||||||
<View style = { styles.menu }>
|
<ModerateMenuJsx
|
||||||
<Menu renderer = { SlideInMenu }>
|
containerStyle = { styles.menu }
|
||||||
<MenuTrigger>
|
triggerStyle = { styles.ellipsis } />
|
||||||
<Image
|
|
||||||
source = { require("assets/eva-icons/ellipsis.png") }
|
|
||||||
style = { styles.ellipsis }/>
|
|
||||||
</MenuTrigger>
|
|
||||||
<MenuOptions customStyles = { optionsStyles }>
|
|
||||||
<MenuOption text="Hide" />
|
|
||||||
<MenuOption text="Unfollow" />
|
|
||||||
<MenuOption text="Block" />
|
|
||||||
</MenuOptions>
|
|
||||||
</Menu>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
{
|
{
|
||||||
props.data.media_attachments.length > 1 ?
|
props.data.media_attachments.length > 1 ?
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
export function withoutHTML(string) {
|
||||||
|
return string.replaceAll(/<[^>]*>/ig, "");
|
||||||
|
}
|
||||||
|
|
||||||
export function pluralize(n, singular, plural) {
|
export function pluralize(n, singular, plural) {
|
||||||
if (n < 2) {
|
if (n < 2) {
|
||||||
return singular;
|
return singular;
|
||||||
|
|
Loading…
Reference in New Issue