<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#import "WXRefreshComponent.h"
#import "WXScrollerComponent.h"
#import "WXLoadingIndicator.h"
#import "WXComponent_internal.h"
#import "WXLog.h"
#import "WXComponent+Layout.h"

@interface WXRefreshComponent()

@property (nonatomic) BOOL displayState;
@property (nonatomic) BOOL initFinished;
@property (nonatomic) BOOL refreshEvent;
@property (nonatomic) BOOL pullingdownEvent;

@property (nonatomic, weak) WXLoadingIndicator *indicator;

@end

@implementation WXRefreshComponent

- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
{
    self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
    if (self) {
        _refreshEvent = NO;
        _pullingdownEvent = NO;
        if (attributes[@"display"]) {
            if ([attributes[@"display"] isEqualToString:@"show"]) {
                _displayState = YES;
            } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
                _displayState = NO;
            } else {
                WXLogError(@"");
            }
        }
        self.flexCssNode->setStylePositionType(WeexCore::kAbsolute);
    }
    return self;
}

- (void)viewDidLoad
{
     _initFinished = YES;
    
    if (!_displayState) {
        [_indicator.view setHidden:YES];
    }
}

- (void)layoutDidFinish
{
    [self.view setFrame: (CGRect) {
        .size = self.calculatedFrame.size,
        .origin.x = self.calculatedFrame.origin.x,
        .origin.y = self.view.frame.origin.y - CGRectGetHeight(self.calculatedFrame)
    }];
}

- (void)viewWillUnload
{
    _displayState = NO;
    _refreshEvent = NO;
    _initFinished = NO;
}

- (void)refresh
{
    if (!_refreshEvent || _displayState) {
        return;
    }
#ifdef DEBUG
    WXLogDebug(@"flexLayout -> refreshComponent : refresh ref:%@",self.ref);
#endif
    [self fireEvent:@"refresh" params:nil];
}

- (void)pullingdown:(NSDictionary*)param
{
    if (!_pullingdownEvent) {
        return ;
    }
#ifdef DEBUG
    WXLogDebug(@"flexLayout -> refreshComponent : pullingdown ,ref:%@",self.ref);
#endif
    
    [self fireEvent:@"pullingdown" params:param];
}

- (BOOL)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
{
    BOOL inserted = [super _insertSubcomponent:subcomponent atIndex:index];
    if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
        _indicator = (WXLoadingIndicator*)subcomponent;
    }
    return inserted;
}

- (void)updateAttributes:(NSDictionary *)attributes
{
    if (attributes[@"display"]) {
        if ([attributes[@"display"] isEqualToString:@"show"]) {
            _displayState = YES;
        } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
            _displayState = NO;
        } else {
            WXLogError(@"");
        }
        [self setDisplay];
    }
}

- (void)addEvent:(NSString *)eventName
{
    if ([eventName isEqualToString:@"refresh"]) {
        _refreshEvent = YES;
    }
    if ([eventName isEqualToString:@"pullingdown"]) {
        _pullingdownEvent = YES;
    }
}

- (void)removeEvent:(NSString *)evetName
{
    if ([evetName isEqualToString:@"refresh"]) {
        _refreshEvent = NO;
    }
    if ([evetName isEqualToString:@"pullingdown"]) {
        _pullingdownEvent = NO;
    }
}

- (void)setDisplay
{
    id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
    if (scrollerProtocol == nil || !_initFinished)
        return;
    
    if ([scrollerProtocol respondsToSelector:@selector(refreshType)] &&
        [[scrollerProtocol refreshType] isEqualToString:@"refreshForAppear"]) {
        UIEdgeInsets inset = [scrollerProtocol contentInset];
        if (_displayState) {
            inset.top = self.calculatedFrame.size.height;
            if ([_indicator.view isHidden]) {
                [_indicator.view setHidden:NO];
            }
            [_indicator start];
        } else {
            inset.top = 0;
            [_indicator stop];
        }
        [scrollerProtocol setContentInset:inset];
    } else {
        CGPoint offset = [scrollerProtocol contentOffset];
        if (_displayState) {
            offset.y = -self.calculatedFrame.size.height;
            if ([_indicator.view isHidden]) {
                [_indicator.view setHidden:NO];
            }
            [_indicator start];
            [scrollerProtocol setContentOffset:offset animated:YES];
        } else {
            offset.y = 0;
            [_indicator stop];
            [UIView animateWithDuration:0.25 animations:^{
                [scrollerProtocol setContentOffset:offset];
            }];
        }
        
        /* If we are adding elements while refreshing, like this demo:http://dotwe.org/vue/f541ed72a121db8447a233b777003e8a
         the scroller cannot stay at (0, 0) when all animations are finished.
         So we use
            [scrollerProtocol setContentOffset:offset animated:YES];
         when _displayState is TRUE and use
            [UIView animateWithDuration:0.25 animations:^{
                [scrollerProtocol setContentOffset:offset];
            }];<commit_msg>[iOS] Fix after very quick refresh, scroller cannot stay at 0 point problem.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#import "WXRefreshComponent.h"
#import "WXScrollerComponent.h"
#import "WXLoadingIndicator.h"
#import "WXComponent_internal.h"
#import "WXLog.h"
#import "WXComponent+Layout.h"

@interface WXRefreshComponent()
{
    NSTimeInterval _refreshStateTriggerTime;
}

@property (nonatomic) BOOL displayState;
@property (nonatomic) BOOL initFinished;
@property (nonatomic) BOOL refreshEvent;
@property (nonatomic) BOOL pullingdownEvent;

@property (nonatomic, weak) WXLoadingIndicator *indicator;

@end

@implementation WXRefreshComponent

- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
{
    self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
    if (self) {
        _refreshEvent = NO;
        _pullingdownEvent = NO;
        if (attributes[@"display"]) {
            if ([attributes[@"display"] isEqualToString:@"show"]) {
                _displayState = YES;
            } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
                _displayState = NO;
            } else {
                WXLogError(@"");
            }
        }
        self.flexCssNode->setStylePositionType(WeexCore::kAbsolute);
    }
    return self;
}

- (void)viewDidLoad
{
     _initFinished = YES;
    
    if (!_displayState) {
        [_indicator.view setHidden:YES];
    }
}

- (void)layoutDidFinish
{
    [self.view setFrame: (CGRect) {
        .size = self.calculatedFrame.size,
        .origin.x = self.calculatedFrame.origin.x,
        .origin.y = self.view.frame.origin.y - CGRectGetHeight(self.calculatedFrame)
    }];
}

- (void)viewWillUnload
{
    _displayState = NO;
    _refreshEvent = NO;
    _initFinished = NO;
}

- (void)refresh
{
    if (!_refreshEvent || _displayState) {
        return;
    }
#ifdef DEBUG
    WXLogDebug(@"flexLayout -> refreshComponent : refresh ref:%@",self.ref);
#endif
    [self fireEvent:@"refresh" params:nil];
}

- (void)pullingdown:(NSDictionary*)param
{
    if (!_pullingdownEvent) {
        return ;
    }
#ifdef DEBUG
    WXLogDebug(@"flexLayout -> refreshComponent : pullingdown ,ref:%@",self.ref);
#endif
    
    [self fireEvent:@"pullingdown" params:param];
}

- (BOOL)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
{
    BOOL inserted = [super _insertSubcomponent:subcomponent atIndex:index];
    if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
        _indicator = (WXLoadingIndicator*)subcomponent;
    }
    return inserted;
}

- (void)updateAttributes:(NSDictionary *)attributes
{
    if (attributes[@"display"]) {
        if ([attributes[@"display"] isEqualToString:@"show"]) {
            _displayState = YES;
        } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
            _displayState = NO;
        } else {
            WXLogError(@"");
        }
        [self setDisplay];
    }
}

- (void)addEvent:(NSString *)eventName
{
    if ([eventName isEqualToString:@"refresh"]) {
        _refreshEvent = YES;
    }
    if ([eventName isEqualToString:@"pullingdown"]) {
        _pullingdownEvent = YES;
    }
}

- (void)removeEvent:(NSString *)evetName
{
    if ([evetName isEqualToString:@"refresh"]) {
        _refreshEvent = NO;
    }
    if ([evetName isEqualToString:@"pullingdown"]) {
        _pullingdownEvent = NO;
    }
}

- (void)setDisplay
{
    id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
    if (scrollerProtocol == nil || !_initFinished)
        return;
    
    if ([scrollerProtocol respondsToSelector:@selector(refreshType)] &&
        [[scrollerProtocol refreshType] isEqualToString:@"refreshForAppear"]) {
        UIEdgeInsets inset = [scrollerProtocol contentInset];
        if (_displayState) {
            inset.top = self.calculatedFrame.size.height;
            if ([_indicator.view isHidden]) {
                [_indicator.view setHidden:NO];
            }
            [_indicator start];
        } else {
            inset.top = 0;
            [_indicator stop];
        }
        [scrollerProtocol setContentInset:inset];
    } else {
        CGPoint offset = [scrollerProtocol contentOffset];
        if (_displayState) {
            offset.y = -self.calculatedFrame.size.height;
            if ([_indicator.view isHidden]) {
                [_indicator.view setHidden:NO];
            }
            [_indicator start];
            [scrollerProtocol setContentOffset:offset animated:YES];
            _refreshStateTriggerTime = CFAbsoluteTimeGetCurrent();
        } else {
            offset.y = 0;
            [_indicator stop];
            if (CFAbsoluteTimeGetCurrent() - _refreshStateTriggerTime < 0.3) {
                /* If javascript doesn't do any refreshing and only update 'display' attribute very quickly.
                 The previous '[scrollerProtocol setContentOffset:offset animated:YES];' is not finished,
                 we should also use '[scrollerProtocol setContentOffset:offset animated:YES]' to restore offset.
                 Or the scroller will not stop at 0.
                 */
                [scrollerProtocol setContentOffset:offset animated:YES];
            }
            else {
                [UIView animateWithDuration:0.25 animations:^{
                    [scrollerProtocol setContentOffset:offset];
                }];
            }
        }
        
        /* If we are adding elements while refreshing, like this demo:http://dotwe.org/vue/f541ed72a121db8447a233b777003e8a
         the scroller cannot stay at (0, 0) when all animations are finished.
         So we use
            [scrollerProtocol setContentOffset:offset animated:YES];
         when _displayState is TRUE and use
            [UIView animateWithDuration:0.25 animations:^{
                [scrollerProtocol setContentOffset:offset];
            }];
{'fraction_non_alphanumeric': 0.10967942249476054, 'fraction_numerical': 0.005899247069781883, 'mean_word_length': 2.8265518265518264, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7055672', 'n_tokens_mistral': 3850, 'n_tokens_neox': 3590, 'n_words': 892}