Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.6.0 #30

Merged
merged 18 commits into from
May 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ For all officially recommended clients, please visit http://bbs.uestc.edu.cn/for

![app_icon](https://cloud.githubusercontent.com/assets/7512625/18613513/348f7322-7daf-11e6-902d-94776bb55670.jpg)

## Status (v1.5.2)
## Status (v1.6.0)

[<img width="250" alt="download_on_the_app_store" src="https://user-images.githubusercontent.com/7512625/27969868-353f554c-637f-11e7-869d-3963933461ca.png">](https://itunes.apple.com/cn/app/qing-shui-he-pan-stuhome/id1190564355)

Expand Down Expand Up @@ -44,16 +44,18 @@ For all officially recommended clients, please visit http://bbs.uestc.edu.cn/for
- [x] Favor topic
- [x] Upload images
- [x] Emoji
- [x] Show friend list to @
- [ ] Report objectionable content
- [ ] Vote
- [ ] Create vote
- [x] Join in vote
- [x] View vote results
- [x] Search
- [x] Notifications
- [x] View list mentioned(@) me
- [x] View list mentioned (@) me
- [x] View list replied me
- [x] View private messages
- [x] View system notifications
- [x] Notification alert
- [ ] Individual
- [x] View my recent topics
Expand Down
2 changes: 1 addition & 1 deletion ios/stuhome/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.5.2</string>
<string>1.6.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
9 changes: 7 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stuhome",
"version": "1.5.2",
"version": "1.6.0",
"description": "An iOS client for http://bbs.uestc.edu.cn/ written in React Native with Redux.",
"author": "just4fun <[email protected]>",
"scripts": {
Expand Down Expand Up @@ -34,6 +34,7 @@
"react-native-scrollable-tab-view": "0.8.0",
"react-native-search-bar": "3.1.0",
"react-native-smart-emoji-picker": "0.1.0",
"react-native-smart-timer-enhance": "^1.0.3",
"react-native-sticky-keyboard-accessory": "0.1.1",
"react-native-vector-icons": "4.4.3",
"react-navigation": "1.0.3",
Expand Down
5 changes: 5 additions & 0 deletions src/actions/message/notifyListAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ export const REQUEST = Symbol();
export const INVALIDATE = Symbol();
export const MARK_AT_ME_AS_READ = Symbol();
export const MARK_REPLY_AS_READ = Symbol();
export const MARK_SYSTEM_AS_READ = Symbol();
export const fetchNotifyList = createAction(REQUEST);
export const invalidateNotifyList = createAction(INVALIDATE);

const markAtMeAsRead = createAction(MARK_AT_ME_AS_READ);
const markReplyAsRead = createAction(MARK_REPLY_AS_READ);
const markSystemAsRead = createAction(MARK_SYSTEM_AS_READ);

// Update unread message count immediately instead of
// clearing them with next poll after 0 ~ 15s.
export const successfulCallback = (payload) => {
Expand All @@ -17,6 +20,8 @@ export const successfulCallback = (payload) => {
return markAtMeAsRead();
case 'post':
return markReplyAsRead();
case 'system':
return markSystemAsRead();
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/actions/user/friendListAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createAction } from 'redux-actions';

export const REQUEST = Symbol();
export const INVALIDATE = Symbol();
export const fetchFriendList = createAction(REQUEST);
export const invalidateFriendList = createAction(INVALIDATE);

export const REQUEST_STARTED = Symbol();
export const REQUEST_COMPELTED = Symbol();
export const REQUEST_FAILED = Symbol();
export const request = createAction(REQUEST_STARTED);
// return 2nd argument as `meta` field
export const success = createAction(REQUEST_COMPELTED, null, (...args) => args[1]);
export const failure = createAction(REQUEST_FAILED);
210 changes: 210 additions & 0 deletions src/components/3rd_party/LoadingSpinnerOverlay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import React, {
Component,
} from 'react'
import {
View,
Modal,
StyleSheet,
Animated,
Easing,
Dimensions,
ActivityIndicator,
ActivityIndicatorIOS,
ProgressBarAndroid,
} from 'react-native'

import TimerEnhance from 'react-native-smart-timer-enhance'

const styles = StyleSheet.create({
overlay: {
position: 'absolute',
left: 0,
top: 0,
zIndex: 998,
},
container: {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
position: 'absolute',
borderRadius: 8,
padding: 20.5,
left: -9999,
top: -9999,
zIndex: 999,
},
})
const noop = () => {}

class LoadingSpinnerOverlay extends Component {

static defaultProps = {
duration: 255,
delay: 0,
marginTop: 0,
modal: true,
}

constructor(props) {
super(props)
this.state = {
visible: false,
opacity: new Animated.Value(0),
children: props.children,
modal: props.modal,
marginTop: props.marginTop,
}
this._loadingSpinnerWidth = null
this._loadingSpinnerHeight = null
this._loadingSpinnerShowAnimation = null
this._loadingSpinnerHideAnimation = null
this._loadingSpinnerAnimationToggle = null
}

render() {
let loadingSpinner = this._renderLoadingSpinner()
return this._renderOverLay(loadingSpinner)
}

_renderOverLay(loadingSpinner) {
let {width: deviceWidth, height: deviceHeight,} = Dimensions.get('window')
return (
this.state.modal ?
(this.state.marginTop === 0 ?
<Modal animationType={'none'} transparent={true} visible={this.state.visible} onRequestClose={noop}>
{loadingSpinner}
</Modal> :
(this.state.visible ?
<View
style={[ styles.overlay, {marginTop: this.props.marginTop, width: deviceWidth, height: deviceHeight - this.props.marginTop,}, this.props.overlayStyle, ]}>
{loadingSpinner}
</View> : null))
: loadingSpinner
)
}

_renderLoadingSpinner() {
let children
if(this.state.children == null) {
children = this._renderActivityIndicator()
}
else {
children = React.Children.map(this.state.children, (child) => {
if (!React.isValidElement(child)) {
return null
}
return child
})
}
return (
this.state.visible ?
<Animated.View
ref={ component => this._container = component }
onLayout={this._onLoadingSpinnerLayout}
style={[styles.container, this.props.style, {opacity:this.state.opacity, }]}>
{children}
</Animated.View> : null
)
}

show({modal = this.state.modal, marginTop = this.state.marginTop, children = this.state.children, duration = this.props.duration, easing = Easing.linear, delay = this.props.delay, animationEnd,}
= {modal: this.state.modal, marginTop: this.state.marginTop, children: this.state.children, duration: this.props.duration, easing: Easing.linear, delay: this.props.delay,}) {

this._loadingSpinnerShowAnimation && this._loadingSpinnerShowAnimation.stop()
this._loadingSpinnerHideAnimation && this._loadingSpinnerHideAnimation.stop()
this._loadingSpinnerAnimationToggle && this.clearTimeout(this._loadingSpinnerAnimationToggle)

if(this.state.visible) {
this._setLoadingSpinnerOverlayPosition({modal, marginTop})
}

this.setState({
children,
modal,
marginTop,
visible: true,
})

this._loadingSpinnerShowAnimation = Animated.timing(
this.state.opacity,
{
toValue: 1,
duration,
easing,
delay,
}
)
this._loadingSpinnerShowAnimation.start(() => {
this._loadingSpinnerShowAnimation = null
animationEnd && animationEnd()
})
}

hide({duration = this.props.duration, easing = Easing.linear, delay = this.props.delay, animationEnd,}
= {duration: this.props.duration, easing: Easing.linear, delay: this.props.delay,}) {

this._loadingSpinnerShowAnimation && this._loadingSpinnerShowAnimation.stop()
this._loadingSpinnerHideAnimation && this._loadingSpinnerHideAnimation.stop()
this.clearTimeout(this._loadingSpinnerAnimationToggle)

this._loadingSpinnerHideAnimation = Animated.timing(
this.state.opacity,
{
toValue: 0,
duration,
easing,
delay,
}
)
this._loadingSpinnerHideAnimation.start( () => {
this._loadingSpinnerHideAnimation = null
this.setState({
visible: false,
})
animationEnd && animationEnd()
})
}

_onLoadingSpinnerLayout = (e) => {
this._loadingSpinnerWidth = e.nativeEvent.layout.width
this._loadingSpinnerHeight = e.nativeEvent.layout.height
this._setLoadingSpinnerOverlayPosition()
}

_setLoadingSpinnerOverlayPosition({modal, marginTop} = {modal: this.state.modal, marginTop: this.state.marginTop}) {
if(!this._loadingSpinnerWidth || !this._loadingSpinnerHeight) {
return
}
let {width: deviceWidth, height: deviceHeight,} = Dimensions.get('window')
let left = (deviceWidth - this._loadingSpinnerWidth) / 2
let top = (deviceHeight - this._loadingSpinnerHeight) / 2 - (modal && marginTop === 0 ? 0 : marginTop)
this._container.setNativeProps({
style: {
left,
top,
}
})
}

_renderActivityIndicator() {
return ActivityIndicator ? (
<ActivityIndicator
style={{position: 'relative', left: 1, top: 1,}}
animating={true}
color={'#fff'}
size={'large'}/>
) : Platform.OS == 'android' ?
(
<ProgressBarAndroid
color={'#fff'}
styleAttr={'large'}/>

) : (
<ActivityIndicatorIOS
animating={true}
color={'#fff'}
size={'large'}/>
)
}

}

export default TimerEnhance(LoadingSpinnerOverlay)
16 changes: 9 additions & 7 deletions src/components/Comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ export default class Comment extends Component {
'复制'
];
let isLoginUser = currentUserId === userId;
if (!isLoginUser) {
// If userId is 0, it's anonymous user.
let canSendPrivateMessage = !isLoginUser && userId !== 0;
let editable =
isLoginUser && managePanel && managePanel.length > 0 && !!managePanel.find(item => item.title === '编辑');
if (canSendPrivateMessage) {
options.push('私信');
} else {
} else if (editable) {
options.push('编辑');
}
options.push('取消');
Expand Down Expand Up @@ -70,13 +74,11 @@ export default class Comment extends Component {
});
break;
case 2:
if (!isLoginUser) {
if (canSendPrivateMessage) {
navigation.navigate('PrivateMessage', { userId });
} else if (managePanel && managePanel.length > 0) {
} else if (editable) {
let editAction = managePanel.find(item => item.title === '编辑');
if (editAction) {
SafariView.show(editAction.action);
}
SafariView.show(editAction.action);
}
break;
}
Expand Down
Loading