public ProjectFilterPageViewModel(
            INavigationService navigationService,
            IProjectDataStore projectDataStore) : base(navigationService)
        {
            Title = "Filter";

            Apply = ReactiveCommand.CreateFromTask(async() =>
            {
                _filter.StartDate           = _selectedStartDate;
                _filter.StartDateComparison = _selectedStartDateComparisonType;

                _filter.Trades.Clear();
                _filter.Trades.AddRange(_selectedTrades);

                await NavigationService.GoBackAsync(new NavigationParameters
                {
                    { "filter", _filter }
                },
                                                    useModalNavigation: true).ConfigureAwait(false);
            });

            Cancel = ReactiveCommand.CreateFromTask(() => NavigationService.GoBackAsync(useModalNavigation: true));

            NavigatedTo
            .Where(args => args.ContainsKey("filter"))
            .Take(1)
            .Select(args => (ProjectFilter)args["filter"])
            .Subscribe(filter => _filter = filter);

            NavigatedTo
            .Where(args => args.ContainsKey("selected_items"))
            .Select(args => (IEnumerable <SelectionViewModel>)args["selected_items"])
            .Subscribe(selections => _selectedTrades = selections.Select(x => x.Item).Cast <Trade>());

            // begin loading the trade data
            var trades = projectDataStore.GetTradesAsync();

            SelectTrades = ReactiveCommand.CreateFromTask(async() =>
            {
                var args = new NavigationParameters();
                args.Add(
                    "items",
                    (await trades).Select(specialty => new SelectionViewModel <Trade>(specialty)
                {
                    DisplayValue = specialty.Name,
                    IsSelected   = _selectedTrades?.Contains(specialty) == true
                }));

                await NavigationService.NavigateAsync(nameof(MultiSelectListViewPage), args).ConfigureAwait(false);
            });
        }
예제 #2
0
        public ProjectsPageViewModel(
            INavigationService navigationService,
            IProjectDataStore projectDataStore,
            IAnalyticService analyticService) : base(navigationService)
        {
            Title = "Projects";

            this.WhenActivated((CompositeDisposable _) =>
            {
                // track every time this screen is activated
                analyticService.TrackScreen("my-projects");
            });

            ViewProjectDetails = ReactiveCommand.CreateFromTask <Project, Unit>(async project =>
            {
                analyticService.TrackTapEvent("view-project");

                await NavigationService.NavigateAsync(
                    nameof(ProjectDetailsPage),
                    new NavigationParameters
                {
                    { "project", project }
                }).ConfigureAwait(false);

                return(Unit.Default);
            });

            AddProject = ReactiveCommand.CreateFromTask(async() =>
            {
                analyticService.TrackTapEvent("new-project");
                await NavigationService.NavigateAsync($"NavigationPage/{nameof(EditProjectPage)}", useModalNavigation: true).ConfigureAwait(false);
            });

            Filter = ReactiveCommand.CreateFromTask(async() =>
            {
                // TODO: Finish this
                await NavigationService.NavigateAsync(
                    $"NavigationPage/{nameof(ProjectFilterPage)}",
                    new NavigationParameters
                {
                    { "filter", _filter }
                },
                    useModalNavigation: true).ConfigureAwait(false);
            });

            // set up the command used to load projects
            LoadProjects = ReactiveCommand.CreateFromTask(_ =>
            {
                this.Log().Debug($"Loading projects on thread: {Thread.CurrentThread.ManagedThreadId}, IsBackground = {Thread.CurrentThread.IsBackground}");
                AssertRunningOnBackgroundThread();
                return(projectDataStore.LoadOldProjects(_oldestProject, ItemsPerPage));
            });

            // set up the command used to refresh projects
            RefreshProjects = ReactiveCommand.CreateFromTask(_ =>
            {
                // TODO: Should track this with analytics?
                this.Log().Debug($"Refreshing projects on thread: {Thread.CurrentThread.ManagedThreadId}, IsBackground = {Thread.CurrentThread.IsBackground}");
                AssertRunningOnBackgroundThread();
                return(_newestProject != null ?
                       projectDataStore.LoadNewProjects(_newestProject, ItemsPerPage) :
                       projectDataStore.LoadNewProjects(ItemsPerPage));
            });

            LoadProjects
            .Merge(RefreshProjects)
            .SubscribeSafe(projects =>
            {
                if (projects.Any())
                {
                    // NOTE: This probably isn't necessary...
                    projects = projects.ExceptBy(Projects, p => p.ProjectId);

                    // get the oldest and newest projects from the new data set. Age is simply
                    // determined by the date the project was created
                    var oldProject = projects.MinBy(p => p.CreateDate).First();
                    var newProject = projects.MaxBy(p => p.CreateDate).First();

                    if (_oldestProject == null && _newestProject == null)
                    {
                        _oldestProject = oldProject;
                        _newestProject = newProject;

                        // first projects being added. Add them to the list
                        Projects.AddRange(projects);
                    }
                    else if (_oldestProject?.CreateDate > oldProject.CreateDate)
                    {
                        _oldestProject = oldProject;

                        // if the projects are older, add them to the end of the list
                        Projects.AddRange(projects);
                    }
                    else if (_newestProject?.CreateDate < newProject.CreateDate)
                    {
                        _newestProject = newProject;

                        // if the projects are newer, insert them at the beginning of the list
                        Projects.InsertRange(0, projects);
                    }
                }
            });

            // when either of the commands are executing, update the busy state
            LoadProjects.IsExecuting
            .CombineLatest(RefreshProjects.IsExecuting, (isLoadExecuting, isRefreshExecuting) => isLoadExecuting || isRefreshExecuting)
            .DistinctUntilChanged()
            .StartWith(false)
            .ToProperty(this, x => x.IsBusy, out _isBusy, scheduler: RxApp.MainThreadScheduler);

            // When an exception is thrown for either command, log the error and let the user handle
            // the exception
            LoadProjects.ThrownExceptions
            .Merge(RefreshProjects.ThrownExceptions)
            .SelectMany(exception =>
            {
                this.Log().ErrorException("Error loading or refreshing data", exception);
                return(SharedInteractions.Error.Handle(exception));
            })
            .Subscribe();
        }
        public EditableProfilePageViewModel(
            INavigationService navigationService,
            IUserDialogs dialogService,
            IProjectDataStore projectDataStore,
            IPermissionsService permissionsService) : base(navigationService)
        {
            Title = "Edit Profile";

            AddValidationRules();

            NavigatedTo
            .Take(1)
            .Select(args => args["user"] as Account)
            .Subscribe(user =>
            {
                // TODO: Handle null user, handle editing...
            });

            ChangePhoto = ReactiveCommand.CreateFromTask(async() =>
            {
                try
                {
                    // check if permission has been granted to the photos
                    var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Photos);
                    if (status != PermissionStatus.Granted)
                    {
                        status = await permissionsService.CheckPermissionsAsync(Permission.Photos, dialogService);
                    }

                    // if permission was granted, open the photo picker
                    if (status == PermissionStatus.Granted)
                    {
                        using (var file = await CrossMedia.Current.PickPhotoAsync(_pickOptions))
                        {
                            if (file != null)
                            {
                                Photo = file.GetStream();
                            }
                        }
                    }
                    else
                    {
                        // permission was not granted. Let the user know they can't pick a photo
                        // without permissions
                        await dialogService.AlertAsync(
                            new AlertConfig
                        {
                            Title   = "Photos Not Supported",
                            Message = ":( Permission not granted to photos.",
                            OkText  = "OK"
                        }).ConfigureAwait(false);

                        return;
                    }
                }
                catch (MediaPermissionException mediaException)
                {
                    await dialogService.AlertAsync(
                        new AlertConfig
                    {
                        Title   = "Permission Error",
                        Message = $"Permissions not granted: {string.Join(", ", mediaException.Permissions)}",
                        OkText  = "OK"
                    }).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    // TODO: log the exception...
                    await dialogService.AlertAsync(
                        new AlertConfig
                    {
                        Title   = "Error",
                        Message = "An error has occurred.",
                        OkText  = "OK"
                    }).ConfigureAwait(false);
                }
            });

            Save = ReactiveCommand.CreateFromTask(async() =>
            {
                // TODO:...
            });

            Cancel = ReactiveCommand.CreateFromTask(async() =>
            {
                await NavigationService.GoBackAsync(useModalNavigation: true).ConfigureAwait(false);
            });

            SelectCommunities = ReactiveCommand.CreateFromTask(async() =>
            {
                var args = new NavigationParameters();
                args.Add(
                    "items",
                    (await projectDataStore.GetTradesAsync()).Select(specialty => new SelectionViewModel <Trade>(specialty)
                {
                    DisplayValue = specialty.Name
                }));

                await NavigationService.NavigateAsync(nameof(MultiSelectListViewPage), args).ConfigureAwait(false);
            });
        }
예제 #4
0
        public TradeSpecialtiesPageViewModel(
            INavigationService navigationService,
            IUserDialogs dialogService,
            IContainerRegistry containerRegistry,
            IProjectDataStore projectDataStore) : base(navigationService)
        {
            Title = "Trade Specialties";

            SelectAll = ReactiveCommand.Create(() =>
            {
                foreach (var item in Items)
                {
                    item.IsSelected = true;
                }
            });

            SelectNone = ReactiveCommand.Create(() =>
            {
                foreach (var item in Items)
                {
                    item.IsSelected = false;
                }
            });

            Next = ReactiveCommand.CreateFromTask(async() =>
            {
                // if there are no items selected, they can't move on. Must select at least one specialty
                var selectedItems = Items.Where(item => item.IsSelected).Select(item => item.Item);
                if (!selectedItems.Any())
                {
                    await dialogService.AlertAsync(
                        new AlertConfig
                    {
                        Title   = "Error",
                        Message = "You must select at least one trade specialty",
                        OkText  = "OK"
                    }).ConfigureAwait(false);

                    return;
                }

                if (_account is ContractorAccount contractor)
                {
                    contractor.AccountTrades.Clear();
                    contractor.AccountTrades.AddRange(
                        selectedItems.Select(item => new AccountTrade
                    {
                        AccountId = contractor.AccountId,
                        TradeId   = item.TradeId
                    }));
                }

                // TODO: Save the account...
                containerRegistry.RegisterInstance <IUserService>(new UserService(_account));
                await NavigationService.NavigateHomeAsync().ConfigureAwait(false);
            });

            projectDataStore
            .GetTradesAsync()
            .ContinueWith(t =>
            {
                // TODO: Handle failure?
                Items.AddRange(
                    t.Result.Select(
                        specialty => new SelectionViewModel <Trade>(specialty)
                {
                    DisplayValue = specialty.Name
                }));
            });

            NavigatingTo
            .Select(args => (Account)args["account"])
            .Subscribe(user =>
            {
                _account = user;
            });
        }
        public MyProjectsPageViewModel(
            INavigationService navigationService,
            IProjectDataStore projectDataStore,
            IUserDialogs dialogService,
            IUserService userService,
            IAnalyticService analyticService) : base(navigationService)
        {
            Title = "My Projects";

            this.WhenActivated((CompositeDisposable _) =>
            {
                // track every time this screen is activated
                analyticService.TrackScreen("my-projects");
            });

            var initialize = ReactiveCommand.CreateFromTask <Unit, IEnumerable <Project> >(_ =>
            {
                AssertRunningOnBackgroundThread();
                return(projectDataStore.LoadProjectsForUser(userService.AuthenticatedUser.AccountId));
            });

            initialize
            .Subscribe(projects =>
            {
                Projects.Reset(projects.Select(proj => new MyProjectViewModel(proj, Delete, Close, Edit)));
            });

            Projects.ActOnEveryObject(
                _ =>
            {
                this.Log().Debug("Item added");
            },
                projViewModel =>
            {
                this.Log().Debug("Item removed");

                // make sure we dispose of the item to release the subscription from the item's
                // commands to our commands
                projViewModel.Dispose();
            });

            // When an exception is thrown while loading data, log the error and let the user handle
            // the exception
            initialize.ThrownExceptions
            .SelectMany(exception =>
            {
                this.Log().ErrorException("Error loading or refreshing data", exception);
                return(SharedInteractions.Error.Handle(exception));
            })
            .Subscribe();

            Activator
            .Activated
            .Take(1)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .InvokeCommand(initialize);

            ViewProjectDetails = ReactiveCommand.CreateFromTask <MyProjectViewModel, Unit>(async project =>
            {
                analyticService.TrackTapEvent("view-project");

                await NavigationService.NavigateAsync(
                    nameof(ProjectDetailsPage),
                    new NavigationParameters
                {
                    { "project", project.Project }
                }).ConfigureAwait(false);

                return(Unit.Default);
            });

            Delete = ReactiveCommand.CreateFromTask <MyProjectViewModel, Unit>(async project =>
            {
                bool shouldDelete = await dialogService.ConfirmAsync(
                    new ConfirmConfig
                {
                    Message    = "Are you sure you want to delete the project?",
                    OkText     = "Delete",
                    CancelText = "Cancel"
                });
                if (!shouldDelete)
                {
                    return(Unit.Default);
                }

                analyticService.TrackTapEvent("delete-project");

                // remove the project
                await projectDataStore.DeleteProjectAsync(project.Project);
                Projects.Remove(project);

                dialogService.Toast(new ToastConfig("Project deleted successfully!"));

                return(Unit.Default);
            });

            Close = ReactiveCommand.CreateFromTask <MyProjectViewModel, Unit>(async project =>
            {
                bool shouldDelete = await dialogService.ConfirmAsync(
                    new ConfirmConfig
                {
                    Message    = "Are you sure you want to close the project?",
                    OkText     = "Close",
                    CancelText = "Cancel"
                });
                if (!shouldDelete)
                {
                    return(Unit.Default);
                }

                analyticService.TrackTapEvent("close-project");

                //project.Project.Status = ProjectStatus.Closed;

                // TODO: Save to the database

                return(Unit.Default);
            });

            Edit = ReactiveCommand.CreateFromTask <MyProjectViewModel, Unit>(async project =>
            {
                analyticService.TrackTapEvent("edit-project");

                await NavigationService.NavigateAsync(
                    $"NavigationPage/{nameof(EditProjectPage)}",
                    new NavigationParameters
                {
                    { "project", project.Project }
                },
                    useModalNavigation: true).ConfigureAwait(false);

                return(Unit.Default);
            });

            initialize.IsExecuting
            .StartWith(false)
            .ToProperty(this, x => x.IsBusy, out _isBusy, scheduler: RxApp.MainThreadScheduler);
        }