From ee71a905e1c1504b57d1763a43d9a7496338619f Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Wed, 3 Feb 2021 16:22:18 +0100 Subject: [PATCH] Use new navigator for all screens --- src/navigator/mod.rs | 4 +- src/screens/ensemble.rs | 89 ++++++++++++------------------ src/screens/person.rs | 115 ++++++++++++++++----------------------- src/screens/recording.rs | 64 ++++++++-------------- src/screens/work.rs | 89 ++++++++++++------------------ src/window.rs | 21 ++++--- 6 files changed, 156 insertions(+), 226 deletions(-) diff --git a/src/navigator/mod.rs b/src/navigator/mod.rs index 873f28a..ffeb443 100644 --- a/src/navigator/mod.rs +++ b/src/navigator/mod.rs @@ -99,7 +99,7 @@ impl Navigator { .vexpand(true) .build(); - widget.add_child(empty_screen); + widget.add_named(empty_screen, Some("empty_screen")); let this = Rc::new(Self { widget, @@ -193,6 +193,8 @@ impl Navigator { let widget = screen.get_widget(); self.widget.set_visible_child(&widget); } else { + self.widget.set_visible_child_name("empty_screen"); + if let Some(cb) = &*self.back_cb.borrow() { cb() } diff --git a/src/screens/ensemble.rs b/src/screens/ensemble.rs index 01bee0a..2ae3496 100644 --- a/src/screens/ensemble.rs +++ b/src/screens/ensemble.rs @@ -1,11 +1,10 @@ use super::RecordingScreen; - use crate::backend::Backend; use crate::database::{Ensemble, Recording}; use crate::editors::EnsembleEditor; -use crate::navigator::NavigatorWindow; -use crate::widgets::{List, Navigator, NavigatorScreen, Screen, Section}; - +use crate::navigator::{NavigatorWindow, NavigationHandle, Screen}; +use crate::widgets; +use crate::widgets::{List, Section, Widget}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; @@ -15,61 +14,54 @@ use std::rc::Rc; /// A screen for showing recordings with a ensemble. pub struct EnsembleScreen { - backend: Rc, + handle: NavigationHandle<()>, ensemble: Ensemble, - widget: Screen, + widget: widgets::Screen, recording_list: Rc, recordings: RefCell>, - navigator: RefCell>>, } -impl EnsembleScreen { +impl Screen for EnsembleScreen { /// Create a new ensemble screen for the specified ensemble and load the /// contents asynchronously. - pub fn new(backend: Rc, ensemble: Ensemble) -> Rc { - let widget = Screen::new(); + fn new(ensemble: Ensemble, handle: NavigationHandle<()>) -> Rc { + let widget = widgets::Screen::new(); widget.set_title(&ensemble.name); let recording_list = List::new(); let this = Rc::new(Self { - backend, + handle, ensemble, widget, recording_list, recordings: RefCell::new(Vec::new()), - navigator: RefCell::new(None), }); - this.widget.set_back_cb(clone!(@strong this => move || { - let navigator = this.navigator.borrow().clone(); - if let Some(navigator) = navigator { - navigator.pop(); - } + this.widget.set_back_cb(clone!(@weak this => move || { + this.handle.pop(None); })); - this.widget.add_action(&gettext("Edit ensemble"), clone!(@strong this => move || { + this.widget.add_action(&gettext("Edit ensemble"), clone!(@weak this => move || { spawn!(@clone this, async move { - let window = NavigatorWindow::new(this.backend.clone()); - replace!(window.navigator, EnsembleEditor, None).await; + let window = NavigatorWindow::new(this.handle.backend.clone()); + replace!(window.navigator, EnsembleEditor, Some(this.ensemble.clone())).await; }); })); - this.widget.add_action(&gettext("Delete ensemble"), clone!(@strong this => move || { - let context = glib::MainContext::default(); - let clone = this.clone(); - context.spawn_local(async move { - clone.backend.db().delete_ensemble(&clone.ensemble.id).await.unwrap(); - clone.backend.library_changed(); + this.widget.add_action(&gettext("Delete ensemble"), clone!(@weak this => move || { + spawn!(@clone this, async move { + this.handle.backend.db().delete_ensemble(&this.ensemble.id).await.unwrap(); + this.handle.backend.library_changed(); }); })); - this.widget.set_search_cb(clone!(@strong this => move || { + this.widget.set_search_cb(clone!(@weak this => move || { this.recording_list.invalidate_filter(); })); - this.recording_list.set_make_widget_cb(clone!(@strong this => move |index| { + this.recording_list.set_make_widget_cb(clone!(@weak this => move |index| { let recording = &this.recordings.borrow()[index]; let row = libadwaita::ActionRow::new(); @@ -78,17 +70,17 @@ impl EnsembleScreen { row.set_subtitle(Some(&recording.get_performers())); let recording = recording.to_owned(); - row.connect_activated(clone!(@strong this => move |_| { - let navigator = this.navigator.borrow().clone(); - if let Some(navigator) = navigator { - navigator.push(RecordingScreen::new(this.backend.clone(), recording.clone())); - } + row.connect_activated(clone!(@weak this => move |_| { + let recording = recording.clone(); + spawn!(@clone this, async move { + push!(this.handle, RecordingScreen, recording.clone()).await; + }); })); row.upcast() })); - this.recording_list.set_filter_cb(clone!(@strong this => move |index| { + this.recording_list.set_filter_cb(clone!(@weak this => move |index| { let recording = &this.recordings.borrow()[index]; let search = this.widget.get_search(); let text = recording.work.get_title() + &recording.get_performers(); @@ -97,43 +89,32 @@ impl EnsembleScreen { // Load the content asynchronously. - let context = glib::MainContext::default(); - let clone = Rc::clone(&this); - - context.spawn_local(async move { - let recordings = clone + spawn!(@clone this, async move { + let recordings = this.handle .backend .db() - .get_recordings_for_ensemble(&clone.ensemble.id) + .get_recordings_for_ensemble(&this.ensemble.id) .await .unwrap(); if !recordings.is_empty() { let length = recordings.len(); - clone.recordings.replace(recordings); - clone.recording_list.update(length); + this.recordings.replace(recordings); + this.recording_list.update(length); - let section = Section::new("Recordings", &clone.recording_list.widget); - clone.widget.add_content(§ion.widget); + let section = Section::new("Recordings", &this.recording_list.widget); + this.widget.add_content(§ion.widget); } - clone.widget.ready(); + this.widget.ready(); }); this } } -impl NavigatorScreen for EnsembleScreen { - fn attach_navigator(&self, navigator: Rc) { - self.navigator.replace(Some(navigator)); - } - +impl Widget for EnsembleScreen { fn get_widget(&self) -> gtk::Widget { self.widget.widget.clone().upcast() } - - fn detach_navigator(&self) { - self.navigator.replace(None); - } } diff --git a/src/screens/person.rs b/src/screens/person.rs index a005cad..074b32b 100644 --- a/src/screens/person.rs +++ b/src/screens/person.rs @@ -1,11 +1,10 @@ use super::{WorkScreen, RecordingScreen}; - use crate::backend::Backend; use crate::database::{Person, Recording, Work}; use crate::editors::PersonEditor; -use crate::navigator::NavigatorWindow; -use crate::widgets::{List, Navigator, NavigatorScreen, Screen, Section}; - +use crate::navigator::{NavigatorWindow, NavigationHandle, Screen}; +use crate::widgets; +use crate::widgets::{List, Section, Widget}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; @@ -15,67 +14,60 @@ use std::rc::Rc; /// A screen for showing works by and recordings with a person. pub struct PersonScreen { - backend: Rc, + handle: NavigationHandle<()>, person: Person, - widget: Screen, + widget: widgets::Screen, work_list: Rc, recording_list: Rc, works: RefCell>, recordings: RefCell>, - navigator: RefCell>>, } -impl PersonScreen { +impl Screen for PersonScreen { /// Create a new person screen for the specified person and load the /// contents asynchronously. - pub fn new(backend: Rc, person: Person) -> Rc { - let widget = Screen::new(); + fn new(person: Person, handle: NavigationHandle<()>) -> Rc { + let widget = widgets::Screen::new(); widget.set_title(&person.name_fl()); let work_list = List::new(); let recording_list = List::new(); let this = Rc::new(Self { - backend, + handle, person, widget, work_list, recording_list, works: RefCell::new(Vec::new()), recordings: RefCell::new(Vec::new()), - navigator: RefCell::new(None), }); - this.widget.set_back_cb(clone!(@strong this => move || { - let navigator = this.navigator.borrow().clone(); - if let Some(navigator) = navigator { - navigator.pop(); - } + this.widget.set_back_cb(clone!(@weak this => move || { + this.handle.pop(None); })); - this.widget.add_action(&gettext("Edit person"), clone!(@strong this => move || { + this.widget.add_action(&gettext("Edit person"), clone!(@weak this => move || { spawn!(@clone this, async move { - let window = NavigatorWindow::new(this.backend.clone()); - replace!(window.navigator, PersonEditor, None).await; + let window = NavigatorWindow::new(this.handle.backend.clone()); + replace!(window.navigator, PersonEditor, Some(this.person.clone())).await; }); })); - this.widget.add_action(&gettext("Delete person"), clone!(@strong this => move || { - let context = glib::MainContext::default(); - let clone = this.clone(); - context.spawn_local(async move { - clone.backend.db().delete_person(&clone.person.id).await.unwrap(); - clone.backend.library_changed(); + this.widget.add_action(&gettext("Delete person"), clone!(@weak this => move || { + spawn!(@clone this, async move { + this.handle.backend.db().delete_person(&this.person.id).await.unwrap(); + this.handle.backend.library_changed(); }); })); - this.widget.set_search_cb(clone!(@strong this => move || { + this.widget.set_search_cb(clone!(@weak this => move || { this.work_list.invalidate_filter(); this.recording_list.invalidate_filter(); })); - this.work_list.set_make_widget_cb(clone!(@strong this => move |index| { + this.work_list.set_make_widget_cb(clone!(@weak this => move |index| { let work = &this.works.borrow()[index]; let row = libadwaita::ActionRow::new(); @@ -83,24 +75,24 @@ impl PersonScreen { row.set_title(Some(&work.title)); let work = work.to_owned(); - row.connect_activated(clone!(@strong this => move |_| { - let navigator = this.navigator.borrow().clone(); - if let Some(navigator) = navigator { - navigator.push(WorkScreen::new(this.backend.clone(), work.clone())); - } + row.connect_activated(clone!(@weak this => move |_| { + let work = work.clone(); + spawn!(@clone this, async move { + push!(this.handle, WorkScreen, work.clone()).await; + }); })); row.upcast() })); - this.work_list.set_filter_cb(clone!(@strong this => move |index| { + this.work_list.set_filter_cb(clone!(@weak this => move |index| { let work = &this.works.borrow()[index]; let search = this.widget.get_search(); let title = work.title.to_lowercase(); search.is_empty() || title.contains(&search) })); - this.recording_list.set_make_widget_cb(clone!(@strong this => move |index| { + this.recording_list.set_make_widget_cb(clone!(@weak this => move |index| { let recording = &this.recordings.borrow()[index]; let row = libadwaita::ActionRow::new(); @@ -109,17 +101,17 @@ impl PersonScreen { row.set_subtitle(Some(&recording.get_performers())); let recording = recording.to_owned(); - row.connect_activated(clone!(@strong this => move |_| { - let navigator = this.navigator.borrow().clone(); - if let Some(navigator) = navigator { - navigator.push(RecordingScreen::new(this.backend.clone(), recording.clone())); - } + row.connect_activated(clone!(@weak this => move |_| { + let recording = recording.clone(); + spawn!(@clone this, async move { + push!(this.handle, RecordingScreen, recording.clone()).await; + }); })); row.upcast() })); - this.recording_list.set_filter_cb(clone!(@strong this => move |index| { + this.recording_list.set_filter_cb(clone!(@weak this => move |index| { let recording = &this.recordings.borrow()[index]; let search = this.widget.get_search(); let text = recording.work.get_title() + &recording.get_performers(); @@ -128,59 +120,48 @@ impl PersonScreen { // Load the content asynchronously. - let context = glib::MainContext::default(); - let clone = Rc::clone(&this); - - context.spawn_local(async move { - let works = clone + spawn!(@clone this, async move { + let works = this.handle .backend .db() - .get_works(&clone.person.id) + .get_works(&this.person.id) .await .unwrap(); - let recordings = clone + let recordings = this.handle .backend .db() - .get_recordings_for_person(&clone.person.id) + .get_recordings_for_person(&this.person.id) .await .unwrap(); if !works.is_empty() { let length = works.len(); - clone.works.replace(works); - clone.work_list.update(length); + this.works.replace(works); + this.work_list.update(length); - let section = Section::new("Works", &clone.work_list.widget); - clone.widget.add_content(§ion.widget); + let section = Section::new("Works", &this.work_list.widget); + this.widget.add_content(§ion.widget); } if !recordings.is_empty() { let length = recordings.len(); - clone.recordings.replace(recordings); - clone.recording_list.update(length); + this.recordings.replace(recordings); + this.recording_list.update(length); - let section = Section::new("Recordings", &clone.recording_list.widget); - clone.widget.add_content(§ion.widget); + let section = Section::new("Recordings", &this.recording_list.widget); + this.widget.add_content(§ion.widget); } - clone.widget.ready(); + this.widget.ready(); }); this } } -impl NavigatorScreen for PersonScreen { - fn attach_navigator(&self, navigator: Rc) { - self.navigator.replace(Some(navigator)); - } - +impl Widget for PersonScreen { fn get_widget(&self) -> gtk::Widget { self.widget.widget.clone().upcast() } - - fn detach_navigator(&self) { - self.navigator.replace(None); - } } diff --git a/src/screens/recording.rs b/src/screens/recording.rs index 241e8ce..5e51443 100644 --- a/src/screens/recording.rs +++ b/src/screens/recording.rs @@ -1,9 +1,9 @@ use crate::backend::Backend; use crate::database::Recording; use crate::editors::RecordingEditor; -use crate::navigator::NavigatorWindow; -use crate::widgets::{List, Navigator, NavigatorScreen, Screen, Section}; - +use crate::navigator::{NavigatorWindow, NavigationHandle, Screen}; +use crate::widgets; +use crate::widgets::{List, Section, Widget}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; @@ -13,58 +13,51 @@ use std::rc::Rc; /// A screen for showing a recording. pub struct RecordingScreen { - backend: Rc, + handle: NavigationHandle<()>, recording: Recording, - widget: Screen, + widget: widgets::Screen, track_list: Rc, recordings: RefCell>, - navigator: RefCell>>, } -impl RecordingScreen { +impl Screen for RecordingScreen { /// Create a new recording screen for the specified recording and load the /// contents asynchronously. - pub fn new(backend: Rc, recording: Recording) -> Rc { - let widget = Screen::new(); + fn new(recording: Recording, handle: NavigationHandle<()>) -> Rc { + let widget = widgets::Screen::new(); widget.set_title(&recording.work.get_title()); widget.set_subtitle(&recording.get_performers()); let track_list = List::new(); let this = Rc::new(Self { - backend, + handle, recording, widget, track_list, recordings: RefCell::new(Vec::new()), - navigator: RefCell::new(None), }); - this.widget.set_back_cb(clone!(@strong this => move || { - let navigator = this.navigator.borrow().clone(); - if let Some(navigator) = navigator { - navigator.pop(); - } + this.widget.set_back_cb(clone!(@weak this => move || { + this.handle.pop(None); })); - this.widget.add_action(&gettext("Edit recording"), clone!(@strong this => move || { + this.widget.add_action(&gettext("Edit recording"), clone!(@weak this => move || { spawn!(@clone this, async move { - let window = NavigatorWindow::new(this.backend.clone()); - replace!(window.navigator, RecordingEditor, None).await; + let window = NavigatorWindow::new(this.handle.backend.clone()); + replace!(window.navigator, RecordingEditor, Some(this.recording.clone())).await; }); })); - this.widget.add_action(&gettext("Delete recording"), clone!(@strong this => move || { - let context = glib::MainContext::default(); - let clone = this.clone(); - context.spawn_local(async move { - clone.backend.db().delete_recording(&clone.recording.id).await.unwrap(); - clone.backend.library_changed(); + this.widget.add_action(&gettext("Delete recording"), clone!(@weak this => move || { + spawn!(@clone this, async move { + this.handle.backend.db().delete_recording(&this.recording.id).await.unwrap(); + this.handle.backend.library_changed(); }); })); - this.widget.set_search_cb(clone!(@strong this => move || { + this.widget.set_search_cb(clone!(@weak this => move || { this.track_list.invalidate_filter(); })); @@ -72,7 +65,7 @@ impl RecordingScreen { // this.track_list.set_make_widget_cb(clone!(@strong this => move |index| { // })); - this.track_list.set_filter_cb(clone!(@strong this => move |index| { + this.track_list.set_filter_cb(clone!(@weak this => move |index| { // TODO: Implement. // search.is_empty() || text.to_lowercase().contains(&search) true @@ -80,29 +73,18 @@ impl RecordingScreen { // Load the content asynchronously. - let context = glib::MainContext::default(); - let clone = Rc::clone(&this); - - context.spawn_local(async move { + spawn!(@clone this, async move { // TODO: Implement. - clone.widget.ready(); + this.widget.ready(); }); this } } -impl NavigatorScreen for RecordingScreen { - fn attach_navigator(&self, navigator: Rc) { - self.navigator.replace(Some(navigator)); - } - +impl Widget for RecordingScreen { fn get_widget(&self) -> gtk::Widget { self.widget.widget.clone().upcast() } - - fn detach_navigator(&self) { - self.navigator.replace(None); - } } diff --git a/src/screens/work.rs b/src/screens/work.rs index 7e1558b..0b1c4ba 100644 --- a/src/screens/work.rs +++ b/src/screens/work.rs @@ -1,11 +1,10 @@ use super::RecordingScreen; - use crate::backend::Backend; use crate::database::{Work, Recording}; use crate::editors::WorkEditor; -use crate::navigator::NavigatorWindow; -use crate::widgets::{List, Navigator, NavigatorScreen, Screen, Section}; - +use crate::navigator::{NavigatorWindow, NavigationHandle, Screen}; +use crate::widgets; +use crate::widgets::{List, Section, Widget}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; @@ -15,62 +14,55 @@ use std::rc::Rc; /// A screen for showing recordings of a work. pub struct WorkScreen { - backend: Rc, + handle: NavigationHandle<()>, work: Work, - widget: Screen, + widget: widgets::Screen, recording_list: Rc, recordings: RefCell>, - navigator: RefCell>>, } -impl WorkScreen { +impl Screen for WorkScreen { /// Create a new work screen for the specified work and load the /// contents asynchronously. - pub fn new(backend: Rc, work: Work) -> Rc { - let widget = Screen::new(); + fn new(work: Work, handle: NavigationHandle<()>) -> Rc { + let widget = widgets::Screen::new(); widget.set_title(&work.title); widget.set_subtitle(&work.composer.name_fl()); let recording_list = List::new(); let this = Rc::new(Self { - backend, + handle, work, widget, recording_list, recordings: RefCell::new(Vec::new()), - navigator: RefCell::new(None), }); - this.widget.set_back_cb(clone!(@strong this => move || { - let navigator = this.navigator.borrow().clone(); - if let Some(navigator) = navigator { - navigator.pop(); - } + this.widget.set_back_cb(clone!(@weak this => move || { + this.handle.pop(None); })); - this.widget.add_action(&gettext("Edit work"), clone!(@strong this => move || { + this.widget.add_action(&gettext("Edit work"), clone!(@weak this => move || { spawn!(@clone this, async move { - let window = NavigatorWindow::new(this.backend.clone()); - replace!(window.navigator, WorkEditor, None).await; + let window = NavigatorWindow::new(this.handle.backend.clone()); + replace!(window.navigator, WorkEditor, Some(this.work.clone())).await; }); })); - this.widget.add_action(&gettext("Delete work"), clone!(@strong this => move || { - let context = glib::MainContext::default(); - let clone = this.clone(); - context.spawn_local(async move { - clone.backend.db().delete_work(&clone.work.id).await.unwrap(); - clone.backend.library_changed(); + this.widget.add_action(&gettext("Delete work"), clone!(@weak this => move || { + spawn!(@clone this, async move { + this.handle.backend.db().delete_work(&this.work.id).await.unwrap(); + this.handle.backend.library_changed(); }); })); - this.widget.set_search_cb(clone!(@strong this => move || { + this.widget.set_search_cb(clone!(@weak this => move || { this.recording_list.invalidate_filter(); })); - this.recording_list.set_make_widget_cb(clone!(@strong this => move |index| { + this.recording_list.set_make_widget_cb(clone!(@weak this => move |index| { let recording = &this.recordings.borrow()[index]; let row = libadwaita::ActionRow::new(); @@ -79,17 +71,17 @@ impl WorkScreen { row.set_subtitle(Some(&recording.get_performers())); let recording = recording.to_owned(); - row.connect_activated(clone!(@strong this => move |_| { - let navigator = this.navigator.borrow().clone(); - if let Some(navigator) = navigator { - navigator.push(RecordingScreen::new(this.backend.clone(), recording.clone())); - } + row.connect_activated(clone!(@weak this => move |_| { + let recording = recording.clone(); + spawn!(@clone this, async move { + push!(this.handle, RecordingScreen, recording.clone()).await; + }); })); row.upcast() })); - this.recording_list.set_filter_cb(clone!(@strong this => move |index| { + this.recording_list.set_filter_cb(clone!(@weak this => move |index| { let recording = &this.recordings.borrow()[index]; let search = this.widget.get_search(); let text = recording.work.get_title() + &recording.get_performers(); @@ -98,43 +90,32 @@ impl WorkScreen { // Load the content asynchronously. - let context = glib::MainContext::default(); - let clone = Rc::clone(&this); - - context.spawn_local(async move { - let recordings = clone + spawn!(@clone this, async move { + let recordings = this.handle .backend .db() - .get_recordings_for_work(&clone.work.id) + .get_recordings_for_work(&this.work.id) .await .unwrap(); if !recordings.is_empty() { let length = recordings.len(); - clone.recordings.replace(recordings); - clone.recording_list.update(length); + this.recordings.replace(recordings); + this.recording_list.update(length); - let section = Section::new("Recordings", &clone.recording_list.widget); - clone.widget.add_content(§ion.widget); + let section = Section::new("Recordings", &this.recording_list.widget); + this.widget.add_content(§ion.widget); } - clone.widget.ready(); + this.widget.ready(); }); this } } -impl NavigatorScreen for WorkScreen { - fn attach_navigator(&self, navigator: Rc) { - self.navigator.replace(Some(navigator)); - } - +impl Widget for WorkScreen { fn get_widget(&self) -> gtk::Widget { self.widget.widget.clone().upcast() } - - fn detach_navigator(&self) { - self.navigator.replace(None); - } } diff --git a/src/window.rs b/src/window.rs index 145eca8..97a1eee 100644 --- a/src/window.rs +++ b/src/window.rs @@ -4,7 +4,7 @@ use crate::import::SourceSelector; use crate::preferences::Preferences; use crate::screens::*; use crate::widgets::*; -use crate::navigator::NavigatorWindow; +use crate::navigator::{Navigator, NavigatorWindow}; use futures::prelude::*; use gettextrs::gettext; use gio::prelude::*; @@ -44,7 +44,7 @@ impl Window { stack.add_named(&player_screen.widget, Some("player_screen")); let poe_list = PoeList::new(backend.clone()); - let navigator = Navigator::new(&window, &empty_screen); + let navigator = Navigator::new(backend.clone(), &window, &empty_screen); navigator.set_back_cb(clone!(@strong leaflet, @strong sidebar_box => move || { leaflet.set_visible_child(&sidebar_box); })); @@ -178,14 +178,17 @@ impl Window { .poe_list .set_selected_cb(clone!(@strong result => move |poe| { result.leaflet.set_visible_child(&result.navigator.widget); - match poe { - PersonOrEnsemble::Person(person) => { - result.navigator.clone().replace(PersonScreen::new(result.backend.clone(), person.clone())); + let poe = poe.to_owned(); + spawn!(@clone result, async move { + match poe { + PersonOrEnsemble::Person(person) => { + replace!(result.navigator, PersonScreen, person.clone()).await; + } + PersonOrEnsemble::Ensemble(ensemble) => { + replace!(result.navigator, EnsembleScreen, ensemble.clone()).await; + } } - PersonOrEnsemble::Ensemble(ensemble) => { - result.navigator.clone().replace(EnsembleScreen::new(result.backend.clone(), ensemble.clone())); - } - } + }); })); result