├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── LICENSE.md
├── app
├── App_Resources
│ ├── Android
│ │ ├── AndroidManifest.xml
│ │ ├── app.gradle
│ │ ├── drawable-hdpi
│ │ │ ├── background.png
│ │ │ ├── ic_add_white.png
│ │ │ ├── icon.png
│ │ │ ├── logo.png
│ │ │ └── logo_small.png
│ │ ├── drawable-ldpi
│ │ │ ├── background.png
│ │ │ ├── ic_add_white.png
│ │ │ ├── icon.png
│ │ │ ├── logo.png
│ │ │ └── logo_small.png
│ │ ├── drawable-mdpi
│ │ │ ├── background.png
│ │ │ ├── ic_add_white.png
│ │ │ ├── icon.png
│ │ │ ├── logo.png
│ │ │ └── logo_small.png
│ │ ├── drawable-nodpi
│ │ │ └── splash_screen.xml
│ │ ├── drawable-xhdpi
│ │ │ ├── background.png
│ │ │ ├── icon.png
│ │ │ ├── logo.png
│ │ │ └── logo_small.png
│ │ ├── drawable-xxhdpi
│ │ │ ├── background.png
│ │ │ ├── icon.png
│ │ │ ├── logo.png
│ │ │ └── logo_small.png
│ │ ├── drawable-xxxhdpi
│ │ │ ├── background.png
│ │ │ ├── icon.png
│ │ │ ├── logo.png
│ │ │ └── logo_small.png
│ │ ├── settings.gradle
│ │ ├── settings.json
│ │ ├── values-de
│ │ │ └── strings.xml
│ │ ├── values-v21
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ └── iOS
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── icon-1024.png
│ │ │ ├── icon-29.png
│ │ │ ├── icon-29@2x.png
│ │ │ ├── icon-29@3x.png
│ │ │ ├── icon-40.png
│ │ │ ├── icon-40@2x.png
│ │ │ ├── icon-40@3x.png
│ │ │ ├── icon-60@2x.png
│ │ │ ├── icon-60@3x.png
│ │ │ ├── icon-76.png
│ │ │ ├── icon-76@2x.png
│ │ │ └── icon-83.5@2x.png
│ │ ├── Contents.json
│ │ ├── LaunchImage.launchimage
│ │ │ ├── Contents.json
│ │ │ ├── Default-1125h.png
│ │ │ ├── Default-568h@2x.png
│ │ │ ├── Default-667h@2x.png
│ │ │ ├── Default-736h@3x.png
│ │ │ ├── Default-Landscape-X.png
│ │ │ ├── Default-Landscape.png
│ │ │ ├── Default-Landscape@2x.png
│ │ │ ├── Default-Landscape@3x.png
│ │ │ ├── Default-Portrait.png
│ │ │ ├── Default-Portrait@2x.png
│ │ │ ├── Default.png
│ │ │ └── Default@2x.png
│ │ ├── LaunchScreen.AspectFill.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchScreen-AspectFill.png
│ │ │ └── LaunchScreen-AspectFill@2x.png
│ │ └── LaunchScreen.Center.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchScreen-Center.png
│ │ │ └── LaunchScreen-Center@2x.png
│ │ ├── Info.plist
│ │ ├── LaunchScreen.storyboard
│ │ ├── build.xcconfig
│ │ ├── de.lproj
│ │ ├── InfoPlist.strings
│ │ └── Localizable.strings
│ │ ├── en.lproj
│ │ ├── InfoPlist.strings
│ │ └── Localizable.strings
│ │ ├── ic_add_white.png
│ │ ├── logo_small.png
│ │ ├── logo_small@2x.png
│ │ └── logo_small@3x.png
├── _app-common.scss
├── _app-variables.scss
├── app-routing.module.ts
├── app.android.scss
├── app.component.html
├── app.component.ts
├── app.ios.scss
├── app.module.ngfactory.d.ts
├── app.module.ts
├── fonts
│ ├── FontAwesome.otf
│ ├── font-awesome.css
│ └── fontawesome-webfont.ttf
├── i18n
│ ├── de.json
│ └── en.default.json
├── main.aot.ts
├── main.ts
├── model
│ ├── Article.ts
│ ├── Articles.ts
│ ├── Comment.ts
│ ├── Profile.ts
│ └── User.ts
├── module
│ ├── article
│ │ ├── article.module.ts
│ │ ├── article.routing.ts
│ │ ├── article.scss
│ │ ├── edit-article.component.html
│ │ ├── edit-article.component.ts
│ │ ├── list-articles.component.html
│ │ ├── list-articles.component.ts
│ │ ├── view-article.component.html
│ │ └── view-article.component.ts
│ ├── comment
│ │ ├── comment.module.ts
│ │ ├── comment.routing.ts
│ │ ├── comment.scss
│ │ ├── list-comments.component.html
│ │ ├── list-comments.component.ts
│ │ ├── write-comment-modal.component.html
│ │ └── write-comment-modal.component.ts
│ ├── home
│ │ ├── about-modal.component.html
│ │ ├── about-modal.component.ts
│ │ ├── home.component.html
│ │ ├── home.component.ts
│ │ ├── home.module.ts
│ │ ├── home.routing.ts
│ │ ├── home.scss
│ │ ├── settings.component.html
│ │ └── settings.component.ts
│ └── user
│ │ ├── edit-profile.component.html
│ │ ├── edit-profile.component.ts
│ │ ├── login.component.html
│ │ ├── login.component.ts
│ │ ├── profile.component.html
│ │ ├── profile.component.ts
│ │ ├── user.module.ts
│ │ ├── user.routing.ts
│ │ └── user.scss
├── package.json
├── service
│ ├── AbstractHttpService.ts
│ ├── ConduitService.ts
│ ├── UserService.ts
│ └── service.module.ts
├── util
│ └── StatusBar.ts
├── vendor-platform.android.ts
├── vendor-platform.ios.ts
└── vendor.ts
├── assets
├── feature.png
├── icon.png
├── screenshot_1.png
├── screenshot_2.png
├── screenshot_3.png
└── screenshot_4.png
├── e2e
├── conduit.e2e-spec.ts
├── config
│ ├── appium.capabilities.json
│ └── mocha.opts
├── home.ts
├── setup.ts
└── tsconfig.json
├── logo.png
├── package-lock.json
├── package.json
├── readme.md
├── references.d.ts
├── tsconfig.esm.json
├── tsconfig.json
├── tslint.json
└── webpack.config.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Use LF for shell scripts
2 | *.sh eol=lf
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Smartphone (please complete the following information):**
24 | - Device: [e.g. iPhone6]
25 | - OS: [e.g. iOS8.1]
26 | - Browser [e.g. stock browser, safari]
27 | - Version [e.g. 22]
28 |
29 | **Additional context**
30 | Add any other context about the problem here.
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /bower_components
6 | /platforms
7 | /hooks
8 | /lib
9 | /debugger
10 | /dist
11 | /report
12 |
13 | /app/**/*.map
14 | /app/**/*.js
15 | /app/**/*.css
16 |
17 | **/*.log
18 | /tags
19 |
20 | # IDEs and editors
21 | /.idea
22 | .project
23 | .classpath
24 | *.launch
25 | .settings/
26 | .vscode/
27 | .cloud/
28 |
29 | # Tests
30 | /e2e/reports
31 | /e2e/**/*.js
32 |
33 | #
34 | *.bat
35 |
36 | #System Files
37 | .DS_Store
38 | Thumbs.db
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Savas Ziplies
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/app/App_Resources/Android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/app.gradle:
--------------------------------------------------------------------------------
1 | // Add your native dependencies here:
2 |
3 | // Uncomment to add recyclerview-v7 dependency
4 | //dependencies {
5 | // compile 'com.android.support:recyclerview-v7:+'
6 | //}
7 |
8 | android {
9 | defaultConfig {
10 | generatedDensities = []
11 | applicationId = "com.insanitydesign.nativescriptrealworldexampleapp"
12 | }
13 | aaptOptions {
14 | additionalParameters "--no-version-vectors"
15 | }
16 | }
17 |
18 | def settingsGradlePath
19 |
20 | if(project.hasProperty("appResourcesPath")){
21 | settingsGradlePath = "$project.appResourcesPath/Android/settings.gradle";
22 | } else {
23 | settingsGradlePath = "$rootDir/../../app/App_Resources/Android/settings.gradle";
24 | }
25 |
26 | def settingsGradleFile = new File(settingsGradlePath);
27 |
28 | if(settingsGradleFile.exists())
29 | {
30 | apply from: settingsGradleFile;
31 | }
32 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-hdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-hdpi/background.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-hdpi/ic_add_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-hdpi/ic_add_white.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-hdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-hdpi/logo.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-hdpi/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-hdpi/logo_small.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-ldpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-ldpi/background.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-ldpi/ic_add_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-ldpi/ic_add_white.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-ldpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-ldpi/logo.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-ldpi/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-ldpi/logo_small.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-mdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-mdpi/background.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-mdpi/ic_add_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-mdpi/ic_add_white.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-mdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-mdpi/logo.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-mdpi/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-mdpi/logo_small.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-nodpi/splash_screen.xml:
--------------------------------------------------------------------------------
1 |
2 | -
3 |
4 |
5 | -
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xhdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xhdpi/background.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xhdpi/icon.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xhdpi/logo.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xhdpi/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xhdpi/logo_small.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xxhdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xxhdpi/background.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xxhdpi/icon.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xxhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xxhdpi/logo.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xxhdpi/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xxhdpi/logo_small.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xxxhdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xxxhdpi/background.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xxxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xxxhdpi/icon.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xxxhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xxxhdpi/logo.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/drawable-xxxhdpi/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/Android/drawable-xxxhdpi/logo_small.png
--------------------------------------------------------------------------------
/app/App_Resources/Android/settings.gradle:
--------------------------------------------------------------------------------
1 | import groovy.json.JsonSlurper
2 |
3 | task replaceSettings {
4 | description "Replaces configuration settings."
5 | def jsonSlurper = new JsonSlurper()
6 | def pathToSettingsJson
7 |
8 | if (project.hasProperty("appResourcesPath")) {
9 | pathToSettingsJson = "$project.appResourcesPath/Android/settings.json";
10 | } else {
11 | pathToSettingsJson = "$rootDir/../../app/App_Resources/Android/settings.json";
12 | }
13 |
14 | def settingsJsonFile = file(pathToSettingsJson);
15 | def settingsResolvedPath = settingsJsonFile.getAbsolutePath();
16 |
17 | if(settingsJsonFile.exists())
18 | {
19 | println "\t Applying settings from $settingsResolvedPath"
20 | String settingsGradleTemplate = """android {
21 | defaultConfig {
22 | applicationId = "__appId__"
23 |
24 | if (__minSdkVersion__) {
25 | minSdkVersion = __minSdkVersion__
26 | }
27 |
28 | if (__targetSdkVersion__) {
29 | targetSdkVersion = __targetSdkVersion__
30 | }
31 | }
32 | }"""
33 |
34 | def settingsJsonContent = settingsJsonFile.getText("UTF-8");
35 | def settingsMap = jsonSlurper.parseText(settingsJsonContent);
36 |
37 | for ( setting in settingsMap ) {
38 | def placeholder = "__${setting.key}__";
39 | def settingValue = setting.value;
40 |
41 | if (settingValue == null) {
42 | settingValue = false
43 | }
44 |
45 | settingsGradleTemplate = settingsGradleTemplate.replaceAll( placeholder, settingValue as String);
46 | }
47 |
48 | new File( "$rootDir/temp_setting.gradle" ).write( settingsGradleTemplate, 'UTF-8');
49 | apply from: "$rootDir/temp_setting.gradle";
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/settings.json:
--------------------------------------------------------------------------------
1 | {"appId":"com.insanitydesign.nativescriptrealworldexampleapp","minSdkVersion":23,"targetSdkVersion":null}
--------------------------------------------------------------------------------
/app/App_Resources/Android/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | "Das darf nicht leer sein!"
4 | "Bitte einloggen!"
5 | "Ein Fehler ist aufgetreten!"
6 | "Möchtest du diesen Kommentar wirklich löschen?"
7 | "Veröffentlichen"
8 | "Reaktion auf"
9 | "Schreib einen Kommentar..."
10 | "Keine Kommentare!"
11 | "Favorisierte Beiträge"
12 | "Meine Beiträge"
13 | "Entfolgen"
14 | "Folgen"
15 | "Profil aktualisiert!"
16 | "Bitte einen Nutzernamen angeben!"
17 | "Bitte gib eine korrekte E-Mail Adresse ein!"
18 | "Bitte die gleichen Passwörter eingeben!"
19 | "Bitte alle geforderten Felder füllen!"
20 | "Noch kein Account?"
21 | "Zurück zu Home"
22 | "Zurück zum Login"
23 | "Bild"
24 | "Bio"
25 | "Passwort bestätigen"
26 | "Passwort"
27 | "E-Mail"
28 | "Nutzername"
29 | "Registrieren"
30 | "Logout"
31 | "Login"
32 | "Beitrag gelöscht!"
33 | "Beitrag gespeichert!"
34 | "Möchtest du diesen Betrag wirklich löschen?"
35 | "Tags"
36 | "Inhalt"
37 | "Beschreibung"
38 | "Titel"
39 | "Keine Artikel gefunden!"
40 | "Beitrag löschen"
41 | "Beitrag bearbeiten"
42 | "Beitrag schreiben"
43 | "Beiträge geladen"
44 | "Lade Beiträge..."
45 | "Globale Artikel"
46 | "Deine Artikel"
47 | "Du wurdest erfolgreich ausgeloggt!"
48 | "Erstellt von https://github.com/nea/"
49 | "Über"
50 | "Einstellungen"
51 | "Home"
52 | "Logout"
53 | "Login/Registrieren"
54 | "Zurücksetzen"
55 | "Bitte eine korrekt URL eingeben"
56 | "Eine URL, welche die Conduit Spec implementiert"
57 | "Backend URL"
58 | "Einstellungen"
59 | "Dieses Beispielprojekt wude durch INsanityDesign.com für RealWorld.io erstellt und ist lizensiert unter der MIT. Du findest den Source auf GitHub.com."
60 | "RealWorld Beispielprojekte wurden durch Thinkster.io gestartet um echte Szenarien als Tutorials anzubieten um zu zeigen, wie verschiedene Backend/Frontend Programmiersprachen und/oder Frameworks in deinem Projekt funktionieren könnten."
61 | "Diese App ist ein Beispielprojekt eines Medium.com Klon (genannt Conduit) gebaut in NativeScript, welcher mit verschiedenen Backends verbunden werden kann von "
62 | "schließen"
63 | "Speichern"
64 | "Eine NativeScript RealWorld Beispiel App"
65 | "Conduit"
66 | "Conduit"
67 | "Conduit"
68 | "Conduit"
69 |
70 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/values-v21/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3d5afe
4 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
14 |
15 |
16 |
19 |
20 |
23 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F5F5F5
4 | #757575
5 | #33B5E5
6 | #272734
7 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | "This may not be empty!"
4 | "Please login!"
5 | "An error occured!"
6 | "Are you sure you want to delete this comment?"
7 | "Publish"
8 | "In response to"
9 | "Write a response..."
10 | "No comments!"
11 | "Favorited Articles"
12 | "My Articles"
13 | "Unfollow"
14 | "Follow"
15 | "Profile updated!"
16 | "Please provide a username!"
17 | "Please enter a valid Email!"
18 | "Please provide matching passwords!"
19 | "Please provide all requested inputs!"
20 | "Don\'t have an account?"
21 | "Back to home"
22 | "Back to login"
23 | "Image"
24 | "Bio"
25 | "Confirm password"
26 | "Password"
27 | "Email"
28 | "Username"
29 | "Register"
30 | "Logout"
31 | "Login"
32 | "Article deleted!"
33 | "Article saved!"
34 | "Are you sure you want to delete this article?"
35 | "Tags"
36 | "Body"
37 | "Description"
38 | "Title"
39 | "No articles found!"
40 | "Delete article"
41 | "Edit article"
42 | "Add article"
43 | "Loaded articles"
44 | "Loading articles..."
45 | "Global Feed"
46 | "Your Feed"
47 | "You have been logged out successfully!"
48 | "Created by https://github.com/nea/"
49 | "About"
50 | "Settings"
51 | "Home"
52 | "Logout"
53 | "Login/Register"
54 | "Reset"
55 | "Please enter a valid URL"
56 | "URL implementing the Conduit backend spec"
57 | "Backend URL"
58 | "Settings"
59 | "This example stack has been created by INsanityDesign.com for RealWorld.io and is licensed under MIT. You can checkout the full source at GitHub.com."
60 | "RealWorld example projects have been initiated by Thinkster.io to provide a real-world scenario as tutorial to see how any different backend/frontend programming language and/or framework could work out in your project."
61 | "This app is an exemplary project to show how a Medium.com clone (called Conduit) is built using NativeScript to connect to any other backend from "
62 | "Close"
63 | "Save"
64 | "A NativeScript RealWorld Example App"
65 | "Conduit"
66 | "Conduit"
67 | "Conduit"
68 | "Conduit"
69 |
70 |
--------------------------------------------------------------------------------
/app/App_Resources/Android/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
17 |
18 |
20 |
21 |
22 |
29 |
30 |
32 |
33 |
34 |
39 |
40 |
42 |
43 |
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "icon-29.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "icon-29@2x.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "icon-29@3x.png",
19 | "scale" : "3x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "icon-40@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "icon-40@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "icon-60@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "icon-60@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "29x29",
47 | "idiom" : "ipad",
48 | "filename" : "icon-29.png",
49 | "scale" : "1x"
50 | },
51 | {
52 | "size" : "29x29",
53 | "idiom" : "ipad",
54 | "filename" : "icon-29@2x.png",
55 | "scale" : "2x"
56 | },
57 | {
58 | "size" : "40x40",
59 | "idiom" : "ipad",
60 | "filename" : "icon-40.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "40x40",
65 | "idiom" : "ipad",
66 | "filename" : "icon-40@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "76x76",
71 | "idiom" : "ipad",
72 | "filename" : "icon-76.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "76x76",
77 | "idiom" : "ipad",
78 | "filename" : "icon-76@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "83.5x83.5",
83 | "idiom" : "ipad",
84 | "filename" : "icon-83.5@2x.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "1024x1024",
89 | "idiom" : "ios-marketing",
90 | "filename" : "icon-1024.png",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "extent" : "full-screen",
5 | "idiom" : "iphone",
6 | "subtype" : "2436h",
7 | "filename" : "Default-1125h.png",
8 | "minimum-system-version" : "11.0",
9 | "orientation" : "portrait",
10 | "scale" : "3x"
11 | },
12 | {
13 | "orientation" : "landscape",
14 | "idiom" : "iphone",
15 | "extent" : "full-screen",
16 | "filename" : "Default-Landscape-X.png",
17 | "minimum-system-version" : "11.0",
18 | "subtype" : "2436h",
19 | "scale" : "3x"
20 | },
21 | {
22 | "extent" : "full-screen",
23 | "idiom" : "iphone",
24 | "subtype" : "736h",
25 | "filename" : "Default-736h@3x.png",
26 | "minimum-system-version" : "8.0",
27 | "orientation" : "portrait",
28 | "scale" : "3x"
29 | },
30 | {
31 | "extent" : "full-screen",
32 | "idiom" : "iphone",
33 | "subtype" : "736h",
34 | "filename" : "Default-Landscape@3x.png",
35 | "minimum-system-version" : "8.0",
36 | "orientation" : "landscape",
37 | "scale" : "3x"
38 | },
39 | {
40 | "extent" : "full-screen",
41 | "idiom" : "iphone",
42 | "subtype" : "667h",
43 | "filename" : "Default-667h@2x.png",
44 | "minimum-system-version" : "8.0",
45 | "orientation" : "portrait",
46 | "scale" : "2x"
47 | },
48 | {
49 | "orientation" : "portrait",
50 | "idiom" : "iphone",
51 | "filename" : "Default@2x.png",
52 | "extent" : "full-screen",
53 | "minimum-system-version" : "7.0",
54 | "scale" : "2x"
55 | },
56 | {
57 | "extent" : "full-screen",
58 | "idiom" : "iphone",
59 | "subtype" : "retina4",
60 | "filename" : "Default-568h@2x.png",
61 | "minimum-system-version" : "7.0",
62 | "orientation" : "portrait",
63 | "scale" : "2x"
64 | },
65 | {
66 | "orientation" : "portrait",
67 | "idiom" : "ipad",
68 | "filename" : "Default-Portrait.png",
69 | "extent" : "full-screen",
70 | "minimum-system-version" : "7.0",
71 | "scale" : "1x"
72 | },
73 | {
74 | "orientation" : "landscape",
75 | "idiom" : "ipad",
76 | "filename" : "Default-Landscape.png",
77 | "extent" : "full-screen",
78 | "minimum-system-version" : "7.0",
79 | "scale" : "1x"
80 | },
81 | {
82 | "orientation" : "portrait",
83 | "idiom" : "ipad",
84 | "filename" : "Default-Portrait@2x.png",
85 | "extent" : "full-screen",
86 | "minimum-system-version" : "7.0",
87 | "scale" : "2x"
88 | },
89 | {
90 | "orientation" : "landscape",
91 | "idiom" : "ipad",
92 | "filename" : "Default-Landscape@2x.png",
93 | "extent" : "full-screen",
94 | "minimum-system-version" : "7.0",
95 | "scale" : "2x"
96 | },
97 | {
98 | "orientation" : "portrait",
99 | "idiom" : "iphone",
100 | "filename" : "Default.png",
101 | "extent" : "full-screen",
102 | "scale" : "1x"
103 | },
104 | {
105 | "orientation" : "portrait",
106 | "idiom" : "iphone",
107 | "filename" : "Default@2x.png",
108 | "extent" : "full-screen",
109 | "scale" : "2x"
110 | },
111 | {
112 | "orientation" : "portrait",
113 | "idiom" : "iphone",
114 | "filename" : "Default-568h@2x.png",
115 | "extent" : "full-screen",
116 | "subtype" : "retina4",
117 | "scale" : "2x"
118 | },
119 | {
120 | "orientation" : "portrait",
121 | "idiom" : "ipad",
122 | "extent" : "to-status-bar",
123 | "scale" : "1x"
124 | },
125 | {
126 | "orientation" : "portrait",
127 | "idiom" : "ipad",
128 | "filename" : "Default-Portrait.png",
129 | "extent" : "full-screen",
130 | "scale" : "1x"
131 | },
132 | {
133 | "orientation" : "landscape",
134 | "idiom" : "ipad",
135 | "extent" : "to-status-bar",
136 | "scale" : "1x"
137 | },
138 | {
139 | "orientation" : "landscape",
140 | "idiom" : "ipad",
141 | "filename" : "Default-Landscape.png",
142 | "extent" : "full-screen",
143 | "scale" : "1x"
144 | },
145 | {
146 | "orientation" : "portrait",
147 | "idiom" : "ipad",
148 | "extent" : "to-status-bar",
149 | "scale" : "2x"
150 | },
151 | {
152 | "orientation" : "portrait",
153 | "idiom" : "ipad",
154 | "filename" : "Default-Portrait@2x.png",
155 | "extent" : "full-screen",
156 | "scale" : "2x"
157 | },
158 | {
159 | "orientation" : "landscape",
160 | "idiom" : "ipad",
161 | "extent" : "to-status-bar",
162 | "scale" : "2x"
163 | },
164 | {
165 | "orientation" : "landscape",
166 | "idiom" : "ipad",
167 | "filename" : "Default-Landscape@2x.png",
168 | "extent" : "full-screen",
169 | "scale" : "2x"
170 | }
171 | ],
172 | "info" : {
173 | "version" : 1,
174 | "author" : "xcode"
175 | }
176 | }
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchScreen-AspectFill.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchScreen-AspectFill@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchScreen-Center.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchScreen-Center@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Conduit
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | Conduit
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.1.1
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.1.1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiresFullScreen
28 |
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationLandscapeLeft
36 | UIInterfaceOrientationLandscapeRight
37 | UIInterfaceOrientationPortrait
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/App_Resources/iOS/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/App_Resources/iOS/build.xcconfig:
--------------------------------------------------------------------------------
1 | // You can add custom settings here
2 | // for example you can uncomment the following line to force distribution code signing
3 | // CODE_SIGN_IDENTITY = iPhone Distribution
4 | // To build for device with XCode 8 you need to specify your development team.
5 | // DEVELOPMENT_TEAM = YOUR_TEAM_ID
6 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
7 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
8 | TARGETED_DEVICE_FAMILY = 1,2;
9 |
--------------------------------------------------------------------------------
/app/App_Resources/iOS/de.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | "CFBundleDisplayName" = "Conduit";
2 | "CFBundleName" = "Conduit";
3 |
--------------------------------------------------------------------------------
/app/App_Resources/iOS/de.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | "_validation_notEmpty_1nAY96" = "Das darf nicht leer sein!";
2 | "_error_login_2ri64q" = "Bitte einloggen!";
3 | "_error_general_29Dwqq" = "Ein Fehler ist aufgetreten!";
4 | "_comment_delete_confirm_Z1NRVEk" = "Möchtest du diesen Kommentar wirklich löschen?";
5 | "_comment_publish_5EfXd" = "Veröffentlichen";
6 | "_comment_inResponse_Z8mVuK" = "Reaktion auf";
7 | "_comment_response_Vrx3w" = "Schreib einen Kommentar...";
8 | "_comment_empty_262VIT" = "Keine Kommentare!";
9 | "_user_feed_favorited_2txVxh" = "Favorisierte Beiträge";
10 | "_user_feed_user_15fqdE" = "Meine Beiträge";
11 | "_user_unfollow_Z2jCfUV" = "Entfolgen";
12 | "_user_follow_Z1fU5gH" = "Folgen";
13 | "_user_form_saved_Z2iuyuC" = "Profil aktualisiert!";
14 | "_user_form_error_validUsername_2j4WBp" = "Bitte einen Nutzernamen angeben!";
15 | "_user_form_error_validEmail_1kuQBh" = "Bitte gib eine korrekte E-Mail Adresse ein!";
16 | "_user_form_error_passwordMismatch_Z1KPdW9" = "Bitte die gleichen Passwörter eingeben!";
17 | "_user_form_error_missing_Z1xz1qv" = "Bitte alle geforderten Felder füllen!";
18 | "_user_form_needAccount_ZBcBvf" = "Noch kein Account?";
19 | "_user_form_backToHome_Z11n6D6" = "Zurück zu Home";
20 | "_user_form_backToLogin_Z1bhfFn" = "Zurück zum Login";
21 | "_user_form_image_Z2j8KJM" = "Bild";
22 | "_user_form_bio_Z1iRqqr" = "Bio";
23 | "_user_form_passwordConfirm_Z17xtkM" = "Passwort bestätigen";
24 | "_user_form_password_1plOy7" = "Passwort";
25 | "_user_form_email_Z2jp2up" = "E-Mail";
26 | "_user_form_username_ZkIqne" = "Nutzername";
27 | "_user_register_2ol0nM" = "Registrieren";
28 | "_user_logout_Z13vVPg" = "Logout";
29 | "_user_login_BVAAY" = "Login";
30 | "_article_form_deleted_UwfYY" = "Beitrag gelöscht!";
31 | "_article_form_saved_ZbwLkB" = "Beitrag gespeichert!";
32 | "_article_form_confirmDelete_25feI5" = "Möchtest du diesen Betrag wirklich löschen?";
33 | "_article_form_tags_ZunJE7" = "Tags";
34 | "_article_form_body_Zuq3aL" = "Inhalt";
35 | "_article_form_description_Z32jJl" = "Beschreibung";
36 | "_article_form_title_ZbrExP" = "Titel";
37 | "_article_empty_ZSs3Sg" = "Keine Artikel gefunden!";
38 | "_article_delete_Z2hRvR1" = "Beitrag löschen";
39 | "_article_edit_ZvLn2r" = "Beitrag bearbeiten";
40 | "_article_add_X0N7q" = "Beitrag schreiben";
41 | "_article_loaded_Z20GeRU" = "Beiträge geladen";
42 | "_article_loading_Z1k4Nqs" = "Lade Beiträge...";
43 | "_article_feed_global_ea326" = "Globale Artikel";
44 | "_article_feed_user_unCrd" = "Deine Artikel";
45 | "_drawer_feedback_loggedOut_1JaKRB" = "Du wurdest erfolgreich ausgeloggt!";
46 | "_drawer_createdBy_Ze1Mjk" = "Erstellt von https://github.com/nea/";
47 | "_drawer_about_Z1I6Qod" = "Über";
48 | "_drawer_settings_FIu8R" = "Einstellungen";
49 | "_drawer_home_gCUhF" = "Home";
50 | "_drawer_logout_Z2aSUPE" = "Logout";
51 | "_drawer_login_Z1HlqmX" = "Login/Registrieren";
52 | "_settings_reset_1zRWpY" = "Zurücksetzen";
53 | "_settings_urlWarning_ZlnNDI" = "Bitte eine korrekt URL eingeben";
54 | "_settings_urlHint_ZJdzNL" = "Eine URL, welche die Conduit Spec implementiert";
55 | "_settings_url_Cvh8y" = "Backend URL";
56 | "_settings_title_1A1C8V" = "Einstellungen";
57 | "_about_insanitydesign_2vmxt9" = "Dieses Beispielprojekt wude durch INsanityDesign.com für RealWorld.io erstellt und ist lizensiert unter der MIT. Du findest den Source auf GitHub.com.";
58 | "_about_thinkster_Z1Mxmg9" = "RealWorld Beispielprojekte wurden durch Thinkster.io gestartet um echte Szenarien als Tutorials anzubieten um zu zeigen, wie verschiedene Backend/Frontend Programmiersprachen und/oder Frameworks in deinem Projekt funktionieren könnten.";
59 | "_about_realworld_Z1YNVKo" = "Diese App ist ein Beispielprojekt eines Medium.com Klon (genannt Conduit) gebaut in NativeScript, welcher mit verschiedenen Backends verbunden werden kann von ";
60 | "_general_close_1X64mW" = "schließen";
61 | "_general_save_1GXi6l" = "Speichern";
62 | "_description_Z22xK3K" = "Eine NativeScript RealWorld Beispiel App";
63 | "_title_7XfKt" = "Conduit";
64 | "_app_name_1k3Sbz" = "Conduit";
65 |
--------------------------------------------------------------------------------
/app/App_Resources/iOS/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | "CFBundleDisplayName" = "Conduit";
2 | "CFBundleName" = "Conduit";
3 |
--------------------------------------------------------------------------------
/app/App_Resources/iOS/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | "_validation_notEmpty_1nAY96" = "This may not be empty!";
2 | "_error_login_2ri64q" = "Please login!";
3 | "_error_general_29Dwqq" = "An error occured!";
4 | "_comment_delete_confirm_Z1NRVEk" = "Are you sure you want to delete this comment?";
5 | "_comment_publish_5EfXd" = "Publish";
6 | "_comment_inResponse_Z8mVuK" = "In response to";
7 | "_comment_response_Vrx3w" = "Write a response...";
8 | "_comment_empty_262VIT" = "No comments!";
9 | "_user_feed_favorited_2txVxh" = "Favorited Articles";
10 | "_user_feed_user_15fqdE" = "My Articles";
11 | "_user_unfollow_Z2jCfUV" = "Unfollow";
12 | "_user_follow_Z1fU5gH" = "Follow";
13 | "_user_form_saved_Z2iuyuC" = "Profile updated!";
14 | "_user_form_error_validUsername_2j4WBp" = "Please provide a username!";
15 | "_user_form_error_validEmail_1kuQBh" = "Please enter a valid Email!";
16 | "_user_form_error_passwordMismatch_Z1KPdW9" = "Please provide matching passwords!";
17 | "_user_form_error_missing_Z1xz1qv" = "Please provide all requested inputs!";
18 | "_user_form_needAccount_ZBcBvf" = "Don't have an account?";
19 | "_user_form_backToHome_Z11n6D6" = "Back to home";
20 | "_user_form_backToLogin_Z1bhfFn" = "Back to login";
21 | "_user_form_image_Z2j8KJM" = "Image";
22 | "_user_form_bio_Z1iRqqr" = "Bio";
23 | "_user_form_passwordConfirm_Z17xtkM" = "Confirm password";
24 | "_user_form_password_1plOy7" = "Password";
25 | "_user_form_email_Z2jp2up" = "Email";
26 | "_user_form_username_ZkIqne" = "Username";
27 | "_user_register_2ol0nM" = "Register";
28 | "_user_logout_Z13vVPg" = "Logout";
29 | "_user_login_BVAAY" = "Login";
30 | "_article_form_deleted_UwfYY" = "Article deleted!";
31 | "_article_form_saved_ZbwLkB" = "Article saved!";
32 | "_article_form_confirmDelete_25feI5" = "Are you sure you want to delete this article?";
33 | "_article_form_tags_ZunJE7" = "Tags";
34 | "_article_form_body_Zuq3aL" = "Body";
35 | "_article_form_description_Z32jJl" = "Description";
36 | "_article_form_title_ZbrExP" = "Title";
37 | "_article_empty_ZSs3Sg" = "No articles found!";
38 | "_article_delete_Z2hRvR1" = "Delete article";
39 | "_article_edit_ZvLn2r" = "Edit article";
40 | "_article_add_X0N7q" = "Add article";
41 | "_article_loaded_Z20GeRU" = "Loaded articles";
42 | "_article_loading_Z1k4Nqs" = "Loading articles...";
43 | "_article_feed_global_ea326" = "Global Feed";
44 | "_article_feed_user_unCrd" = "Your Feed";
45 | "_drawer_feedback_loggedOut_1JaKRB" = "You have been logged out successfully!";
46 | "_drawer_createdBy_Ze1Mjk" = "Created by https://github.com/nea/";
47 | "_drawer_about_Z1I6Qod" = "About";
48 | "_drawer_settings_FIu8R" = "Settings";
49 | "_drawer_home_gCUhF" = "Home";
50 | "_drawer_logout_Z2aSUPE" = "Logout";
51 | "_drawer_login_Z1HlqmX" = "Login/Register";
52 | "_settings_reset_1zRWpY" = "Reset";
53 | "_settings_urlWarning_ZlnNDI" = "Please enter a valid URL";
54 | "_settings_urlHint_ZJdzNL" = "URL implementing the Conduit backend spec";
55 | "_settings_url_Cvh8y" = "Backend URL";
56 | "_settings_title_1A1C8V" = "Settings";
57 | "_about_insanitydesign_2vmxt9" = "This example stack has been created by INsanityDesign.com for RealWorld.io and is licensed under MIT. You can checkout the full source at GitHub.com.";
58 | "_about_thinkster_Z1Mxmg9" = "RealWorld example projects have been initiated by Thinkster.io to provide a real-world scenario as tutorial to see how any different backend/frontend programming language and/or framework could work out in your project.";
59 | "_about_realworld_Z1YNVKo" = "This app is an exemplary project to show how a Medium.com clone (called Conduit) is built using NativeScript to connect to any other backend from ";
60 | "_general_close_1X64mW" = "Close";
61 | "_general_save_1GXi6l" = "Save";
62 | "_description_Z22xK3K" = "A NativeScript RealWorld Example App";
63 | "_title_7XfKt" = "Conduit";
64 | "_app_name_1k3Sbz" = "Conduit";
65 |
--------------------------------------------------------------------------------
/app/App_Resources/iOS/ic_add_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/ic_add_white.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/logo_small.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/logo_small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/logo_small@2x.png
--------------------------------------------------------------------------------
/app/App_Resources/iOS/logo_small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/App_Resources/iOS/logo_small@3x.png
--------------------------------------------------------------------------------
/app/_app-common.scss:
--------------------------------------------------------------------------------
1 | // Import app variables
2 | @import 'app-variables';
3 | // Import the theme’s main ruleset.
4 | @import '~nativescript-theme-core/scss/index';
5 | // Place any CSS rules you want to apply on both iOS and Android here.
6 | // This is where the vast majority of your CSS code goes.
7 | .fa {
8 | font-family: FontAwesome, fontawesome-webfont;
9 | }
10 |
11 | :disabled {
12 | opacity: 0.5;
13 | }
14 |
15 | ActionBar {
16 | background-color: $brand-primary;
17 | color: white;
18 | }
19 |
20 | ActionBar .h2 {
21 | color: white;
22 | }
23 |
24 | ActionBar .fa {
25 | font-size: 20;
26 | }
27 |
28 | ActionBar .btn-rounded {
29 | border-color: $brand-primary;
30 | border-width: 1;
31 | border-radius: 5;
32 | padding: 5;
33 | }
34 |
35 | .btn-danger {
36 | @extend .btn-primary;
37 | color: black;
38 | background-color: red;
39 | }
40 |
41 | // Drawer navigation custom styles
42 | $sidedrawer-header-image-size: 60;
43 | $sidedrawer-header-image-offset-top: 20;
44 | $sidedrawer-header-image-offset-bottom: 5;
45 | $sidedrawer-list-item-offset-left: 15;
46 | $sidedrawer-list-icon-offset: 10;
47 | $sidedrawer-list-icon-size: 20;
48 |
49 | .sidedrawer {
50 | &.sidedrawer-left {
51 | background-color: $ab-background;
52 |
53 | .sidedrawer-header-image {
54 | color: $brand-dark;
55 | height: $sidedrawer-header-image-size;
56 | width: $sidedrawer-header-image-size;
57 | font-size: $sidedrawer-header-image-size;
58 | padding: 0;
59 | margin-bottom: $sidedrawer-header-image-offset-bottom;
60 | margin-top: $sidedrawer-header-image-offset-top;
61 | }
62 |
63 | .footnote {
64 | color: rgba($ab-color, 0.5);
65 | }
66 | }
67 |
68 | .sidedrawer-header {
69 | background-color: $brand-light;
70 |
71 | .sidedrawer-header-brand {
72 | color: $ab-color;
73 | }
74 | }
75 |
76 | .sidedrawer-content {
77 | background-color: $brand-light;
78 | $sidedrawer-list-item-offset-left: 15;
79 | $sidedrawer-list-icon-offset: 10;
80 | $sidedrawer-list-icon-size: 20;
81 |
82 | .sidedrawer-list-item {
83 | padding-left: $sidedrawer-list-item-offset-left;
84 |
85 | Label {
86 | vertical-align: center;
87 | color: $brand-primary;
88 | }
89 |
90 | .fa {
91 | width: $sidedrawer-list-icon-size;
92 | margin-right: $sidedrawer-list-icon-offset;
93 | }
94 |
95 | &.selected {
96 | background-color: $brand-light;
97 | Label {
98 | color: $brand-light-font;
99 | }
100 | }
101 | }
102 | }
103 |
104 | .created-by {
105 | background-color: $brand-primary;
106 | color: $brand-primary-font;
107 | font-style: italic;
108 | font-size: 12;
109 | }
110 | }
--------------------------------------------------------------------------------
/app/_app-variables.scss:
--------------------------------------------------------------------------------
1 | // Import the theme’s variables. If you’re using a color scheme
2 | // other than “light”, switch the path to the alternative scheme,
3 | // for example '~nativescript-theme-core/scss/dark'.
4 | @import '~nativescript-theme-core/scss/light';
5 |
6 | // Customize any of the theme’s variables here, for instance $btn-color: red;
7 | $brand-primary: #5cb85c;
8 | $brand-light: #f3f3f3;
9 | $brand-dark: #818a91;
10 |
11 | $brand-primary-font: white;
12 | $brand-light-font: black;
13 | $brand-dark-font: white;
14 |
15 | $accent: $brand-primary;
--------------------------------------------------------------------------------
/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from "@angular/core";
2 | import { Routes } from "@angular/router";
3 | import { NativeScriptRouterModule } from "nativescript-angular/router";
4 |
5 | const routes: Routes = [
6 | { path: "", redirectTo: "/home", pathMatch: "full" },
7 | ];
8 |
9 | @NgModule({
10 | imports: [NativeScriptRouterModule.forRoot(routes)],
11 | exports: [NativeScriptRouterModule]
12 | })
13 | export class AppRoutingModule { }
14 |
--------------------------------------------------------------------------------
/app/app.android.scss:
--------------------------------------------------------------------------------
1 | @import 'app-common';
2 | @import '~nativescript-theme-core/scss/platforms/index.android';
3 |
4 | // Place any CSS rules you want to apply only on Android here
--------------------------------------------------------------------------------
/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewContainerRef } from "@angular/core";
2 | import { filter } from "rxjs/operators";
3 | import { RouterExtensions } from "nativescript-angular/router";
4 | import { Router, NavigationEnd } from "@angular/router";
5 | import { RadSideDrawer } from "nativescript-ui-sidedrawer";
6 | import * as app from "application";
7 | import { UserService } from "~/service/UserService";
8 | import { Feedback } from "nativescript-feedback";
9 | import { localize } from "nativescript-localize";
10 | import { ModalDialogService } from "nativescript-angular/directives/dialogs";
11 | import { AboutModal } from "~/module/home/about-modal.component";
12 | import * as utils from "utils/utils";
13 |
14 | @Component({
15 | selector: "conduit-app",
16 | templateUrl: "app.component.html",
17 | providers: [ModalDialogService]
18 | })
19 | export class AppComponent {
20 | /** */
21 | protected activatedUrl: string;
22 | /** */
23 | protected feedback: Feedback;
24 |
25 | /**
26 | *
27 | * @param router
28 | * @param routerExtensions
29 | * @param modal
30 | * @param vcRef
31 | * @param userService
32 | */
33 | constructor(
34 | protected router: Router,
35 | protected routerExtensions: RouterExtensions,
36 | protected modal: ModalDialogService,
37 | protected vcRef: ViewContainerRef,
38 | public userService: UserService
39 | ) {
40 | this.feedback = new Feedback();
41 | }
42 |
43 | /**
44 | *
45 | */
46 | public ngOnInit() {
47 | this.activatedUrl = "/home";
48 | this.router.events
49 | .pipe(filter((event: any) => event instanceof NavigationEnd))
50 | .subscribe((event: NavigationEnd) => (this.activatedUrl = event.urlAfterRedirects));
51 | }
52 |
53 | /**
54 | *
55 | */
56 | public onLogout() {
57 | if (this.userService.logout()) {
58 | this.feedback.success({
59 | title: localize("drawer.logout"),
60 | message: localize("drawer.feedback.loggedOut")
61 | });
62 | }
63 | }
64 |
65 | /**
66 | *
67 | * @param url
68 | */
69 | public isSelected(url: string): boolean {
70 | return this.activatedUrl === url;
71 | }
72 |
73 | /**
74 | *
75 | * @param route
76 | */
77 | public onItem(route: string): void {
78 | this.routerExtensions.navigate([route], {
79 | transition: {
80 | name: "fade"
81 | }
82 | });
83 |
84 | const sideDrawer = app.getRootView();
85 | sideDrawer.closeDrawer();
86 | }
87 |
88 | /**
89 | * Show the "About" modal
90 | */
91 | public onAbout() {
92 | this.modal.showModal(AboutModal, {
93 | fullscreen: false,
94 | viewContainerRef: this.vcRef
95 | });
96 | }
97 |
98 | /**
99 | *
100 | */
101 | public onCreated() {
102 | utils.openUrl("https://github.com/nea/");
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app/app.ios.scss:
--------------------------------------------------------------------------------
1 | @import 'app-common';
2 | @import '~nativescript-theme-core/scss/platforms/index.ios';
3 |
4 | // Place any CSS rules you want to apply only on iOS here
--------------------------------------------------------------------------------
/app/app.module.ngfactory.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A dynamically generated module when compiled with AoT.
3 | */
4 | export const AppModuleNgFactory: any;
5 |
--------------------------------------------------------------------------------
/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, NgModuleFactoryLoader, NO_ERRORS_SCHEMA } from "@angular/core";
2 | import { NativeScriptModule } from "nativescript-angular/nativescript.module";
3 | import { NativeScriptHttpClientModule } from "nativescript-angular/http-client";
4 | import { NativeScriptUISideDrawerModule } from "nativescript-ui-sidedrawer/angular";
5 | import { NativeScriptLocalizeModule } from "nativescript-localize/angular";
6 | import { NativeScriptRouterModule } from "nativescript-angular/router";
7 | import { TNSFontIconModule } from "nativescript-ngx-fonticon";
8 |
9 | import { AppRoutingModule } from "./app-routing.module";
10 | import { AppComponent } from "./app.component";
11 |
12 | import { HomeModule } from "./module/home/home.module";
13 | import { ServiceModule } from "./service/service.module";
14 |
15 | @NgModule({
16 | bootstrap: [AppComponent],
17 | imports: [
18 | NativeScriptModule,
19 | NativeScriptRouterModule,
20 | NativeScriptHttpClientModule,
21 | NativeScriptUISideDrawerModule,
22 | NativeScriptLocalizeModule,
23 | AppRoutingModule,
24 | TNSFontIconModule.forRoot({
25 | fa: "./fonts/font-awesome.css"
26 | }),
27 | ServiceModule.forRoot(),
28 | HomeModule
29 | ],
30 | declarations: [AppComponent],
31 | schemas: [NO_ERRORS_SCHEMA]
32 | })
33 | export class AppModule {}
34 |
--------------------------------------------------------------------------------
/app/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/app/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nea/nativescript-realworld-example-app/eb932b7b015b9e4ae0af344c01bb27cca837dd1e/app/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/app/i18n/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "app.name": "Conduit",
3 | "title": "Conduit",
4 | "description": "Eine NativeScript RealWorld Beispiel App",
5 | "general": {
6 | "save": "Speichern",
7 | "close": "schließen"
8 | },
9 | "about": {
10 | "realworld": "Diese App ist ein Beispielprojekt eines Medium.com Klon (genannt Conduit) gebaut in NativeScript, welcher mit verschiedenen Backends verbunden werden kann von ",
11 | "thinkster": "RealWorld Beispielprojekte wurden durch Thinkster.io gestartet um echte Szenarien als Tutorials anzubieten um zu zeigen, wie verschiedene Backend/Frontend Programmiersprachen und/oder Frameworks in deinem Projekt funktionieren könnten.",
12 | "insanitydesign": "Dieses Beispielprojekt wude durch INsanityDesign.com für RealWorld.io erstellt und ist lizensiert unter der MIT. Du findest den Source auf GitHub.com."
13 | },
14 | "settings": {
15 | "title": "Einstellungen",
16 | "url": "Backend URL",
17 | "urlHint": "Eine URL, welche die Conduit Spec implementiert",
18 | "urlWarning": "Bitte eine korrekt URL eingeben",
19 | "reset": "Zurücksetzen"
20 | },
21 | "drawer": {
22 | "login": "Login/Registrieren",
23 | "logout": "Logout",
24 | "home": "Home",
25 | "settings": "Einstellungen",
26 | "about": "Über",
27 | "createdBy": "Erstellt von https://github.com/nea/",
28 | "feedback": {
29 | "loggedOut": "Du wurdest erfolgreich ausgeloggt!"
30 | }
31 | },
32 | "article": {
33 | "feed": {
34 | "user": "Deine Artikel",
35 | "global": "Globale Artikel"
36 | },
37 | "loading": "Lade Beiträge...",
38 | "loaded": "Beiträge geladen",
39 | "add": "Beitrag schreiben",
40 | "edit": "Beitrag bearbeiten",
41 | "delete": "Beitrag löschen",
42 | "empty": "Keine Artikel gefunden!",
43 | "form": {
44 | "title": "Titel",
45 | "description": "Beschreibung",
46 | "body": "Inhalt",
47 | "tags": "Tags",
48 | "confirmDelete": "Möchtest du diesen Betrag wirklich löschen?",
49 | "saved": "Beitrag gespeichert!",
50 | "deleted": "Beitrag gelöscht!"
51 | }
52 | },
53 | "user": {
54 | "login": "Login",
55 | "logout": "Logout",
56 | "register": "Registrieren",
57 | "form": {
58 | "username": "Nutzername",
59 | "email": "E-Mail",
60 | "password": "Passwort",
61 | "passwordConfirm": "Passwort bestätigen",
62 | "bio": "Bio",
63 | "image": "Bild",
64 | "backToLogin": "Zurück zum Login",
65 | "backToHome": "Zurück zu Home",
66 | "needAccount": "Noch kein Account?",
67 | "error": {
68 | "missing": "Bitte alle geforderten Felder füllen!",
69 | "passwordMismatch": "Bitte die gleichen Passwörter eingeben!",
70 | "validEmail": "Bitte gib eine korrekte E-Mail Adresse ein!",
71 | "validUsername": "Bitte einen Nutzernamen angeben!"
72 | },
73 | "saved": "Profil aktualisiert!"
74 | },
75 | "follow": "Folgen",
76 | "unfollow": "Entfolgen",
77 | "feed": {
78 | "user": "Meine Beiträge",
79 | "favorited": "Favorisierte Beiträge"
80 | }
81 | },
82 | "comment": {
83 | "empty": "Keine Kommentare!",
84 | "response": "Schreib einen Kommentar...",
85 | "inResponse": "Reaktion auf",
86 | "publish": "Veröffentlichen",
87 | "delete": {
88 | "confirm": "Möchtest du diesen Kommentar wirklich löschen?"
89 | }
90 | },
91 | "error": {
92 | "general": "Ein Fehler ist aufgetreten!",
93 | "login": "Bitte einloggen!"
94 | },
95 | "validation": {
96 | "notEmpty": "Das darf nicht leer sein!"
97 | }
98 | }
--------------------------------------------------------------------------------
/app/i18n/en.default.json:
--------------------------------------------------------------------------------
1 | {
2 | "app.name": "Conduit",
3 | "title": "Conduit",
4 | "description": "A NativeScript RealWorld Example App",
5 | "general": {
6 | "save": "Save",
7 | "close": "Close"
8 | },
9 | "about": {
10 | "realworld": "This app is an exemplary project to show how a Medium.com clone (called Conduit) is built using NativeScript to connect to any other backend from ",
11 | "thinkster": "RealWorld example projects have been initiated by Thinkster.io to provide a real-world scenario as tutorial to see how any different backend/frontend programming language and/or framework could work out in your project.",
12 | "insanitydesign": "This example stack has been created by INsanityDesign.com for RealWorld.io and is licensed under MIT. You can checkout the full source at GitHub.com."
13 | },
14 | "settings": {
15 | "title": "Settings",
16 | "url": "Backend URL",
17 | "urlHint": "URL implementing the Conduit backend spec",
18 | "urlWarning": "Please enter a valid URL",
19 | "reset": "Reset"
20 | },
21 | "drawer": {
22 | "login": "Login/Register",
23 | "logout": "Logout",
24 | "home": "Home",
25 | "settings": "Settings",
26 | "about": "About",
27 | "createdBy": "Created by https://github.com/nea/",
28 | "feedback": {
29 | "loggedOut": "You have been logged out successfully!"
30 | }
31 | },
32 | "article": {
33 | "feed": {
34 | "user": "Your Feed",
35 | "global": "Global Feed"
36 | },
37 | "loading": "Loading articles...",
38 | "loaded": "Loaded articles",
39 | "add": "Add article",
40 | "edit": "Edit article",
41 | "delete": "Delete article",
42 | "empty": "No articles found!",
43 | "form": {
44 | "title": "Title",
45 | "description": "Description",
46 | "body": "Body",
47 | "tags": "Tags",
48 | "confirmDelete": "Are you sure you want to delete this article?",
49 | "saved": "Article saved!",
50 | "deleted": "Article deleted!"
51 | }
52 | },
53 | "user": {
54 | "login": "Login",
55 | "logout": "Logout",
56 | "register": "Register",
57 | "form": {
58 | "username": "Username",
59 | "email": "Email",
60 | "password": "Password",
61 | "passwordConfirm": "Confirm password",
62 | "bio": "Bio",
63 | "image": "Image",
64 | "backToLogin": "Back to login",
65 | "backToHome": "Back to home",
66 | "needAccount": "Don't have an account?",
67 | "error": {
68 | "missing": "Please provide all requested inputs!",
69 | "passwordMismatch": "Please provide matching passwords!",
70 | "validEmail": "Please enter a valid Email!",
71 | "validUsername": "Please provide a username!"
72 | },
73 | "saved": "Profile updated!"
74 | },
75 | "follow": "Follow",
76 | "unfollow": "Unfollow",
77 | "feed": {
78 | "user": "My Articles",
79 | "favorited": "Favorited Articles"
80 | }
81 | },
82 | "comment": {
83 | "empty": "No comments!",
84 | "response": "Write a response...",
85 | "inResponse": "In response to",
86 | "publish": "Publish",
87 | "delete": {
88 | "confirm": "Are you sure you want to delete this comment?"
89 | }
90 | },
91 | "error": {
92 | "general": "An error occured!",
93 | "login": "Please login!"
94 | },
95 | "validation": {
96 | "notEmpty": "This may not be empty!"
97 | }
98 | }
--------------------------------------------------------------------------------
/app/main.aot.ts:
--------------------------------------------------------------------------------
1 | // this import should be first in order to load some required settings (like globals and reflect-metadata)
2 | import { platformNativeScript } from "nativescript-angular/platform-static";
3 |
4 | import { AppModuleNgFactory } from "./app.module.ngfactory";
5 |
6 | platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);
7 |
--------------------------------------------------------------------------------
/app/main.ts:
--------------------------------------------------------------------------------
1 | // this import should be first in order to load some required settings (like globals and reflect-metadata)
2 | import { platformNativeScriptDynamic } from "nativescript-angular/platform";
3 |
4 | import { AppModule } from "./app.module";
5 |
6 | platformNativeScriptDynamic().bootstrapModule(AppModule);
7 |
--------------------------------------------------------------------------------
/app/model/Article.ts:
--------------------------------------------------------------------------------
1 | import { Profile } from "~/model/Profile";
2 |
3 | /**
4 | *
5 | */
6 | export class Article {
7 | /** */
8 | public slug?: string = null;
9 | /** */
10 | public title: string = null;
11 | /** */
12 | public description: string = null;
13 | /** */
14 | public body: string = null;
15 | /** */
16 | public tagList?: string[] = [];
17 | /** */
18 | public createdAt?: Date = null;
19 | /** */
20 | public updatedAt?: Date = null;
21 | /** */
22 | public favorited?: boolean = false;
23 | /** */
24 | public favoritesCount?: number = 0;
25 | /** */
26 | public author?: Profile = null;
27 | }
28 |
--------------------------------------------------------------------------------
/app/model/Articles.ts:
--------------------------------------------------------------------------------
1 | import { Article } from "~/model/Article";
2 |
3 | /**
4 | *
5 | */
6 | export class Articles {
7 | /** */
8 | public articles: Article[] = [];
9 | /** */
10 | public articlesCount: number = 0;
11 | }
12 |
--------------------------------------------------------------------------------
/app/model/Comment.ts:
--------------------------------------------------------------------------------
1 | import { Profile } from "~/model/Profile";
2 |
3 | /**
4 | *
5 | */
6 | export class Comment {
7 | /** */
8 | public id: number = 0;
9 | /** */
10 | public body: string = null;
11 | /** */
12 | public createdAt: Date = null;
13 | /** */
14 | public updatedAt: Date = null;
15 | /** */
16 | public author: Profile = null;
17 | }
18 |
--------------------------------------------------------------------------------
/app/model/Profile.ts:
--------------------------------------------------------------------------------
1 | import * as validator from "validator";
2 |
3 | /**
4 | *
5 | */
6 | export class Profile {
7 | /** */
8 | public username: string = null;
9 | /** */
10 | public bio: string = null;
11 | /** */
12 | public _image: string = null;
13 | /** */
14 | public following: boolean = false;
15 |
16 | /**
17 | * @return The image url or a default
18 | */
19 | public get image(): string {
20 | return validator.isURL(this._image) ? this._image : "https://static.productionready.io/images/smiley-cyrus.jpg";
21 | }
22 |
23 | /**
24 | * @param image
25 | */
26 | public set image(image: string) {
27 | this._image = image;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/model/User.ts:
--------------------------------------------------------------------------------
1 | import * as validator from "validator";
2 |
3 | /**
4 | *
5 | */
6 | export class User {
7 | /** */
8 | public id?: number = null;
9 | /** */
10 | public email: string = null;
11 | /** */
12 | public token?: string = null;
13 | /** */
14 | public username: string = null;
15 | /** */
16 | public bio?: string = null;
17 | /** */
18 | public _image?: string = null;
19 | /** */
20 | public password?: string = null;
21 | /** */
22 | public createdAt?: Date = null;
23 | /** */
24 | public updatedAt?: Date = null;
25 |
26 | /**
27 | *
28 | */
29 | public hasValidEmail(): boolean {
30 | return validator.isEmail(this.email);
31 | }
32 |
33 | /**
34 | * @return The image url or a default
35 | */
36 | public get image(): string {
37 | return validator.isURL(this._image) ? this._image : "https://static.productionready.io/images/smiley-cyrus.jpg";
38 | }
39 |
40 | /**
41 | * @param image
42 | */
43 | public set image(image: string) {
44 | this._image = image;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/module/article/article.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
2 | import { NativeScriptModule } from "nativescript-angular/nativescript.module";
3 | import { NativeScriptUIDataFormModule } from "nativescript-ui-dataform/angular";
4 | import { TNSFontIconModule } from "nativescript-ngx-fonticon";
5 | import { NativeScriptLocalizeModule } from "nativescript-localize/angular";
6 | import { NativeScriptUIAutoCompleteTextViewModule } from "nativescript-ui-autocomplete/angular";
7 | import { NativeScriptUIListViewModule } from "nativescript-ui-listview/angular";
8 |
9 | import { ArticleRouting } from "./article.routing";
10 |
11 | import { CommentModule } from "~/module/comment/comment.module";
12 | import { ServiceModule } from "~/service/service.module";
13 |
14 | import { EditArticleComponent } from "./edit-article.component";
15 | import { ListArticlesComponent } from "~/module/article/list-articles.component";
16 | import { ViewArticleComponent } from "~/module/article/view-article.component";
17 |
18 | @NgModule({
19 | imports: [
20 | NativeScriptModule,
21 | NativeScriptUIDataFormModule,
22 | NativeScriptLocalizeModule,
23 | NativeScriptUIAutoCompleteTextViewModule,
24 | NativeScriptUIListViewModule,
25 | ServiceModule,
26 | CommentModule,
27 | ArticleRouting,
28 | TNSFontIconModule
29 | ],
30 | declarations: [ListArticlesComponent, EditArticleComponent, ViewArticleComponent],
31 | exports: [ListArticlesComponent],
32 | schemas: [NO_ERRORS_SCHEMA]
33 | })
34 | export class ArticleModule {}
35 |
--------------------------------------------------------------------------------
/app/module/article/article.routing.ts:
--------------------------------------------------------------------------------
1 | import { ModuleWithProviders } from "@angular/core";
2 | import { Routes, RouterModule } from "@angular/router";
3 |
4 | import { EditArticleComponent } from "./edit-article.component";
5 | import { ViewArticleComponent } from "~/module/article/view-article.component";
6 |
7 | // prettier-ignore
8 | const ArticleRoutes: Routes = [
9 | { path: "editor", component: EditArticleComponent },
10 | { path: 'editor/:slug', component: EditArticleComponent },
11 | { path: 'article/:slug', component: ViewArticleComponent }
12 | ];
13 |
14 | export const ArticleRouting: ModuleWithProviders = RouterModule.forChild(ArticleRoutes);
15 |
--------------------------------------------------------------------------------
/app/module/article/article.scss:
--------------------------------------------------------------------------------
1 | // Import app variables
2 | @import '~/app-variables';
3 |
4 | ActionBar {
5 | background-color: white;
6 | color: black;
7 | }
8 |
9 | .article {
10 | background-color: $brand-light;
11 | width: 100%;
12 | height: 100%;
13 |
14 | .add-comment {
15 | width: 100%;
16 | padding: 8 15 4 15;
17 | background-color: white;
18 | border-bottom-color: $brand-dark;
19 | border-bottom-width: 1;
20 | }
21 | }
22 |
23 | .article-item {
24 | padding: 0 0 8 0;
25 | background-color: $brand-light;
26 | }
27 |
28 | .article-item-content {
29 | padding: 8 15 4 15;
30 | background-color: white;
31 | border-bottom-color: $brand-dark;
32 | border-bottom-width: 1;
33 | }
34 |
35 | .favorited {
36 | color: green;
37 | }
--------------------------------------------------------------------------------
/app/module/article/edit-article.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/module/article/edit-article.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ElementRef, OnInit, ViewChild } from "@angular/core";
2 | import { Router, ActivatedRoute } from "@angular/router";
3 | import { RadDataFormComponent } from "nativescript-ui-dataform/angular/dataform-directives";
4 | import { EntityProperty, DataFormEventData } from "nativescript-ui-dataform";
5 | import { Article } from "~/model/Article";
6 | import { ConduitService } from "~/service/ConduitService";
7 | import { Feedback, FeedbackType, FeedbackPosition } from "nativescript-feedback";
8 | import { localize } from "nativescript-localize";
9 | import { RadAutoCompleteTextViewComponent } from "nativescript-ui-autocomplete/angular/autocomplete-directives";
10 | import { ObservableArray } from "tns-core-modules/data/observable-array/observable-array";
11 | import { TokenModel } from "nativescript-ui-autocomplete";
12 | import { PageRoute } from "nativescript-angular/router";
13 | import { switchMap } from "rxjs/operators";
14 | import { topmost } from "ui/frame";
15 | import * as dialogs from "ui/dialogs";
16 |
17 | @Component({
18 | selector: "conduit-edit-article",
19 | moduleId: module.id,
20 | templateUrl: "./edit-article.component.html",
21 | styleUrls: ["./article.css"]
22 | })
23 | export class EditArticleComponent implements OnInit {
24 | /** */
25 | public title: string;
26 | /** */
27 | public article: Article;
28 | /** */
29 | public isLoading: boolean = false;
30 | /** */
31 | private feedback: Feedback;
32 | /** */
33 | @ViewChild("formArticle") protected formArticle: RadDataFormComponent;
34 | /** */
35 | @ViewChild("tagsField") protected tagsField: RadAutoCompleteTextViewComponent;
36 |
37 | private tags: Array = new Array();
38 |
39 | /**
40 | *
41 | * @param router
42 | * @param route
43 | */
44 | constructor(private router: Router, private pageRoute: PageRoute, private conduit: ConduitService) {
45 | this.feedback = new Feedback();
46 |
47 | //Get the given article or create a new
48 | this.title = localize("article.add");
49 | this.article = new Article();
50 | console.log(this.article);
51 | this.pageRoute.activatedRoute.pipe(switchMap(activatedRoute => activatedRoute.params)).forEach(params => {
52 | if (params["slug"]) {
53 | this.isLoading = true;
54 | this.title = localize("article.edit");
55 | this.conduit
56 | .getArticle(params["slug"])
57 | .subscribe(
58 | (article: Article) => {
59 | this.article = article;
60 | article.tagList.forEach(tag => {
61 | let token = new TokenModel(tag, undefined);
62 | this.tags.push(token);
63 | this.tagsField.autoCompleteTextView.addToken(token);
64 | });
65 | },
66 | error => {
67 | this.feedback.error({
68 | title: localize("error.general"),
69 | message: JSON.stringify(error.error)
70 | });
71 | }
72 | )
73 | .add(() => {
74 | this.isLoading = false;
75 | });
76 | }
77 | });
78 | }
79 |
80 | /**
81 | *
82 | */
83 | public ngOnInit() {
84 | this.conduit.getTags().subscribe((tags: string[]) => {
85 | tags.forEach(tag => {
86 | this.tags.push(new TokenModel(tag, null));
87 | });
88 | });
89 |
90 | this.tagsField.autoCompleteTextView.loadSuggestionsAsync = text => {
91 | let items: Array = Object.assign([], this.tags);
92 | let promise = new Promise((resolve, reject) => {
93 | if (text !== "") {
94 | items.push(new TokenModel(text, null));
95 | resolve(items);
96 | } else {
97 | reject();
98 | }
99 | });
100 | return promise;
101 | };
102 | }
103 |
104 | /**
105 | *
106 | */
107 | public onBack() {
108 | topmost().goBack();
109 | }
110 |
111 | /**
112 | *
113 | * @param args
114 | */
115 | public onTokenAdded(args) {
116 | this.article.tagList.push(args.token.text);
117 | }
118 |
119 | /**
120 | *
121 | * @param args
122 | */
123 | public onTokenRemoved(args) {
124 | delete this.article.tagList[args.token.text];
125 | }
126 |
127 | /**
128 | *
129 | */
130 | public onSave() {
131 | this.formArticle.dataForm.validateAll().then(result => {
132 | if (!this.formArticle.dataForm.hasValidationErrors()) {
133 | this.isLoading = true;
134 | this.conduit
135 | .addArticle(this.article.title, this.article.description, this.article.body, ...this.article.tagList)
136 | .subscribe(
137 | (article: Article) => {
138 | this.feedback.success({
139 | title: localize("article.form.saved"),
140 | message: this.article.title
141 | });
142 | },
143 | error => {
144 | this.feedback.error({
145 | title: localize("error.general"),
146 | message: JSON.stringify(error.error)
147 | });
148 | }
149 | )
150 | .add(() => {
151 | this.isLoading = false;
152 | this.router.navigate(["/home"]);
153 | });
154 | }
155 | });
156 | }
157 |
158 | /**
159 | *
160 | */
161 | public onDelete() {
162 | dialogs.confirm(localize("article.form.confirmDelete")).then(result => {
163 | if (result) {
164 | this.isLoading = true;
165 | this.conduit
166 | .removeArticle(this.article.slug)
167 | .subscribe(() => {
168 | this.router.navigate(["/home"]);
169 | })
170 | .add(() => {
171 | this.isLoading = false;
172 | });
173 | }
174 | });
175 | }
176 |
177 | /**
178 | *
179 | */
180 | get tagsProvider(): Array {
181 | return this.tags;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/app/module/article/list-articles.component.html:
--------------------------------------------------------------------------------
1 | 0" loadOnDemandMode="Auto" (loadMoreDataRequested)="onLoadMoreData($event)">
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/module/article/list-articles.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input } from "@angular/core";
2 | import { Router } from "@angular/router";
3 | import { PropertyChangeData } from "ui/page";
4 | import { ConduitService } from "~/service/ConduitService";
5 | import { Article } from "~/model/Article";
6 | import { Articles } from "~/model/Articles";
7 | import { RadListViewComponent } from "nativescript-ui-listview/angular";
8 | import { ListViewEventData } from "nativescript-ui-listview";
9 | import { ObservableArray } from "tns-core-modules/data/observable-array/observable-array";
10 | import { Feedback, FeedbackType, FeedbackPosition } from "nativescript-feedback";
11 | import { localize } from "nativescript-localize";
12 | import { Subscription } from "rxjs";
13 |
14 | @Component({
15 | selector: "conduit-list-articles",
16 | moduleId: module.id,
17 | templateUrl: "./list-articles.component.html",
18 | styleUrls: ["./article.css"]
19 | })
20 | export class ListArticlesComponent implements OnInit {
21 | /** */
22 | @Input("isUserFeed") public isUserFeed: boolean = false;
23 | /** */
24 | @Input("offsetInterval") public offsetInterval: number = 20;
25 | /** */
26 | @Input("tag") public tag: string;
27 | /** */
28 | @Input("author") public author: string;
29 | /** */
30 | @Input("favorited") public favorited: string;
31 | /** */
32 | @Input("limit") public limit: number = 20;
33 | /** */
34 | @Input("offset") public offset: number = 0;
35 |
36 | /** */
37 | public articles: ObservableArray = new ObservableArray();
38 | /** */
39 | public isLoading: boolean = false;
40 | /** */
41 | private feedback: Feedback;
42 |
43 | /**
44 | *
45 | * @param router
46 | * @param conduit
47 | */
48 | constructor(private router: Router, private conduit: ConduitService) {
49 | this.feedback = new Feedback();
50 | }
51 |
52 | /**
53 | *
54 | */
55 | public ngOnInit() {
56 | this.loadArticles();
57 | }
58 |
59 | /**
60 | *
61 | */
62 | protected loadArticles(): Subscription {
63 | this.isLoading = true;
64 | if (this.isUserFeed) {
65 | return this.conduit
66 | .getArticlesFeed(this.limit, this.offset)
67 | .subscribe(this.onLoadingArticles, this.onLoadingError)
68 | .add(() => {
69 | this.onLoadingComplete();
70 | });
71 | } else {
72 | return this.conduit
73 | .getArticles(this.tag, this.author, this.favorited, this.limit, this.offset)
74 | .subscribe(this.onLoadingArticles, this.onLoadingError)
75 | .add(() => {
76 | this.onLoadingComplete();
77 | });
78 | }
79 | }
80 |
81 | /**
82 | *
83 | * @param args
84 | */
85 | public onPullToRefresh(args: ListViewEventData) {
86 | //Reset articles
87 | this.articles = new ObservableArray();
88 | //Reload
89 | this.loadArticles().add(() => {
90 | args.object.notifyPullToRefreshFinished();
91 | });
92 | }
93 |
94 | /**
95 | *
96 | * @param args
97 | */
98 | public onLoadMoreData(args: ListViewEventData) {
99 | //Increase offset
100 | this.offset += this.offsetInterval;
101 | //and load more data
102 | this.loadArticles().add(() => {
103 | args.object.notifyLoadOnDemandFinished();
104 | args.returnValue = true;
105 | });
106 | }
107 |
108 | /**
109 | *
110 | */
111 | protected onLoadingArticles = (articles: Articles) => {
112 | this.articles.push(articles.articles);
113 | };
114 |
115 | /**
116 | *
117 | */
118 | protected onLoadingError = error => {
119 | this.feedback.error({
120 | title: localize("error.general"),
121 | message: error
122 | });
123 | };
124 |
125 | /**
126 | *
127 | */
128 | protected onLoadingComplete = () => {
129 | this.isLoading = false;
130 | };
131 |
132 | /**
133 | *
134 | * @param username
135 | */
136 | public onAuthor(username) {
137 | this.router.navigate([`/profile/${username}`]);
138 | }
139 |
140 | /**
141 | *
142 | * @param articleSlug
143 | */
144 | public onArticle(articleSlug: string) {
145 | this.router.navigate([`/article/${articleSlug}`]);
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/app/module/article/view-article.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/module/article/view-article.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ViewChild, ViewContainerRef, ElementRef } from "@angular/core";
2 | import { Router, ActivatedRoute } from "@angular/router";
3 | import { ConduitService } from "~/service/ConduitService";
4 | import { PageRoute } from "nativescript-angular/router";
5 | import { Article } from "~/model/Article";
6 | import { switchMap } from "rxjs/operators";
7 | import { Feedback } from "nativescript-feedback";
8 | import { topmost } from "ui/frame";
9 | import * as SocialShare from "nativescript-social-share";
10 | import { UserService } from "~/service/UserService";
11 | import { ModalDialogService } from "nativescript-angular/directives/dialogs";
12 | import { WriteCommentModal } from "~/module/comment/write-comment-modal.component";
13 | import { ListCommentsComponent } from "~/module/comment/list-comments.component";
14 | import { markdown } from "markdown";
15 | import { localize } from "nativescript-localize";
16 | import { ActionBar } from "tns-core-modules/ui/action-bar/action-bar";
17 |
18 | @Component({
19 | selector: "conduit-view-article",
20 | moduleId: module.id,
21 | templateUrl: "./view-article.component.html",
22 | styleUrls: ["./article.css"],
23 | providers: [ModalDialogService]
24 | })
25 | export class ViewArticleComponent implements OnInit {
26 | /** */
27 | public article: Article;
28 | /** */
29 | public articleBody: string = "";
30 | /** */
31 | public isLoading: boolean = false;
32 | /** */
33 | protected feedback: Feedback;
34 | /** */
35 | @ViewChild("commentsList") protected commentsList: ListCommentsComponent;
36 |
37 | /**
38 | *
39 | * @param router
40 | * @param pageRoute
41 | * @param conduit
42 | */
43 | constructor(
44 | private router: Router,
45 | private pageRoute: PageRoute,
46 | protected conduit: ConduitService,
47 | public userService: UserService,
48 | protected modal: ModalDialogService,
49 | protected vcRef: ViewContainerRef
50 | ) {
51 | this.feedback = new Feedback();
52 |
53 | //
54 | this.pageRoute.activatedRoute.pipe(switchMap(activatedRoute => activatedRoute.params)).forEach(params => {
55 | if (params["slug"]) {
56 | this.isLoading = true;
57 | this.conduit
58 | .getArticle(params["slug"])
59 | .subscribe(
60 | (article: Article) => {
61 | this.article = article;
62 | this.articleBody = markdown.toHTML(article.body, "Maruku");
63 | },
64 | error => {
65 | this.feedback.error({
66 | title: localize("error.general"),
67 | message: error
68 | });
69 | this.onBack();
70 | }
71 | )
72 | .add(() => {
73 | this.isLoading = false;
74 | });
75 | }
76 | });
77 | }
78 |
79 | /**
80 | *
81 | */
82 | public ngOnInit() {}
83 |
84 | /**
85 | *
86 | */
87 | public onWriteComment() {
88 | this.modal
89 | .showModal(WriteCommentModal, {
90 | context: this.article,
91 | fullscreen: true,
92 | viewContainerRef: this.vcRef
93 | })
94 | .then(res => {
95 | if (res) {
96 | this.commentsList.reloadComments();
97 | }
98 | });
99 | }
100 |
101 | /**
102 | *
103 | */
104 | public onFavorited() {
105 | this.article.favorited = !this.article.favorited;
106 | this.conduit.favorArticle(this.article.slug, this.article.favorited).subscribe(
107 | (article: Article) => {
108 | this.article = article;
109 | },
110 | error => {
111 | this.feedback.error({
112 | title: localize("error.general"),
113 | message: error
114 | });
115 | }
116 | );
117 | }
118 |
119 | /**
120 | *
121 | * @param username
122 | */
123 | public onAuthor(username) {
124 | this.router.navigate([`/profile/${username}`]);
125 | }
126 |
127 | /**
128 | *
129 | */
130 | public onShare() {
131 | SocialShare.shareText(`${this.article.title}
132 |
133 | ${this.article.description}
134 |
135 | ${this.articleBody}`);
136 | }
137 |
138 | /**
139 | *
140 | */
141 | public onEdit() {
142 | this.router.navigate([`/editor/${this.article.slug}`]);
143 | }
144 |
145 | /**
146 | *
147 | */
148 | public onBack() {
149 | topmost().goBack();
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/app/module/comment/comment.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
2 | import { NativeScriptModule } from "nativescript-angular/nativescript.module";
3 | import { TNSFontIconModule } from "nativescript-ngx-fonticon";
4 | import { NativeScriptLocalizeModule } from "nativescript-localize/angular";
5 | import { CommentRouting } from "./comment.routing";
6 | import { ListCommentsComponent } from "~/module/comment/list-comments.component";
7 | import { ServiceModule } from "~/service/service.module";
8 | import { WriteCommentModal } from "~/module/comment/write-comment-modal.component";
9 | import { NativeScriptUIListViewModule } from "nativescript-ui-listview/angular";
10 |
11 | @NgModule({
12 | imports: [
13 | NativeScriptModule,
14 | NativeScriptLocalizeModule,
15 | NativeScriptUIListViewModule,
16 | ServiceModule,
17 | CommentRouting,
18 | TNSFontIconModule
19 | ],
20 | declarations: [ListCommentsComponent, WriteCommentModal],
21 | entryComponents: [ListCommentsComponent, WriteCommentModal],
22 | exports: [ListCommentsComponent, WriteCommentModal],
23 | schemas: [NO_ERRORS_SCHEMA]
24 | })
25 | export class CommentModule {}
26 |
--------------------------------------------------------------------------------
/app/module/comment/comment.routing.ts:
--------------------------------------------------------------------------------
1 | import { ModuleWithProviders } from "@angular/core";
2 | import { Routes, RouterModule } from "@angular/router";
3 | import { ListCommentsComponent } from "~/module/comment/list-comments.component";
4 |
5 | // prettier-ignore
6 | const CommentRoutes: Routes = [
7 | { path: 'articles/:slug/comments', component: ListCommentsComponent }
8 | ];
9 |
10 | export const CommentRouting: ModuleWithProviders = RouterModule.forChild(CommentRoutes);
11 |
--------------------------------------------------------------------------------
/app/module/comment/comment.scss:
--------------------------------------------------------------------------------
1 | // Import app variables
2 | @import '~/app-variables';
3 |
4 | .comment-item {
5 | padding: 0 0 8 0;
6 | background-color: $brand-light;
7 | }
8 |
9 | .comment-item-content {
10 | padding: 8 15 4 15;
11 | background-color: white;
12 | border-bottom-color: $brand-dark;
13 | border-bottom-width: 1;
14 | }
15 |
16 | .comment-reply-box {
17 | border-width: 1;
18 | border-color: $brand-primary;
19 | }
20 |
--------------------------------------------------------------------------------
/app/module/comment/list-comments.component.html:
--------------------------------------------------------------------------------
1 | 0" [items]="comments" class="list-group">
2 |
3 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/module/comment/list-comments.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ElementRef, OnInit, ViewChild, Input } from "@angular/core";
2 | import { Router } from "@angular/router";
3 | import { Page, PropertyChangeData } from "ui/page";
4 | import { ConduitService } from "~/service/ConduitService";
5 | import { Article } from "~/model/Article";
6 | import { Articles } from "~/model/Articles";
7 | import { UserService } from "~/service/UserService";
8 | import { User } from "~/model/User";
9 | import { RadListViewComponent } from "nativescript-ui-listview/angular";
10 | import { ListViewEventData } from "nativescript-ui-listview";
11 | import { ObservableArray } from "tns-core-modules/data/observable-array/observable-array";
12 | import { SegmentedBar, SegmentedBarItem } from "ui/segmented-bar";
13 | import { Feedback, FeedbackType, FeedbackPosition } from "nativescript-feedback";
14 | import { localize } from "nativescript-localize";
15 | import { Subscription } from "rxjs";
16 | import * as dialogs from "ui/dialogs";
17 | import { ListView } from "ui/list-view";
18 |
19 | @Component({
20 | selector: "conduit-list-comments",
21 | moduleId: module.id,
22 | templateUrl: "./list-comments.component.html",
23 | styleUrls: ["./comment.css"]
24 | })
25 | export class ListCommentsComponent implements OnInit {
26 | /** */
27 | @Input("slug") public slug: string;
28 | /** */
29 | public comments: ObservableArray = new ObservableArray();
30 | /** */
31 | public isLoading: boolean = false;
32 | /** */
33 | private feedback: Feedback;
34 |
35 | /**
36 | *
37 | * @param router
38 | * @param conduit
39 | */
40 | constructor(private router: Router, private conduit: ConduitService, private userService: UserService) {
41 | this.feedback = new Feedback();
42 | }
43 |
44 | /**
45 | *
46 | */
47 | public ngOnInit() {
48 | this.reloadComments();
49 | }
50 |
51 | /**
52 | *
53 | */
54 | public reloadComments(): Subscription {
55 | this.isLoading = true;
56 | return this.conduit
57 | .getComments(this.slug)
58 | .subscribe(this.onLoadingComments, this.onLoadingError)
59 | .add(() => {
60 | this.onLoadingComplete();
61 | });
62 | }
63 |
64 | /**
65 | *
66 | */
67 | protected onLoadingComments = (comments: Comment[]) => {
68 | this.comments = new ObservableArray();
69 | this.comments.push(comments);
70 | };
71 |
72 | /**
73 | *
74 | */
75 | protected onLoadingError = error => {
76 | this.feedback.error({
77 | title: localize("error.general"),
78 | message: error
79 | });
80 | };
81 |
82 | /**
83 | *
84 | */
85 | protected onLoadingComplete = () => {
86 | this.isLoading = false;
87 | };
88 |
89 | /**
90 | *
91 | * @param args
92 | */
93 | public onAuthor(args) {
94 | this.router.navigate([`/profile/${args.object.text}`]);
95 | }
96 |
97 | /**
98 | * @param commentId
99 | */
100 | public onDelete(commentId: number) {
101 | dialogs.confirm(localize("comment.delete.confirm")).then(result => {
102 | if (result) {
103 | this.isLoading = true;
104 | this.conduit
105 | .deleteComment(this.slug, commentId)
106 | .subscribe(() => {}, this.onLoadingError)
107 | .add(() => {
108 | this.reloadComments();
109 | });
110 | }
111 | });
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/app/module/comment/write-comment-modal.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/module/comment/write-comment-modal.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewChild, ElementRef } from "@angular/core";
2 | import { ModalDialogParams } from "nativescript-angular/directives/dialogs";
3 | import { Page } from "tns-core-modules/ui/page/page";
4 | import { Article } from "~/model/Article";
5 | import { TextField } from "ui/text-field";
6 | import { ConduitService } from "~/service/ConduitService";
7 |
8 | @Component({
9 | selector: "conduit-write-comment-modal",
10 | moduleId: module.id,
11 | templateUrl: "./write-comment-modal.component.html",
12 | styleUrls: ["./comment.css"]
13 | })
14 | export class WriteCommentModal {
15 | /** */
16 | public article: Article;
17 | /** */
18 | public isLoading: boolean = false;
19 | /** */
20 | @ViewChild("txtComment") protected txtComment: ElementRef;
21 |
22 | /**
23 | *
24 | * @param params
25 | * @param page
26 | * @param conduit
27 | */
28 | public constructor(private params: ModalDialogParams, private page: Page, protected conduit: ConduitService) {
29 | this.article = params.context;
30 | this.page.on("unloaded", () => {
31 | this.params.closeCallback();
32 | });
33 | }
34 |
35 | /**
36 | *
37 | */
38 | public onClose() {
39 | this.params.closeCallback();
40 | }
41 |
42 | /**
43 | *
44 | */
45 | public onSubmit() {
46 | this.isLoading = true;
47 | let commentField = this.txtComment.nativeElement;
48 | this.conduit.addComment(this.article.slug, commentField.text).subscribe(() => {
49 | this.params.closeCallback(commentField.text);
50 | }).add(() => {
51 | this.isLoading = false;
52 | });
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/module/home/about-modal.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/module/home/about-modal.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewChild, ElementRef } from "@angular/core";
2 | import { ModalDialogParams } from "nativescript-angular/directives/dialogs";
3 | import * as utils from "utils/utils";
4 |
5 | @Component({
6 | selector: "conduit-about-modal",
7 | moduleId: module.id,
8 | templateUrl: "./about-modal.component.html",
9 | styleUrls: ["./home.css"]
10 | })
11 | export class AboutModal {
12 | /**
13 | *
14 | * @param params
15 | */
16 | public constructor(private params: ModalDialogParams) {}
17 |
18 | /**
19 | *
20 | */
21 | public onClose() {
22 | this.params.closeCallback();
23 | }
24 |
25 | /**
26 | *
27 | * @param url
28 | */
29 | public onLink(url: string) {
30 | utils.openUrl(url);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/module/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/module/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from "@angular/core";
2 | import { Router } from "@angular/router";
3 | import { UserService } from "~/service/UserService";
4 | import { SegmentedBar, SegmentedBarItem } from "ui/segmented-bar";
5 | import { localize } from "nativescript-localize";
6 | import { ListArticlesComponent } from "~/module/article/list-articles.component";
7 | import { RadSideDrawer } from "nativescript-ui-sidedrawer";
8 | import * as app from "application";
9 | import { PropertyChangeData } from "tns-core-modules/ui/page/page";
10 |
11 | import { registerElement } from "nativescript-angular/element-registry";
12 | registerElement("Fab", () => require("nativescript-floatingactionbutton").Fab);
13 |
14 | @Component({
15 | selector: "conduit-home",
16 | moduleId: module.id,
17 | templateUrl: "./home.component.html",
18 | styleUrls: ["./home.css"]
19 | })
20 | export class HomeComponent implements OnInit {
21 | /** */
22 | public isLoading: boolean = false;
23 | /** */
24 | protected isUserFeed: boolean = false;
25 |
26 | /**
27 | *
28 | * @param router
29 | * @param userService
30 | */
31 | constructor(protected router: Router, public userService: UserService) {}
32 |
33 | /**
34 | *
35 | */
36 | public ngOnInit() {}
37 |
38 | /**
39 | *
40 | */
41 | public get IsUserFeed() {
42 | return this.userService.getUser() !== null ? this.isUserFeed : false;
43 | }
44 |
45 | /**
46 | *
47 | * @param args
48 | */
49 | public onFeedChange(args: PropertyChangeData) {
50 | this.isUserFeed = args.value === 0;
51 | }
52 |
53 | /**
54 | *
55 | */
56 | public onAddArticle() {
57 | this.router.navigate(["/editor"]);
58 | }
59 |
60 | /**
61 | *
62 | */
63 | public onDrawer() {
64 | const sideDrawer = app.getRootView();
65 | sideDrawer.showDrawer();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/module/home/home.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
2 | import { NativeScriptModule } from "nativescript-angular/nativescript.module";
3 | import { TNSFontIconModule } from "nativescript-ngx-fonticon";
4 | import { NativeScriptLocalizeModule } from "nativescript-localize/angular";
5 |
6 | import { HomeRouting } from "./home.routing";
7 | import { HomeComponent } from "./home.component";
8 |
9 | import { ArticleModule } from "~/module/article/article.module";
10 | import { UserModule } from "~/module/user/user.module";
11 | import { ServiceModule } from "~/service/service.module";
12 |
13 | import { AboutModal } from "~/module/home/about-modal.component";
14 | import { SettingsComponent } from "~/module/home/settings.component";
15 |
16 | @NgModule({
17 | imports: [NativeScriptModule, HomeRouting, NativeScriptLocalizeModule, ServiceModule, ArticleModule, UserModule, TNSFontIconModule],
18 | entryComponents: [AboutModal],
19 | declarations: [HomeComponent, AboutModal, SettingsComponent],
20 | schemas: [NO_ERRORS_SCHEMA]
21 | })
22 | export class HomeModule {}
23 |
--------------------------------------------------------------------------------
/app/module/home/home.routing.ts:
--------------------------------------------------------------------------------
1 | import { ModuleWithProviders } from "@angular/core";
2 | import { Routes, RouterModule } from "@angular/router";
3 |
4 | import { HomeComponent } from "./home.component";
5 | import { SettingsComponent } from "./settings.component";
6 |
7 | // prettier-ignore
8 | const HomeRoutes: Routes = [
9 | { path: "home", component: HomeComponent },
10 | { path: "settings", component: SettingsComponent }
11 | ];
12 |
13 | export const HomeRouting: ModuleWithProviders = RouterModule.forChild(HomeRoutes);
14 |
--------------------------------------------------------------------------------
/app/module/home/home.scss:
--------------------------------------------------------------------------------
1 | // Import app variables
2 | @import '~/app-variables';
3 |
4 | .article-item {
5 | padding: 0 0 8 0;
6 | background-color: $brand-light;
7 | }
8 |
9 | .article-item-content {
10 | padding: 8 15 4 15;
11 | background-color: white;
12 | border-bottom-color: $brand-dark;
13 | border-bottom-width: 1;
14 | }
15 |
16 | .fab {
17 | height: 48;
18 | width: 48;
19 | margin: 10;
20 | background-color: $brand-primary;
21 | horizontal-align: right;
22 | vertical-align: bottom;
23 | }
--------------------------------------------------------------------------------
/app/module/home/settings.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/module/home/settings.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ViewChild, ElementRef } from "@angular/core";
2 | import { Router } from "@angular/router";
3 | import { AbstractHttpService } from "~/service/AbstractHttpService";
4 | import { setString, getString } from "tns-core-modules/application-settings/application-settings";
5 | import { Feedback } from "nativescript-feedback";
6 | import { localize } from "nativescript-localize";
7 | import * as isURL from "validator/lib/isURL";
8 | import { topmost } from "tns-core-modules/ui/frame/frame";
9 | import { UserService } from "~/service/UserService";
10 |
11 | @Component({
12 | selector: "conduit-settings",
13 | moduleId: module.id,
14 | templateUrl: "./settings.component.html",
15 | styleUrls: ["./home.css"]
16 | })
17 | export class SettingsComponent implements OnInit {
18 | /** */
19 | private feedback: Feedback;
20 | /** */
21 | public url: string;
22 | /** */
23 | @ViewChild("txtUrl") protected txtUrl: ElementRef;
24 |
25 | /**
26 | *
27 | * @param router
28 | * @param userService
29 | */
30 | constructor(protected router: Router, public userService: UserService) {
31 | this.feedback = new Feedback();
32 | }
33 |
34 | /**
35 | *
36 | */
37 | public ngOnInit() {
38 | this.url = getString("apiUrl", AbstractHttpService.PRODUCTIONREADY_IO_API_BASE_URL);
39 | }
40 |
41 | /**
42 | *
43 | */
44 | public onSave() {
45 | if (!this.txtUrl.nativeElement.text || !isURL(this.txtUrl.nativeElement.text)) {
46 | this.feedback.warning({
47 | title: localize("error.general"),
48 | message: localize("settings.urlWarning")
49 | });
50 | return;
51 | }
52 | this.save(this.txtUrl.nativeElement.text);
53 | topmost().goBack();
54 | }
55 |
56 | /**
57 | * Reset the backend url back to the productionready.io url
58 | */
59 | public onReset() {
60 | this.save(AbstractHttpService.PRODUCTIONREADY_IO_API_BASE_URL);
61 | }
62 |
63 | /**
64 | *
65 | */
66 | public onBack() {
67 | topmost().goBack();
68 | }
69 |
70 | /**
71 | *
72 | * @param url
73 | */
74 | protected save(url) {
75 | setString("apiUrl", url);
76 | this.url = url;
77 | this.txtUrl.nativeElement.text = url;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/module/user/edit-profile.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/module/user/edit-profile.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ViewChild } from "@angular/core";
2 | import { Router, ActivatedRoute } from "@angular/router";
3 | import { PropertyChangeData } from "ui/page";
4 | import { Feedback, FeedbackType, FeedbackPosition } from "nativescript-feedback";
5 | import { localize } from "nativescript-localize";
6 | import { Profile } from "~/model/Profile";
7 | import { UserService } from "~/service/UserService";
8 | import { PageRoute } from "nativescript-angular/router";
9 | import { switchMap } from "rxjs/operators";
10 | import { topmost } from "ui/frame";
11 | import { RadDataFormComponent } from "nativescript-ui-dataform/angular/dataform-directives";
12 | import { User } from "~/model/User";
13 |
14 | @Component({
15 | selector: "conduit-edit-profile",
16 | moduleId: module.id,
17 | templateUrl: "./edit-profile.component.html",
18 | styleUrls: ["./user.css"]
19 | })
20 | export class EditProfileComponent implements OnInit {
21 | /** */
22 | public isLoading: boolean = false;
23 | /** */
24 | private feedback: Feedback;
25 | /** */
26 | public user: User = new User();
27 | /** */
28 | @ViewChild("formProfile") protected formProfile: RadDataFormComponent;
29 |
30 | /**
31 | *
32 | * @param router
33 | * @param userService
34 | */
35 | constructor(private router: Router, public userService: UserService) {
36 | this.feedback = new Feedback();
37 | Object.assign(this.user, this.userService.getUser());
38 | }
39 |
40 | /**
41 | *
42 | */
43 | public ngOnInit() {}
44 |
45 | /**
46 | *
47 | */
48 | public onSave() {
49 | this.formProfile.dataForm.validateAll().then(result => {
50 | if (!this.formProfile.dataForm.hasValidationErrors() && this.user.hasValidEmail()) {
51 | this.isLoading = true;
52 | //Update password (if any is given)
53 | if (this.user.password) {
54 | this.userService.updatePassword(this.user.password).subscribe(
55 | (user: User) => {},
56 | error => {
57 | this.feedback.error({
58 | title: localize("error.general"),
59 | message: JSON.stringify(error.error)
60 | });
61 | }
62 | );
63 | }
64 | //Update User
65 | this.userService
66 | .updateUser(this.user)
67 | .subscribe(
68 | (user: User) => {
69 | this.feedback.success({
70 | title: localize("user.form.saved"),
71 | message: this.user.username
72 | });
73 | },
74 | error => {
75 | this.feedback.error({
76 | title: localize("error.general"),
77 | message: JSON.stringify(error.error)
78 | });
79 | }
80 | )
81 | .add(() => {
82 | this.isLoading = false;
83 | this.router.navigate(["/home"]);
84 | });
85 | }
86 | });
87 | }
88 |
89 | /**
90 | *
91 | */
92 | public onBack() {
93 | topmost().goBack();
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/module/user/login.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
27 |
28 |
29 |
30 |
32 |
33 |
34 |
35 |
36 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/module/user/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ElementRef, ViewChild } from "@angular/core";
2 | import { Router } from "@angular/router";
3 | import { alert, prompt } from "tns-core-modules/ui/dialogs";
4 | import { Page } from "tns-core-modules/ui/page";
5 | import { User } from "~/model/User";
6 | import { UserService } from "~/service/UserService";
7 | import { Feedback } from "nativescript-feedback";
8 | import { localize } from "nativescript-localize";
9 | import { RouterExtensions } from "nativescript-angular/router";
10 | import * as isEmail from "validator/lib/isEmail";
11 |
12 | @Component({
13 | selector: "conduit-login",
14 | moduleId: module.id,
15 | templateUrl: "./login.component.html",
16 | styleUrls: ["./user.css"]
17 | })
18 | export class LoginComponent {
19 | /** */
20 | public isLoggingIn = true;
21 | /** */
22 | public isLoading = false;
23 | /** */
24 | public user: User;
25 | /** */
26 | protected feedback: Feedback;
27 | /** */
28 | @ViewChild("username") public username: ElementRef;
29 | /** */
30 | @ViewChild("password") public password: ElementRef;
31 | /** */
32 | @ViewChild("confirmPassword") public confirmPassword: ElementRef;
33 |
34 | /**
35 | *
36 | * @param page
37 | * @param router
38 | * @param userService
39 | */
40 | constructor(protected page: Page, protected routerExtensions: RouterExtensions, public userService: UserService) {
41 | this.feedback = new Feedback();
42 | this.page.actionBarHidden = true;
43 | this.user = new User();
44 | }
45 |
46 | /**
47 | *
48 | */
49 | public onSubmit() {
50 | if (!this.user.email || !this.password.nativeElement.text) {
51 | this.showError("user.form.error.missing");
52 | return;
53 | }
54 | if (!isEmail(this.user.email)) {
55 | this.showError("user.form.error.validEmail");
56 | return;
57 | }
58 |
59 | if (this.isLoggingIn) {
60 | this.login();
61 | } else {
62 | this.register();
63 | }
64 | }
65 |
66 | /**
67 | *
68 | */
69 | protected login() {
70 | this.isLoading = true;
71 | this.userService
72 | .login(this.user.email, this.password.nativeElement.text)
73 | .subscribe(
74 | () => {
75 | this.onBack();
76 | },
77 | error => {
78 | this.showError(JSON.stringify(error.error));
79 | }
80 | )
81 | .add(() => {
82 | this.isLoading = false;
83 | });
84 | }
85 |
86 | /**
87 | *
88 | */
89 | protected register() {
90 | if (this.password.nativeElement.text !== this.confirmPassword.nativeElement.text) {
91 | this.showError("user.form.error.passwordMismatch");
92 | return;
93 | }
94 | if (!this.user.username) {
95 | this.showError("user.form.error.validUsername");
96 | return;
97 | }
98 | this.isLoading = true;
99 | this.userService
100 | .register(this.user.username, this.user.email, this.password.nativeElement.text)
101 | .subscribe(() => {
102 | this.onBack();
103 | })
104 | .add(() => {
105 | this.isLoading = false;
106 | });
107 | }
108 |
109 | /**
110 | *
111 | */
112 | public onBack() {
113 | this.routerExtensions.navigate(["/home"], { clearHistory: true });
114 | }
115 |
116 | /**
117 | * @param messageKey Key to localize as error message
118 | */
119 | protected showError(messageKey: string) {
120 | this.feedback.error({
121 | title: localize("error.general"),
122 | message: localize(messageKey)
123 | });
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/module/user/profile.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/module/user/profile.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from "@angular/core";
2 | import { Router, ActivatedRoute } from "@angular/router";
3 | import { PropertyChangeData } from "ui/page";
4 | import { RadListViewComponent } from "nativescript-ui-listview/angular";
5 | import { ListViewEventData } from "nativescript-ui-listview";
6 | import { ObservableArray } from "tns-core-modules/data/observable-array/observable-array";
7 | import { Feedback, FeedbackType, FeedbackPosition } from "nativescript-feedback";
8 | import { localize } from "nativescript-localize";
9 | import { Profile } from "~/model/Profile";
10 | import { UserService } from "~/service/UserService";
11 | import { PageRoute } from "nativescript-angular/router";
12 | import { switchMap } from "rxjs/operators";
13 | import { topmost } from "ui/frame";
14 |
15 | @Component({
16 | selector: "conduit-profile",
17 | moduleId: module.id,
18 | templateUrl: "./profile.component.html",
19 | styleUrls: ["./user.css"]
20 | })
21 | export class ProfileComponent implements OnInit {
22 | /** */
23 | public isLoading: boolean = false;
24 | /** */
25 | public isMyArticles: boolean = true;
26 | /** */
27 | private feedback: Feedback;
28 | /** */
29 | public profile: Profile;
30 |
31 | /**
32 | *
33 | * @param router
34 | * @param conduit
35 | */
36 | constructor(private router: Router, private pageRoute: PageRoute, public userService: UserService) {
37 | this.feedback = new Feedback();
38 |
39 | //Get the given username's profile
40 | this.pageRoute.activatedRoute.pipe(switchMap(activatedRoute => activatedRoute.params)).forEach(params => {
41 | this.isLoading = true;
42 | this.userService
43 | .getProfile(params["username"])
44 | .subscribe(
45 | (profile: Profile) => {
46 | this.profile = profile;
47 | },
48 | error => {
49 | this.feedback.error({
50 | title: localize("error.general"),
51 | message: error
52 | });
53 | }
54 | )
55 | .add(() => {
56 | this.isLoading = false;
57 | });
58 | });
59 | }
60 |
61 | /**
62 | *
63 | */
64 | public ngOnInit() {}
65 |
66 | /**
67 | *
68 | * @param args
69 | */
70 | public onFeedChange(args: PropertyChangeData) {
71 | this.isMyArticles = args.value === 0;
72 | }
73 |
74 | /**
75 | * Just follow/unfollow and set the local profile value expecting success.
76 | */
77 | public onFollow() {
78 | this.profile.following = !this.profile.following;
79 | this.userService.followUser(this.profile.username, this.profile.following).subscribe(
80 | (profile: Profile) => {
81 | this.profile = profile;
82 | },
83 | error => {
84 | this.feedback.error({
85 | title: localize("error.general"),
86 | message: error
87 | });
88 | }
89 | );
90 | }
91 |
92 | /**
93 | *
94 | */
95 | public onBack() {
96 | topmost().goBack();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/app/module/user/user.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
2 | import { NativeScriptModule } from "nativescript-angular/nativescript.module";
3 | import { NativeScriptFormsModule } from "nativescript-angular/forms";
4 | import { TNSFontIconModule } from "nativescript-ngx-fonticon";
5 | import { NativeScriptLocalizeModule } from "nativescript-localize/angular";
6 | import { NativeScriptUIDataFormModule } from "nativescript-ui-dataform/angular";
7 |
8 | import { UserRouting } from "./user.routing";
9 |
10 | import { ArticleModule } from "~/module/article/article.module";
11 | import { ServiceModule } from "~/service/service.module";
12 |
13 | import { ProfileComponent } from "./profile.component";
14 | import { LoginComponent } from "./login.component";
15 | import { EditProfileComponent } from "~/module/user/edit-profile.component";
16 |
17 | @NgModule({
18 | imports: [
19 | NativeScriptModule,
20 | NativeScriptFormsModule,
21 | NativeScriptLocalizeModule,
22 | NativeScriptUIDataFormModule,
23 | ServiceModule,
24 | ArticleModule,
25 | UserRouting,
26 | TNSFontIconModule
27 | ],
28 | declarations: [ProfileComponent, LoginComponent, EditProfileComponent],
29 | schemas: [NO_ERRORS_SCHEMA]
30 | })
31 | export class UserModule {}
32 |
--------------------------------------------------------------------------------
/app/module/user/user.routing.ts:
--------------------------------------------------------------------------------
1 | import { ModuleWithProviders } from "@angular/core";
2 | import { Routes, RouterModule } from "@angular/router";
3 |
4 | import { ProfileComponent } from "./profile.component";
5 | import { LoginComponent } from "./login.component";
6 | import { EditProfileComponent } from "~/module/user/edit-profile.component";
7 |
8 | // prettier-ignore
9 | const UserRoutes: Routes = [
10 | { path: 'profile/:username', component: ProfileComponent },
11 | { path: 'user/settings', component: EditProfileComponent },
12 | { path: 'login', component: LoginComponent }
13 | ];
14 |
15 | export const UserRouting: ModuleWithProviders = RouterModule.forChild(UserRoutes);
16 |
--------------------------------------------------------------------------------
/app/module/user/user.scss:
--------------------------------------------------------------------------------
1 | // Import app variables
2 | @import '~/app-variables';
3 |
4 | ActionBar {
5 | background-color: white;
6 | color: black;
7 | border-width: 1;
8 | border-color: transparent;
9 | }
10 |
11 | .page {
12 | align-items: center;
13 | flex-direction: column;
14 | }
15 |
16 | .form {
17 | margin-left: 30;
18 | margin-right: 30;
19 | flex-grow: 2;
20 | vertical-align: middle;
21 | }
22 |
23 | .logo {
24 | margin-bottom: 12;
25 | height: 90;
26 | font-weight: bold;
27 | }
28 |
29 | .header {
30 | horizontal-align: center;
31 | font-size: 25;
32 | font-weight: 600;
33 | margin-bottom: 70;
34 | text-align: center;
35 | color: $brand-primary;
36 | }
37 |
38 | .input-field {
39 | margin-bottom: 25;
40 | }
41 |
42 | .input {
43 | font-size: 18;
44 | placeholder-color: $brand-dark;
45 | }
46 |
47 | .btn-primary {
48 | height: 50;
49 | margin: 30 5 15 5;
50 | background-color: $brand-primary;
51 | border-radius: 5;
52 | font-size: 20;
53 | font-weight: 600;
54 | }
55 |
56 | .login-label {
57 | horizontal-align: center;
58 | color: $brand-dark;
59 | font-size: 16;
60 | }
61 |
62 | .sign-up-label {
63 | margin-bottom: 20;
64 | }
65 |
66 | .bold {
67 | color: #000000;
68 | }
69 |
70 | .follow {
71 | color: green;
72 | }
73 |
74 | .unfollow {
75 | color: red;
76 | }
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "android": {
3 | "v8Flags": "--expose_gc"
4 | },
5 | "main": "main.js",
6 | "name": "tns-template-blank-ng",
7 | "version": "4.0.0"
8 | }
--------------------------------------------------------------------------------
/app/service/AbstractHttpService.ts:
--------------------------------------------------------------------------------
1 | import { Observable as RxObservable } from "rxjs";
2 | import { HttpClient, HttpHeaders, HttpResponse, HttpErrorResponse } from "@angular/common/http";
3 | import { getString, setString } from "application-settings";
4 | import { UserService } from "~/service/UserService";
5 |
6 | /**
7 | *
8 | */
9 | export abstract class AbstractHttpService {
10 | /**
11 | * Default online URL in case no other is given
12 | */
13 | public static PRODUCTIONREADY_IO_API_BASE_URL: string = "https://conduit.productionready.io/api";
14 |
15 | /**
16 | *
17 | * @param http
18 | */
19 | constructor(protected http: HttpClient) {}
20 |
21 | /**
22 | *
23 | * @param urlSuffix
24 | * @param params
25 | */
26 | protected get(urlSuffix, params = {}): RxObservable