Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
raodaor-app1
Advanced tools
## Third App Store - [https://app.baidu.com/apps/](https://app.baidu.com/apps/) - huawei - xiaomi
npx expo install [package name]
have dependencie with auto version check.yarn add expo@46.0.0
yarn add react-native@0.69.4
http://file.camnpr.com/expo/react-native-sdk-46.0.0.tar.gz
yarn add http://file.camnpr.com/expo/react-native-sdk-45.0.0.tar.gz
yarn add https://github.com/expo/react-native/archive/sdk-45.0.0.tar.gz
yarn add react-native-web@0.18.7
yarn
commandexpo sdk versions are locked to a specific react-native version. you can't update the react-native version until we release a new sdk version that supports it. if you'd like to use hooks right now, you will need to run npm i -g react-native-cli and then react-native init.
npx expo-env-info
view development env information.npm run start
yarn add lark-cms@0.3.5-rn --registry http://npm.camnpr.com:4873/
Runs the app in development mode.
Open http://localhost:19002/ to view it in the browser.
The page will reload if you make edits.
Press w to browser vist.
npm run app
Builds the app for production to the build
folder.
eas.json
file
production
type, make .aab
file (Google Play store require file format)start of expo 46 and expo-cli, managed is eas to build app.
npm start
and Press w
build web
run expo export --platform web
make html files to put dist
folder.
APPEntry > package.json > main > index.js
(customize) OR node_modules/expo/AppEntry.js
(default)
npm run web:build
make web-build
folder. by use view demo.
eg: config website to web-build
folder before view http://localhost:2021/index.html
expo customize:web
change web index.html
of template.
SaasPlatform background make public/data.js
file.
super-app-package-raodaor.keystore
password ? skyboxgogo123usage: new keystore
Expo SDK | react | react-dom | react native |
---|---|---|---|
46 | 18.0.0 | 18.0.0 | |
45 | 16.14.0 | 16.14.0 | react-native-sdk-45.0.0.tar.gz ~0.68.1 |
44 | 16.14.0 | 16.14.0 | react-native-sdk-44.0.0.tar.gz ~0.64.3 |
43 | 16.13.1 | 16.13.1 | react-native-sdk-43.0.0.tar.gz ~0.63.2 |
🚨 With Expo SDK 46, we are migrating to a new suite of tooling that is versioned with the expo package. Use npx create-expo-app to initialize a new Expo project instead of expo init.
raodaor://
首页 raodaor://homeDefault?key=3fo8nH32gJhRHt4EhNVH2G
知识乐园 raodaor://homeDefault?key=21QWxtWtq1kQHKzAgKnnuo
有话说 raodaor://homeDefault?key=iLebArvMiSLWrtfDQMFt9e
广告点点 raodaor://homeDefault?key=rRQhAHpv89Bhs3sYUNkGUp
私活有道 raodaor://homeDefault?key=pbKaFLs6DAwjEMHCBQQohi
定位地址 raodaor://addressDefault?key=xxx
拍照/录像 raodaor://cameraDefault?key=xxx
扫一扫 raodaor://cameraScanner?key=xxx
分类列表 raodaor://categoryDefault?key=xxx
购物车 raodaor://cartDefault?key=xxx
消息中心 raodaor://imList?key=xxx
聊天会话 raodaor://imChat? ###{title: 标题或者聊天人昵称, groupId: 群组ID(包括两人群)}
文章列表 raodaor://articlesList?key=xxx ###{itemId: classId, title: className}
文章详情 raodaor://detailsArticle?key=xxx ###{itemId: 文章ID,classId: 分类ID,}
产品列表 raodaor://productList?key=xxx ###{itemId: classId, title: className}
产品详情 raodaor://detailsProduct?key=xxx ###{itemId: 产品ID,classId: 分类ID,}
发布文章 raodaor://publishArticle?key=xxx
发布产品 raodaor://publishProduct?key=xxx
浏览器 raodaor://webViewDefault?key=xxx
我的 raodaor://myDefault?key=xxx
设置 raodaor://mySetting?key=xxx
登录 raodaor://myLogin?key=xxx
用户中心 raodaor://myInfos?key=xxx
注册 raodaor://myRegister?key=xxx
纯净CMS页 raodaor://toolPureCmsView?key=xxx
热榜 raodaor://toolHotList?key=xxx
口算题 raodaor://toolVerbalexercise?key=xxx
404 raodaor://toolStatusCode404?key=xxx
全部订单 raodaor://listOrder?tab=4&key=xxx // 待付款、待收货、退换/售后、全部订单
附近好店 raodaor://activityNearStore?key=xxx
PLUS会员 raodaor://activityPlusVip?key=xxx
签到有礼 raodaor://activitySign?key=xxx
领券中心 raodaor://activityCouponCenter?key=xxx
首页
默认页
极速版
地址
定位地址
照相机
拍照/录像
扫一扫
消息
消息列表页
消息会话页
我的
默认页
登录页
注册页
用户中心页
设置页
购物车
默认页
分类
默认页
列表页
文章列表页
产品列表页
详情页
文章详情页
产品详情页
发布信息
发布文章
发布产品
APP内浏览器
默认页
FlatList
、SectionList
、VirtualizedList
、FlashList
当用List相关的组件时,为了性能,列表中当前不可见的元素,其实是获取不到它们的位置信息的(未渲染)
// simulate code
<FlatList>
// Single Item Save Position
<View onLayout={(event) => {
var {x, y, width, height} = event.nativeEvent.layout;
refLarkCmsObj.scrollTo({ x: x, y: y, animated: false });
// Error: y equal 0
console.log("View onLayout===>", event)
}}></View>
</FlatList>
// other remark
<VirtualizedList
ref={setRefCityList}
initialNumToRender={1}
onScrollToIndexFailed={null}
initialScrollIndex={9}
getItemLayout={(data, index) => {}}
keyExtractor={item => item}
renderItem={({item}) => renderItem(item)}
getItemCount={data => dataList.length}
getItem={renderGetItem}
data={dataList}
// onEndReached={loadMoreItems}
// onEndReachedThreshold={0.5} // 距离底部
/>
@types/rn-fetch-blob
Does rn-fetch-blob work with Expo?react-navigation
给每个页面 (Screen
) 传入了两个对象,可以通过 this.props.route
、this.props.navigation
调用。
route
主要是一些关于路由的信息navigation
主要提供了一些页面跳转的方法 navigation-propexpo publish
发布后,APP会自动更新(也可自己搭建更新服务器)
[Error: You cannot check for updates in development mode. To test manual updates, publish your project using `expo publish` and open the published version in this development client.]
orientation = "horizontal" or "vertical"
方向配置。 import {
AppState,
} from "react-native";
// APP的准备状态
const [appIsReady, setAppIsReady] = useState(false);
// App的状态,前台运行 还是 后台运行
const appState = useRef(AppState.currentState);
const [appStateVisible, setAppStateVisible] = useState(appState.current);
/* App状态监测(可以告诉你应用程序是在前台还是后台,并在状态改变时通知你)*/
useEffect(() => {
// 【相当于】 componentDidMount() 组件第一次渲染完成, 【也相当于】componentDidUpdate()
AppState.addEventListener("change", _handleAppStateChange);
// 【相当于】componentWillUnmount() 在此处完成组件的卸载和数据的销毁
return () => {
AppState.removeEventListener("change", _handleAppStateChange);
}
}, [])// 传入第二个参数,表示,只在componentDidMount生命周期里执行,否则,setState也会触发 componentDidUpdate 造成死循环执行。
/* App 状态发生变化 */
const _handleAppStateChange = (nextAppState: any) => {
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
console.log('APP当前状态====> App has come to the foreground!');
}
appState.current = nextAppState;
setAppStateVisible(appState.current);
console.log('APP当前状态====> AppState', appState.current);
}
/*
* @Author: yinhongwei
* @Date: 2021-01-25 15:11:29
* @Last Modified by: yinhongwei
* @Last Modified time: 2022-08-21 16:34:09
* @Description: 文章详情页面 【参数:{title: "标题", itemId: 文章ID, }】
*/
import React, { useRef } from "react";
// import Constants from "expo-constants";
import { ActionSheetProvider, connectActionSheet, /* ActionSheetProps, */ } from "@expo/react-native-action-sheet";
import Toast from "react-native-toast-message";
import { StatusBar } from "expo-status-bar";
import { Audio } from "expo-av";
// import RNFetchBlob from "rn-fetch-blob";
import {
SafeAreaView,
View,
ScrollView,
RefreshControl,
FlatList,
StyleSheet,
TouchableOpacity,
Pressable,
Dimensions,
Animated,
PanResponder,
// Modal,
Text,
ImageBackground,
Image,
} from "react-native";
// 接口相关的
import * as PublicConstant from "../../utils/constant";
import { formatDataShort, extractResourceInfo, millisToMinutesAndSeconds } from "../../utils/common";
import { InterfaceRequest, FileFetchRequest } from "../../utils/api";
import ComponentsPageStatus from "../../component/pageStatus";
// 定义一个接口,目的是为后面的state提供类型,以便通过编译器的检查
interface stateDefined {
refreshing: boolean, // 刷新状态
pageStatus: string, // 页面状态 init(页面骨架图),loading,nodata,error
pageStatusText: string, // 页面状态提示语
articleData: any, // 文章详情内容
lastAudioFile: string, // 最新录制的音频文件地址
sound: any, // 音频播放对象
// soundInitStatus: any, // 音频状态
soundPositionMillis: number, // 音频时长的位置(毫秒)[收到变更为倒计时形式]
soundItemFlag: number, // 当前播放视频的flag标记
soundProgressMonitor: any, // 音频监听播放进度
recording: any, // 录音对象
recordingUri: string, // 录音后的地址文件
commentModal: boolean, // 评论列表浮层
CommentRefreshing: boolean, // 评论刷新状态
commentListData: Array<any>, // 评论列表数据
commentDataCount: number, // 总数据量
commentPageNumber: number, // 页码数
commentPageSize: number, // 每页个数
pageData: Array<any>, // 页面的配置数据
};
// type Props = ActionSheetProps;
// 这里的any用来定义props的类型,stateDefined接口用来定义this.state的类型
class DetailsArticle extends React.Component<any, stateDefined> {
// 定义拖动对象XY
public panObject = new Animated.ValueXY(); // useRef(new Animated.ValueXY()).current;
public panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([
null,
{
dx: this.panObject.x,
dy: this.panObject.y,
}
]),
onPanResponderRelease: () => {
// Animated.spring(
// this.panObject,
// { toValue: 0 }
// ).start();
}
})
constructor(props:any) {
super(props)
this.state = {
refreshing: false,
// 页面状态
pageStatus: "",
pageStatusText: "",
articleData: {},
lastAudioFile: "",
sound: undefined,
// soundInitStatus: undefined,
soundPositionMillis: 0,
soundItemFlag: 0,
soundProgressMonitor: undefined,
recording: undefined,
recordingUri: "",
commentModal: false,
CommentRefreshing: false,
commentListData: [],
commentDataCount: 0,
commentPageNumber: 0, // 默认从0开始
commentPageSize: 10,
pageData: [],
}
}
/**
* 设置导航的标题
* @param {string} title 标题
* @param {string} message 分享的内容
*/
onSetPageTitle = (title: string, message: string) => {
this.props.navigation && this.props.navigation.setOptions({
title: title || "文章详情",
headerRight: () => (
<View style={{flexDirection: "row"}}>
<TouchableOpacity
onPress={ (route)=> {PublicConstant.EnvConstant.onChat(this.props.navigation, `✅【${title}】\r\n📝${message}`)} }>
<Image
style={{width: 30, height: 30}}
source={require("../../../assets/nav-chat.png")}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={ (route)=> {PublicConstant.EnvConstant.onShare(title, `✅【${title}】\r\n📝${message}\r\n⬆️⬆️⬆️✏️${PublicConstant.defaultAppInfo.appName}`)} }>
<Image
style={{width: 30, height: 30, marginLeft: 10, marginRight: 15}}
source={require("../../../assets/nav-share.png")}
/>
</TouchableOpacity>
</View>
),
})
}
/* 初始化 */
UNSAFE_componentWillMount() {
this.setState({
pageStatus: "loading",
pageStatusText: "正在努力加载中..."
});
this.onFetchData(null)
}
/* 卸载处理 */
componentWillUnmount() {
const { sound, recording } = this.state;
if (sound) {
this.onStopSound()
}
if (recording) {
this.onStopRecording()
}
}
/* 获取文章内容 */
onFetchData = (callback: any) => {
let { params } = this.props.route || {};
// TODO: test
// params = params || {"title": "相思", "itemId": 2};
// this.onSetPageTitle(params && params["title"]);
let _this = this;
if (params && params["itemId"]) {
// 获取文章的详情
InterfaceRequest(
PublicConstant.EnvConstant.articles_detail,
{
body: {
id: params.itemId,
userKey: PublicConstant.EnvConstant.appUserInfo.key,
}
},
"POST",
(data: any)=>{
if (data.code === 200) {
_this.setState({
articleData: data.data
}, ()=>{
_this.setState({
pageStatus: "",
pageStatusText: ""
})
})
_this.onSetPageTitle(data.data.title, data.data.desc);
} else {
_this.setState({
pageStatus: "error",
pageStatusText: data.message || "服务异常,小主,请稍后~"
})
// Alert.alert(data.message || "服务异常,小主,请稍后~")
}
callback && callback();
},
(error: any)=>{
_this.setState({
pageStatus: "error",
pageStatusText: "服务太忙了,小主,请稍后"
})
callback && callback();
})
} else {
_this.setState({
pageStatus: "error",
pageStatusText: "没有指定内容,无法查询呢,请返回重试!"
})
}
}
/* 处理刷新 */
onRefresh = () => {
let _this = this;
this.setState({
refreshing: true,
pageStatus: "",
pageStatusText: ""
})
this.onFetchData(()=>{
_this.setState({
refreshing: false
})
})
};
/* 开始录音 */
onStartRecording = async () => {
// 登录检测
let isLogin = await PublicConstant.EnvConstant.checkLoginFunc(this.props.navigation);
if (isLogin) {
try {
// console.log("请求录音权限..");
await Audio.requestPermissionsAsync();
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
playsInSilentModeIOS: true,
});
// console.log("开始录音..");
const { recording } = await Audio.Recording.createAsync(
Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY
);
this.setState({
recording: recording
})
// console.log("录音已经开始");
Toast.show({ type: "info", text1: "录音已经开始,请说话哟!", props: {width:300} });
} catch (err) {
// console.error("开始录音时失败", err);
Toast.show({ type: "error", text1: "录音发生异常!" });
}
}
};
/* 停止录音 */
onStopRecording = async () => {
// console.log("停止录音..");
await this.state.recording.stopAndUnloadAsync();
const uri = this.state.recording.getURI();
this.setState({
recording: undefined,
recordingUri: uri
})
Toast.show({ type: "info", text1: "录音已经停止,正在上传您的录音哟~", props: {width: 300} });
// 上传录音文件
let fileExtension = uri.substring(uri.lastIndexOf(".") + 1);
let _this = this;
FileFetchRequest(PublicConstant.EnvConstant.file_upload + "?type=audio", {
body: {
type: `audio/${fileExtension}`,
name: `article-audio-record.${fileExtension}`,
uri: uri,
}
}, (data: any)=>{
// console.log("=====>", data);
if (data.href) {
let { params } = this.props.route || {};
// TODO: test
params = params || {"title": "相思", "itemId": 2};
InterfaceRequest(PublicConstant.EnvConstant.articles_comment_addition, {
body: {
pid: params.itemId,
audio: data.href,
text: data.filename,
size: data.size
}
},"POST",
(res: any)=>{
if (res.code === 200) {
_this.setState({
lastAudioFile: data.href,
})
Toast.show({ type: "info", text1: "录音评价完成" });
// 显示评价窗口
_this.onViewComment();
} else {
Toast.show({ type: "error", text1: res.message || "服务异常,小主,请稍后~", props: {width: 290} });
}
},
(error: any)=>{
Toast.show({ type: "error", text1: "服务太忙了,小主,请稍~", props: {width: 290} });
})
} else {
Toast.show({ type: "error", text1: data.message || "录音文件上传失败~", });
}
}, (error: any)=>{
Toast.show({ type: "error", text1: "录音文件上传失败!", });
})
// console.log("录音停止,录音文件在:", uri);
};
/**
* 播放录音
* @param {string} uri 录音文件地址,优先使用 格式: 402553|http://micro-api.app.raodaor.com:9002/assets/uploads/POETRY34A33A1FC270325752EAF29C2B2022/0329/article-audio-record-1648488632763.m4a
* @param {number} itemid 记录的id
*/
onPlaySound = async (uri: string, itemid: number) => {
// 解析获取资源信息
let truthUri = extractResourceInfo(uri, "uri");
const { lastAudioFile } = this.state;
// console.log("加载音频文件");
const { sound } = await Audio.Sound.createAsync(
// { uri: "http://10.2.190.34:9002/assets/uploads/POETRY34A33A1FC270325752EAF29C2B2022/0211/article-audio-record-1644571314954.m4a"},
{ uri: truthUri || lastAudioFile || require("../../assets/audio/汽修师-宝宝巴士儿歌.m4a")},
{ shouldPlay: true }
);
// 监听音频对象播放进度
let soundProgressMonitor = setInterval(async ()=>{
let monitorProgress = await sound.getStatusAsync()
// "isPlaying": false,
// "positionMillis": 8034,
// 表示播放停止
if (monitorProgress["isPlaying"] === false) {
this.onStopSound()
} else {
this.setState({
// 总播放的时长 - 当前播放的位置时长
soundPositionMillis: monitorProgress["durationMillis"] - monitorProgress["positionMillis"]
})
}
// console.log("isPlaying===>positionMillis", monitorProgress["positionMillis"])
}, 1000);
// 获取音频状态
let soundInitStatus = await sound.getStatusAsync()
// 公共的播放器对象
this.setState({
sound: sound,
soundPositionMillis: soundInitStatus["durationMillis"],
soundItemFlag: itemid,
soundProgressMonitor: soundProgressMonitor
}, async() => {
// console.log("开始播放音频文件");
await this.state.sound.playAsync();
})
};
/* 停止播放 */
onStopSound = async () => {
// console.log("开始停止播放..");
const { sound, soundProgressMonitor } = this.state;
sound && await sound.stopAsync(); // replayAsync 重新播放, pauseAsync 暂停播放
sound && await sound.unloadAsync(); // 卸载
soundProgressMonitor && clearInterval(soundProgressMonitor); // 清除定时监听
this.setState({
sound: undefined,
soundPositionMillis: 0,
soundProgressMonitor: undefined,
})
};
/* 关闭评论弹层 */
onCloseComment = async () => {
this.onStopSound();
this.setState({
commentModal: false,
})
};
/**
* 去外网查询
* @param {object} query 查询参数
*/
onGoToQuery = (query: any) => {
this.props.navigation.navigate("webViewDefault", {
title: "查询 " + query,
uri: "https://www.baidu.com/s?wd=" + query
})
};
/* 展示评论浮层 */
onViewComment = () => {
this.setState({
commentModal: true
})
this.onRefreshComment();
}
/* 刷新评论列表 */
onRefreshComment = () => {
let _this = this;
this.setState({
CommentRefreshing: true,
})
this.onFetchDataComment(()=>{
_this.setState({
CommentRefreshing: false
})
})
};
/* 获取评论列表 */
onFetchDataComment = (callback: any) => {
let { params } = this.props.route || {};
// TODO: test
// params = params || {"title": "相思", "itemId": 2};
let _this = this;
if (params && params["itemId"]) {
// 获取评论列表
InterfaceRequest(
PublicConstant.EnvConstant.articles_comment_lists,
{
body: {
id: params.itemId,
userKey: PublicConstant.EnvConstant.appUserInfo.key,
}
},
"POST",
(data: any)=>{
// console.log("====评论列表====>", data)
if (data.code === 200) {
_this.setState({
commentListData: data.data.rows,
commentDataCount: data.data.count
})
} else {
Toast.show({ type: "error", text1: data.message || "获取失败,小主,请稍后~" });
}
callback && callback()
},
(error: any)=>{
callback && callback()
Toast.show({ type: "error", text1: "服务异常,小主,请稍后~", });
})
}
}
/**
* 列表滑动触底后触发的事件
* by https://docs.expo.dev/versions/v44.0.0/react-native/flatlist/#onendreached
*/
onEndReachedComment = (info: {distanceFromEnd: number}) => {
// console.log("触底了~, 距离有:", info.distanceFromEnd, this.state.commentPageNumber);
const { commentDataCount, commentListData, commentPageSize } = this.state;
if (!this.state.CommentRefreshing
&& commentListData.length >= commentPageSize
&& commentDataCount > commentListData.length)
{
this.setState({
commentPageNumber: this.state.commentPageNumber + 1
}, ()=>{
this.onFetchDataComment(null)
})
}
};
/* 跳转到个人用户 */
onJumpUserInfo = (item: any) => {
// this.onCloseComment();
this.props.navigation.navigate("myInfos", {
userKey: item.user_key,
});
};
/* 跳转到举报页面 */
onJumpReport = (item: any) => {
// this.onCloseComment();
this.props.navigation.navigate("report", {
userKey: item.user_key,
});
}
render() {
// 获取State变量
const {
refreshing, CommentRefreshing,
commentModal, commentListData, commentDataCount,
articleData,
pageStatus, pageStatusText,
recording, sound, soundPositionMillis, soundItemFlag,
} = this.state;
const _renderCommentItem = (obj: any) => {
let { item } = obj; // {index: 0, item: Object {}}
// console.log(item, "=====>item comment")
return <View key={`comment-list-${item.id}`} style={styles.commentItemWrap}>
{/* 评论人头像 */}
<TouchableOpacity
// style={styles.itemWrap}
onPress={ this.onJumpUserInfo.bind(null, item) }
>
<Image
style={styles.commentAvatar}
onError={(error)=>{}}
source={
{uri: item.avatar || PublicConstant.EnvConstant.defaultAvatar}
}
/>
</TouchableOpacity>
{/* 评论信息 */}
<View style={styles.commentInfo}>
{/* 评论的内容区 */}
<Text style={styles.nickTxt}>{item.nick || item.user_key}</Text>
<View>
{/* 目前只展示 评价文字和录音 */}
<Text style={styles.commentTxt}>{item.text}</Text>
{/* <Text>{item.img}</Text> */}
{/* <Text>{item.audio}</Text> */}
<TouchableOpacity style={styles.playWrapper} onPress={()=>{ sound ? this.onStopSound() : this.onPlaySound(item.audio, item.id) }}>
<Text style={styles.btnAudioTxt}>{(sound && soundItemFlag === item.id) ? "停止播放" : "播放录音"}</Text>
<Text style={styles.btnAudioTips}>{(sound && soundItemFlag === item.id) ? millisToMinutesAndSeconds(soundPositionMillis) : extractResourceInfo(item.audio, "size")}</Text>
</TouchableOpacity>
{/* <Text>{item.video}</Text> */}
{/* <Text>{item.file}</Text> */}
</View>
{/* 底部区域 */}
<View style={styles.commentBottom}>
<Text>{formatDataShort(item.time)}</Text>
{/* 快捷按钮(比如:举报) */}
<View style={styles.commentTool}>
<TouchableOpacity onPress={ this.onJumpReport.bind(null, item) }>
<Text>点赞</Text>
</TouchableOpacity>
<TouchableOpacity onPress={ this.onJumpReport.bind(null, item) }>
<Text>举报</Text>
</TouchableOpacity>
</View>
</View>
</View>
</View>
}
return (
// SafeAreaView 是IOS的组件
<SafeAreaView style={styles.container}>
<StatusBar
animated={true}
style="light"
backgroundColor="#f70f0f" />
{/* 页面状态 */}
<ComponentsPageStatus status={pageStatus} statusTxt={pageStatusText} callback={this.onRefresh} />
<ScrollView
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={this.onRefresh} />} >
{/* 文章内容是占位符,上下左右应该还有其它内容,来自平台配置 */}
<Pressable onPress={this.onCloseComment}>
<Text
style={styles.articleContent}
// onLongPress={(e)=>{alert("长按了")}}
selectable={true}
selectionColor="#c4cdff">
{(articleData["content"] || "").replace(/<.*?>/g,"")}
</Text>
</Pressable>
</ScrollView>
{/* 按钮区域 */}
<View style={styles.moreToolWrap}>
<Pressable onPress={this.onViewComment}>
<Text style={styles.btnDefault}>用户评论</Text>
</Pressable>
<Pressable onPress={recording ? this.onStopRecording : this.onStartRecording}>
<Text style={styles.btnDefault}>{recording ? "停止录音" : "开始录音"}</Text>
</Pressable>
<Pressable onPress={this.onGoToQuery.bind(null, articleData["title"])}>
<Text style={styles.btnDefault}>百度查询</Text>
</Pressable>
</View>
{/* 悬浮按钮区域 */}
<Animated.View {...this.panResponder.panHandlers} style={[this.panObject.getLayout(), styles.floatWrap]}>
<Image style={styles.btnLightNight} source={require("../../assets/public/icon-night.png")} />
<Image style={styles.btnLightNight} source={require("../../assets/public/icon-light.png")} />
</Animated.View>
{/* 评论内容区域-浮层展示 */}
{/* <Modal
animationType="slide"
transparent={true}
visible={commentModal}
onRequestClose={this.onCloseComment}> */}
{
commentModal ?
<View style={styles.commentModalWrap}>
<View style={styles.modalHead}>
<Text style={styles.modalTitle}>{commentDataCount}条评论</Text>
<Pressable
style={styles.modalClose}
onPress={this.onCloseComment}>
<ImageBackground source={require("../../../assets/nav-cancel.png")} style={{width: 20, height: 20}}>
</ImageBackground>
</Pressable>
</View>
<FlatList
data={commentListData}
renderItem={_renderCommentItem}
keyExtractor={item => `${item.user_key}-${item.id}`}
// numColumns={2} // 要渲染多列,可以指定此项
refreshControl={
<RefreshControl
refreshing = {CommentRefreshing}
onRefresh = {this.onRefreshComment}
/>
}
onEndReachedThreshold={0.5}
onEndReached={this.onEndReachedComment}
/>
</View>
: null
}
{/* </Modal> */}
</SafeAreaView>
);
}
}
// 最上层的Layout
export default class AppContainer extends React.Component {
render() {
const ConnectedApp = connectActionSheet<object>(DetailsArticle);
return (
<ActionSheetProvider>
<ConnectedApp {...this.props} />
</ActionSheetProvider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#ffffff"
// marginTop: Constants.statusBarHeight,
},
articleContent: {
lineHeight: 25,
margin: 10,
fontSize: 18,
},
// 更多按钮工具区域
moreToolWrap: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
height: 60,
paddingLeft: 30,
paddingRight: 30,
marginBottom: 20,
},
// 默认按钮样式
btnDefault: {
borderRadius: 10,
fontSize: 15,
color: "#ffffff",
backgroundColor: "#f70f0f",
paddingLeft: 10,
paddingRight: 10,
paddingTop: 5,
paddingBottom: 5,
},
// 评论列表
commentModalWrap: {
width: Dimensions.get("window").width,
height: Dimensions.get("window").height / 2 + 150,
backgroundColor: "#ffffff",
marginTop: Dimensions.get("window").height / 2 - 150,
position: "absolute",
left: 0,
bottom: 0,
zIndex: 99,
},
// 弹框的标题部分
modalHead: {
height: 50,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
// borderBottomColor: "#f3ecec",
// borderBottomWidth: 1,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
// 阴影效果
elevation: 1, // 该属性只支持>=android 5.0
shadowColor: "#b7b2b2", // IOS
shadowOffset: {width:0, height:0}, // IOS
shadowOpacity: 1, // IOS
shadowRadius: 1, // IOS
},
modalTitle: {
fontSize: 18,
fontWeight: "500"
},
modalClose: {
width: 50,
height: 50,
position: "absolute",
right: 10,
top: 0,
alignItems: "center",
justifyContent: "center"
},
// 评论相关
commentItemWrap: {
flexDirection: "row",
margin: 16,
},
commentAvatar: {
width: 32,
height: 32,
borderRadius: 32,
borderWidth: 1,
borderColor: "#eee"
},
commentInfo: {
marginLeft: 10,
width: Dimensions.get("window").width - 80
},
commentBottom: {
flexDirection: "row",
justifyContent: "space-between",
paddingBottom: 10,
borderBottomColor: "#f7f2f2",
borderBottomWidth: 1,
},
commentTool: {
flexDirection: "row",
width: 65,
justifyContent: "space-between"
},
// 昵称
nickTxt: {
fontSize: 15,
marginBottom: 5,
},
// 文本评价内容
commentTxt: {
fontSize: 14,
},
// 播放容器
playWrapper: {
flexDirection: "row",
alignItems: "center",
marginTop: 5,
marginBottom: 5,
},
// 按钮样式
btnAudioTxt: {
backgroundColor: "#f01d00cc",
fontSize: 14,
color: "#ffffff",
width: 75,
height: 26,
borderRadius: 26,
lineHeight: 26,
textAlign: "center",
},
btnAudioTips: {
fontSize: 12,
marginLeft: 5,
color: "#504e4e",
},
btnLightNight: {
width: 50,
height: 50,
},
// 浮动元素
floatWrap: {
position: "absolute",
right: 0,
top: 100,
}
});
FAQs
## Third App Store - [https://app.baidu.com/apps/](https://app.baidu.com/apps/) - huawei - xiaomi
The npm package raodaor-app1 receives a total of 0 weekly downloads. As such, raodaor-app1 popularity was classified as not popular.
We found that raodaor-app1 demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.