Exemplo n.º 1
0
        public FilePickerVM(object parentVM = null)
        {
            Parent = parentVM;
            SetTargetPathCommand = ConstructTypicalPickerCommand();

            var existsCheckTuple = Observable.CombineLatest(
                this.WhenAny(x => x.ExistCheckOption),
                this.WhenAny(x => x.PathType),
                this.WhenAny(x => x.TargetPath)
                // Dont want to debounce the initial value, because we know it's null
                .Skip(1)
                .Debounce(TimeSpan.FromMilliseconds(200), RxApp.MainThreadScheduler)
                .StartWith(default(string)),
                resultSelector: (existsOption, type, path) => (ExistsOption: existsOption, Type: type, Path: path))
                                   .StartWith((ExistsOption: ExistCheckOption, Type: PathType, Path: TargetPath))
                                   .Replay(1)
                                   .RefCount();

            var doExistsCheck = existsCheckTuple
                                .Select(t =>
            {
                // Don't do exists type if we don't know what path type we're tracking
                if (t.Type == PathTypeOptions.Off)
                {
                    return(false);
                }
                switch (t.ExistsOption)
                {
                case CheckOptions.Off:
                    return(false);

                case CheckOptions.IfPathNotEmpty:
                    return(!string.IsNullOrWhiteSpace(t.Path));

                case CheckOptions.On:
                    return(true);

                default:
                    throw new NotImplementedException();
                }
            })
                                .Replay(1)
                                .RefCount();

            _exists = Observable.Interval(TimeSpan.FromSeconds(3), RxApp.TaskpoolScheduler)
                      // Only check exists on timer if desired
                      .FlowSwitch(doExistsCheck)
                      .Unit()
                      // Also check though, when fields change
                      .Merge(this.WhenAny(x => x.PathType).Unit())
                      .Merge(this.WhenAny(x => x.ExistCheckOption).Unit())
                      .Merge(this.WhenAny(x => x.TargetPath).Unit())
                      // Signaled to check, get latest params for actual use
                      .CombineLatest(existsCheckTuple,
                                     resultSelector: (_, tuple) => tuple)
                      // Refresh exists
                      .ObserveOn(RxApp.TaskpoolScheduler)
                      .Select(t =>
            {
                switch (t.ExistsOption)
                {
                case CheckOptions.IfPathNotEmpty:
                    if (string.IsNullOrWhiteSpace(t.Path))
                    {
                        return(false);
                    }
                    break;

                case CheckOptions.On:
                    break;

                case CheckOptions.Off:
                default:
                    return(false);
                }
                switch (t.Type)
                {
                case PathTypeOptions.Either:
                    return(File.Exists(t.Path) || Directory.Exists(t.Path));

                case PathTypeOptions.File:
                    return(File.Exists(t.Path));

                case PathTypeOptions.Folder:
                    return(Directory.Exists(t.Path));

                case PathTypeOptions.Off:
                default:
                    return(false);
                }
            })
                      .DistinctUntilChanged()
                      .StartWith(false)
                      .ToGuiProperty(this, nameof(Exists));

            var passesFilters = Observable.CombineLatest(
                this.WhenAny(x => x.TargetPath),
                this.WhenAny(x => x.PathType),
                this.WhenAny(x => x.FilterCheckOption),
                Filters.Connect().QueryWhenChanged(),
                resultSelector: (target, type, checkOption, query) =>
            {
                switch (type)
                {
                case PathTypeOptions.Either:
                case PathTypeOptions.File:
                    break;

                default:
                    return(true);
                }
                if (query.Count == 0)
                {
                    return(true);
                }
                switch (checkOption)
                {
                case CheckOptions.Off:
                    return(true);

                case CheckOptions.IfPathNotEmpty:
                    if (string.IsNullOrWhiteSpace(target))
                    {
                        return(true);
                    }
                    break;

                case CheckOptions.On:
                    break;

                default:
                    throw new NotImplementedException();
                }

                try
                {
                    var extension = Path.GetExtension(target);
                    if (extension == null || !extension.StartsWith("."))
                    {
                        return(false);
                    }
                    extension = extension.Substring(1);
                    if (!query.Any(filter => filter.Extensions.Any(ext => string.Equals(ext, extension))))
                    {
                        return(false);
                    }
                }
                catch (ArgumentException)
                {
                    return(false);
                }
                return(true);
            })
                                .StartWith(true)
                                .Select(passed =>
            {
                if (passed)
                {
                    return(ErrorResponse.Success);
                }
                return(ErrorResponse.Fail(DoesNotPassFiltersText));
            })
                                .Replay(1)
                                .RefCount();

            _errorState = Observable.CombineLatest(
                Observable.CombineLatest(
                    this.WhenAny(x => x.Exists),
                    doExistsCheck,
                    resultSelector: (exists, doExists) => !doExists || exists)
                .Select(exists => ErrorResponse.Create(successful: exists, exists ? default(string) : PathDoesNotExistText)),
                passesFilters,
                this.WhenAny(x => x.AdditionalError)
                .Select(x => x ?? Observable.Return <IErrorResponse>(ErrorResponse.Success))
                .Switch(),
                resultSelector: (existCheck, filter, err) =>
            {
                if (existCheck.Failed)
                {
                    return(existCheck);
                }
                if (filter.Failed)
                {
                    return(filter);
                }
                return(ErrorResponse.Convert(err));
            })
                          .ToGuiProperty(this, nameof(ErrorState));

            _inError = this.WhenAny(x => x.ErrorState)
                       .Select(x => !x.Succeeded)
                       .ToGuiProperty(this, nameof(InError));

            // Doesn't derive from ErrorState, as we want to bubble non-empty tooltips,
            // which is slightly different logic
            _errorTooltip = Observable.CombineLatest(
                Observable.CombineLatest(
                    this.WhenAny(x => x.Exists),
                    doExistsCheck,
                    resultSelector: (exists, doExists) => !doExists || exists)
                .Select(exists => exists ? default(string) : PathDoesNotExistText),
                passesFilters
                .Select(x => x.Reason),
                this.WhenAny(x => x.AdditionalError)
                .Select(x => x ?? Observable.Return <IErrorResponse>(ErrorResponse.Success))
                .Switch(),
                resultSelector: (exists, filters, err) =>
            {
                if (!string.IsNullOrWhiteSpace(exists))
                {
                    return(exists);
                }
                if (!string.IsNullOrWhiteSpace(filters))
                {
                    return(filters);
                }
                return(err?.Reason);
            })
                            .ToGuiProperty(this, nameof(ErrorTooltip));
        }