├── fastlane
├── metadata
│ ├── copyright.txt
│ ├── en-US
│ │ ├── keywords.txt
│ │ ├── subtitle.txt
│ │ ├── description.txt
│ │ ├── marketing_url.txt
│ │ ├── privacy_url.txt
│ │ ├── release_notes.txt
│ │ ├── support_url.txt
│ │ ├── name.txt
│ │ └── promotional_text.txt
│ ├── primary_category.txt
│ ├── secondary_category.txt
│ ├── primary_first_sub_category.txt
│ ├── review_information
│ │ ├── notes.txt
│ │ ├── demo_user.txt
│ │ ├── first_name.txt
│ │ ├── last_name.txt
│ │ ├── phone_number.txt
│ │ ├── demo_password.txt
│ │ └── email_address.txt
│ ├── primary_second_sub_category.txt
│ ├── secondary_first_sub_category.txt
│ ├── secondary_second_sub_category.txt
│ └── trade_representative_contact_information
│ │ ├── country.txt
│ │ ├── state.txt
│ │ ├── city_name.txt
│ │ ├── postal_code.txt
│ │ ├── address_line2.txt
│ │ ├── is_displayed_on_app_store.txt
│ │ ├── address_line1.txt
│ │ └── trade_name.txt
├── Appfile
├── Matchfile
├── Deliverfile
├── screenshots
│ └── README.txt
├── README.md
├── Fastfile
└── report.xml
├── .gitignore
├── .Podfile.swp
├── mobile
├── resources
│ ├── assets.xcassets
│ │ ├── Contents.json
│ │ ├── ui
│ │ │ ├── Contents.json
│ │ │ ├── fade-top.imageset
│ │ │ │ ├── fade-top@2x.png
│ │ │ │ ├── fade-top@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-bottom.imageset
│ │ │ │ ├── fade-bottom@2x.png
│ │ │ │ ├── fade-bottom@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── dark-fade-top.imageset
│ │ │ │ ├── dark-fade-top@2x.png
│ │ │ │ ├── dark-fade-top@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-top-blue.imageset
│ │ │ │ ├── fade-top-blue@2x.png
│ │ │ │ ├── fade-top-blue@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-top-drag.imageset
│ │ │ │ ├── fade-top-drag@2x.png
│ │ │ │ ├── fade-top-drag@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-top-candy.imageset
│ │ │ │ ├── fade-top-candy@2x.png
│ │ │ │ ├── fade-top-candy@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-top-green.imageset
│ │ │ │ ├── fade-top-green@2x.png
│ │ │ │ ├── fade-top-green@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── dark-fade-bottom.imageset
│ │ │ │ ├── dark-fade-bottom@2x.png
│ │ │ │ ├── dark-fade-bottom@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-bottom-blue.imageset
│ │ │ │ ├── fade-bottom-blue@2x.png
│ │ │ │ ├── fade-bottom-blue@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-bottom-drag.imageset
│ │ │ │ ├── fade-bottom-drag@2x.png
│ │ │ │ ├── fade-bottom-drag@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-top-dracula.imageset
│ │ │ │ ├── fade-top-dracula@2x.png
│ │ │ │ ├── fade-top-dracula@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-bottom-candy.imageset
│ │ │ │ ├── fade-bottom-candy@2x.png
│ │ │ │ ├── fade-bottom-candy@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── fade-bottom-green.imageset
│ │ │ │ ├── fade-bottom-green@2x.png
│ │ │ │ ├── fade-bottom-green@3x.png
│ │ │ │ └── Contents.json
│ │ │ └── fade-bottom-dracula.imageset
│ │ │ │ ├── fade-bottom-dracula@2x.png
│ │ │ │ ├── fade-bottom-dracula@3x.png
│ │ │ │ └── Contents.json
│ │ ├── placeholder.imageset
│ │ │ ├── placeholder@3x.png
│ │ │ └── Contents.json
│ │ └── Mojilist.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-57x57@1x.png
│ │ │ ├── Icon-App-57x57@2x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-72x72@1x.png
│ │ │ ├── Icon-App-72x72@2x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── ItunesArtwork@2x.png
│ │ │ ├── Icon-Small-50x50@1x.png
│ │ │ ├── Icon-Small-50x50@2x.png
│ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ ├── MojilistBlue@1x.png
│ ├── MojilistBlue@2x.png
│ ├── MojilistBlue@3x.png
│ ├── MojilistCandy@1x.png
│ ├── MojilistCandy@2x.png
│ ├── MojilistCandy@3x.png
│ ├── MojilistDark@1x.png
│ ├── MojilistDark@2x.png
│ ├── MojilistDark@3x.png
│ ├── MojilistDrag@1x.png
│ ├── MojilistDrag@2x.png
│ ├── MojilistDrag@3x.png
│ ├── MojilistDracula@1x.png
│ ├── MojilistDracula@2x.png
│ ├── MojilistDracula@3x.png
│ ├── MojilistGreeny@1x.png
│ ├── MojilistGreeny@2x.png
│ └── MojilistGreeny@3x.png
├── views
│ ├── Xibs.swift
│ ├── AboutStoryboard.swift
│ ├── Store.storyboard
│ ├── MainStoryboard.swift
│ └── LaunchScreen.storyboard
├── controllers
│ ├── common
│ │ ├── BaseDataViewModel.swift
│ │ ├── BaseTableViewCell.swift
│ │ ├── BaseCollectionViewCell.swift
│ │ ├── BaseViewModel.swift
│ │ ├── WebViewController.swift
│ │ ├── FloatingButtons.swift
│ │ ├── BaseTableViewController.swift
│ │ ├── BaseCollectionViewController.swift
│ │ ├── EmojiDropView.swift
│ │ └── BaseViewController.swift
│ ├── creating
│ │ ├── CreateListViewModel.swift
│ │ └── CreateListViewController.swift
│ ├── selectPack
│ │ ├── AsciiPackCell.swift
│ │ ├── BasePackCell.swift
│ │ ├── SelectPackViewModel.swift
│ │ ├── ImagePackCell.swift
│ │ └── SelectPackViewController.swift
│ ├── lists
│ │ ├── AsciiListCell.swift
│ │ ├── BaseListCell.swift
│ │ ├── ListsViewModel.swift
│ │ ├── ImageListCell.swift
│ │ └── ListsViewController.swift
│ ├── store
│ │ └── StoreViewController.swift
│ ├── changeTheme
│ │ ├── ChangeThemeViewModel.swift
│ │ └── ChangeThemeViewController.swift
│ ├── using
│ │ ├── AsciiEmojiCell.swift
│ │ ├── BaseEmojiCell.swift
│ │ ├── ImageEmojiCell.swift
│ │ └── UsingListViewModel.swift
│ ├── selectEmojis
│ │ └── SelectEmojisViewModel.swift
│ ├── share
│ │ └── ShareSnippetView.swift
│ └── about
│ │ └── SettingsViewModel.swift
├── core
│ ├── Funcs.swift
│ ├── StoryboardContext.swift
│ ├── Aliases.swift
│ ├── Constants.swift
│ ├── AppConfig.swift
│ ├── Sharing.swift
│ ├── Operators.swift
│ ├── Tracker.swift
│ ├── Extensions.swift
│ ├── Emojis.swift
│ ├── Launcher.swift
│ └── App.swift
├── AppDelegate.swift
├── models
│ └── local
│ │ ├── REmojiPackItem.swift
│ │ ├── REmoji.swift
│ │ ├── REmojiPack.swift
│ │ └── REmojiList.swift
├── localized
│ ├── zh-Hans.lproj
│ │ └── Localizable.strings
│ ├── ko.lproj
│ │ └── Localizable.strings
│ ├── ja.lproj
│ │ └── Localizable.strings
│ ├── en.lproj
│ │ └── Localizable.strings
│ ├── it.lproj
│ │ └── Localizable.strings
│ ├── de.lproj
│ │ └── Localizable.strings
│ ├── ru.lproj
│ │ └── Localizable.strings
│ ├── pt-BR.lproj
│ │ └── Localizable.strings
│ ├── pt-PT.lproj
│ │ └── Localizable.strings
│ ├── es.lproj
│ │ └── Localizable.strings
│ ├── es-419.lproj
│ │ └── Localizable.strings
│ └── fr.lproj
│ │ └── Localizable.strings
└── Info.plist
├── MyPlayground.playground
├── contents.xcplayground
├── playground.xcworkspace
│ └── contents.xcworkspacedata
└── Contents.swift
├── Emojilist.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcuserdata
│ └── thiagoricieri.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── xcshareddata
│ └── xcschemes
│ └── Emojilist.xcscheme
├── Emojilist.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── tests
├── Info.plist
└── EmojilistTests.swift
├── ui_tests
├── Info.plist
└── EmojilistUITests.swift
├── README.md
├── .circleci-not-used
└── config.yml
├── Podfile
└── Podfile.lock
/fastlane/metadata/copyright.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/keywords.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/subtitle.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/primary_category.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Pods/
2 | assets.xcassets/icons/
3 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/description.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/marketing_url.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/privacy_url.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/release_notes.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/support_url.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/secondary_category.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/name.txt:
--------------------------------------------------------------------------------
1 | Pods_Emojilist
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/en-US/promotional_text.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/primary_first_sub_category.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/review_information/notes.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/primary_second_sub_category.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/review_information/demo_user.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/review_information/first_name.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/review_information/last_name.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/review_information/phone_number.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/secondary_first_sub_category.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/secondary_second_sub_category.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/review_information/demo_password.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/review_information/email_address.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.Podfile.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/.Podfile.swp
--------------------------------------------------------------------------------
/fastlane/metadata/trade_representative_contact_information/country.txt:
--------------------------------------------------------------------------------
1 | Brazil
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/trade_representative_contact_information/state.txt:
--------------------------------------------------------------------------------
1 | Parana
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/trade_representative_contact_information/city_name.txt:
--------------------------------------------------------------------------------
1 | Curitiba
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/trade_representative_contact_information/postal_code.txt:
--------------------------------------------------------------------------------
1 | 80620-010
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/trade_representative_contact_information/address_line2.txt:
--------------------------------------------------------------------------------
1 | Agua Verde
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/trade_representative_contact_information/is_displayed_on_app_store.txt:
--------------------------------------------------------------------------------
1 | false
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/trade_representative_contact_information/address_line1.txt:
--------------------------------------------------------------------------------
1 | Av. Republica Argentina 1160, - Sala 408
2 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/fastlane/metadata/trade_representative_contact_information/trade_name.txt:
--------------------------------------------------------------------------------
1 | Ghost Ship Servicos em Tecnologia da Informacao LTDA ME
2 |
--------------------------------------------------------------------------------
/mobile/resources/MojilistBlue@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistBlue@1x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistBlue@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistBlue@2x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistBlue@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistBlue@3x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistCandy@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistCandy@1x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistCandy@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistCandy@2x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistCandy@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistCandy@3x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistDark@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDark@1x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistDark@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDark@2x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistDark@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDark@3x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistDrag@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDrag@1x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistDrag@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDrag@2x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistDrag@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDrag@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/mobile/resources/MojilistDracula@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDracula@1x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistDracula@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDracula@2x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistDracula@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistDracula@3x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistGreeny@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistGreeny@1x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistGreeny@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistGreeny@2x.png
--------------------------------------------------------------------------------
/mobile/resources/MojilistGreeny@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/MojilistGreeny@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top.imageset/fade-top@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top.imageset/fade-top@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top.imageset/fade-top@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top.imageset/fade-top@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/placeholder.imageset/placeholder@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/placeholder.imageset/placeholder@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-57x57@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-57x57@1x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-57x57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-57x57@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-72x72@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-72x72@1x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-72x72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-72x72@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/ItunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/ItunesArtwork@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom.imageset/fade-bottom@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom.imageset/fade-bottom@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom.imageset/fade-bottom@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom.imageset/fade-bottom@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-Small-50x50@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-Small-50x50@1x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-Small-50x50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-Small-50x50@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/Mojilist.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/dark-fade-top.imageset/dark-fade-top@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/dark-fade-top.imageset/dark-fade-top@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/dark-fade-top.imageset/dark-fade-top@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/dark-fade-top.imageset/dark-fade-top@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-blue.imageset/fade-top-blue@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-blue.imageset/fade-top-blue@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-blue.imageset/fade-top-blue@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-blue.imageset/fade-top-blue@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-drag.imageset/fade-top-drag@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-drag.imageset/fade-top-drag@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-drag.imageset/fade-top-drag@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-drag.imageset/fade-top-drag@3x.png
--------------------------------------------------------------------------------
/MyPlayground.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-candy.imageset/fade-top-candy@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-candy.imageset/fade-top-candy@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-candy.imageset/fade-top-candy@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-candy.imageset/fade-top-candy@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-green.imageset/fade-top-green@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-green.imageset/fade-top-green@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-green.imageset/fade-top-green@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-green.imageset/fade-top-green@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/dark-fade-bottom.imageset/dark-fade-bottom@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/dark-fade-bottom.imageset/dark-fade-bottom@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/dark-fade-bottom.imageset/dark-fade-bottom@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/dark-fade-bottom.imageset/dark-fade-bottom@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-blue.imageset/fade-bottom-blue@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-blue.imageset/fade-bottom-blue@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-blue.imageset/fade-bottom-blue@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-blue.imageset/fade-bottom-blue@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-drag.imageset/fade-bottom-drag@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-drag.imageset/fade-bottom-drag@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-drag.imageset/fade-bottom-drag@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-drag.imageset/fade-bottom-drag@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-dracula.imageset/fade-top-dracula@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-dracula.imageset/fade-top-dracula@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-dracula.imageset/fade-top-dracula@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-top-dracula.imageset/fade-top-dracula@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-candy.imageset/fade-bottom-candy@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-candy.imageset/fade-bottom-candy@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-candy.imageset/fade-bottom-candy@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-candy.imageset/fade-bottom-candy@3x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-green.imageset/fade-bottom-green@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-green.imageset/fade-bottom-green@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-green.imageset/fade-bottom-green@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-green.imageset/fade-bottom-green@3x.png
--------------------------------------------------------------------------------
/MyPlayground.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-dracula.imageset/fade-bottom-dracula@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-dracula.imageset/fade-bottom-dracula@2x.png
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-dracula.imageset/fade-bottom-dracula@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thiagoricieri/Mojilist/HEAD/mobile/resources/assets.xcassets/ui/fade-bottom-dracula.imageset/fade-bottom-dracula@3x.png
--------------------------------------------------------------------------------
/Emojilist.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/mobile/views/Xibs.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShareStoryboard.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 14/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Xibs {
12 | static let resources = "Resources"
13 | }
14 |
--------------------------------------------------------------------------------
/Emojilist.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Emojilist.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/fastlane/Appfile:
--------------------------------------------------------------------------------
1 | app_identifier "co.ghostship.ios.Emojilist" # The bundle identifier of your app
2 | apple_id "thiago@ghostship.co" # Your Apple email address
3 | team_id "8H5Z8M869P" # Developer Portal Team ID
4 | itc_team_id "118510554"
5 |
6 | # you can even provide different app identifiers, Apple IDs and team names per lane:
7 | # More information: https://docs.fastlane.tools/advanced/#appfile
8 |
--------------------------------------------------------------------------------
/mobile/controllers/common/BaseDataViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseDataViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class BaseDataViewModel: BaseViewModel {
12 |
13 | var itemsCount: Int! { return 0 }
14 |
15 | func loadSource() {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mobile/views/AboutStoryboard.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AboutStoryboard.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 18/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class AboutStoryboard: StoryboardContext {
12 |
13 | struct Segue {
14 | static let toWebView = "toWebView"
15 | static let toChangeTheme = "toChangeTheme"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mobile/controllers/creating/CreateListViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CreateListViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class CreateListViewModel: BaseViewModel {
12 |
13 | func validateInput(listName: String?) -> Bool {
14 | return listName != nil && !listName!.isEmpty
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/fastlane/Matchfile:
--------------------------------------------------------------------------------
1 | git_url "https://github.com/thiagoricieri/Mojilist-Certificates"
2 |
3 | type "appstore" # The default type, can be: appstore, adhoc, enterprise or development
4 |
5 | # app_identifier ["tools.fastlane.app", "tools.fastlane.app2"]
6 | # username "user@fastlane.tools" # Your Apple Developer Portal username
7 |
8 | # For all available options run `fastlane match --help`
9 | # Remove the # in the beginning of the line to enable the other options
10 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "placeholder@3x.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Emojilist.xcodeproj/xcuserdata/thiagoricieri.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Emojilist.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 3
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-top@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-top@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/views/Store.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-bottom@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-bottom@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/fastlane/Deliverfile:
--------------------------------------------------------------------------------
1 | ###################### More Options ######################
2 | # If you want to have even more control, check out the documentation
3 | # https://docs.fastlane.tools/actions/deliver
4 |
5 |
6 | ###################### Automatically generated ######################
7 | # Feel free to remove the following line if you use fastlane (which you should)
8 |
9 | app_identifier "co.ghostship.ios.Emojilist" # The bundle identifier of your app
10 | username "thiago@ghostship.co" # your Apple ID user
11 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/dark-fade-top.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "dark-fade-top@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "dark-fade-top@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-blue.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-top-blue@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-top-blue@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-candy.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-top-candy@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-top-candy@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-drag.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-top-drag@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-top-drag@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-green.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-top-green@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-top-green@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/dark-fade-bottom.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "dark-fade-bottom@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "dark-fade-bottom@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-blue.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-bottom-blue@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-bottom-blue@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-drag.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-bottom-drag@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-bottom-drag@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-top-dracula.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-top-dracula@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-top-dracula@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/core/Funcs.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Funcs.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 04/01/18.
6 | // Copyright © 2018 GhostShip. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | func UIColorFromRGB(rgb: UInt) -> UIColor {
13 | return UIColor(
14 | red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
15 | green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0,
16 | blue: CGFloat(rgb & 0x0000FF) / 255.0,
17 | alpha: CGFloat(1.0)
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-candy.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-bottom-candy@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-bottom-candy@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-green.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-bottom-green@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-bottom-green@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/ui/fade-bottom-dracula.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "fade-bottom-dracula@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "fade-bottom-dracula@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/fastlane/screenshots/README.txt:
--------------------------------------------------------------------------------
1 | Put all screenshots you want to use inside the folder of its language (e.g. en-US).
2 | The device type will automatically be recognized using the image resolution. Apple TV screenshots
3 | should be stored in a subdirectory named appleTV with language folders inside of it. iMessage
4 | screenshots, like Apple TV screenshots, should also be stored in a subdirectory named iMessage
5 | with language folders inside of it.
6 |
7 | The screenshots can be named whatever you want, but keep in mind they are sorted alphabetically.
8 |
--------------------------------------------------------------------------------
/mobile/controllers/selectPack/AsciiPackCell.swift:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // AsciiPackCell.swift
4 | // Emojilist
5 | //
6 | // Created by Thiago Ricieri on 20/01/2018.
7 | // Copyright © 2018 Ghost Ship. All rights reserved.
8 | //
9 |
10 | import Foundation
11 | import UIKit
12 |
13 | class AsciiPackCell: BasePackCell {
14 |
15 | static let identifier = "AsciiPackCell"
16 |
17 | @IBOutlet weak var emojiText: UILabel!
18 |
19 | override func configure(with item: EmojiPackViewModel) {
20 | super.configure(with: item)
21 | emojiText.text = item.inlineEmojis
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/mobile/controllers/common/BaseTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class BaseTableViewCell: UITableViewCell {
13 |
14 | func applyTheme(_ theme: Theme) {
15 | theme.background(self)
16 | theme.background(self.contentView)
17 |
18 | if let v = self.backgroundView { theme.background(v) }
19 | if let v = self.selectedBackgroundView { theme.darkBackground(v) }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/mobile/controllers/lists/AsciiListCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AsciiListCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class AsciiListCell: BaseListCell {
13 |
14 | static let identifier = "ListCell"
15 | static let cellHeight = CGFloat(100)
16 |
17 | @IBOutlet weak var emojiText: UILabel!
18 |
19 | override func configure(with item: EmojiListViewModel) {
20 | super.configure(with: item)
21 | emojiText.text = item.inlineEmojis
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/mobile/controllers/common/BaseCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseCollectionViewCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class BaseCollectionViewCell: UICollectionViewCell {
13 |
14 | func applyTheme(_ theme: Theme) {
15 | theme.background(self)
16 | theme.background(self.contentView)
17 |
18 | if let v = self.backgroundView { theme.background(v) }
19 | if let v = self.selectedBackgroundView { theme.darkBackground(v) }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/mobile/controllers/store/StoreViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoreViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 09/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class StoreViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | // Do any additional setup after loading the view, typically from a nib.
16 | }
17 |
18 | override func didReceiveMemoryWarning() {
19 | super.didReceiveMemoryWarning()
20 | // Dispose of any resources that can be recreated.
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/mobile/controllers/common/BaseViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | class BaseViewModel {
13 |
14 | // MARK: - Convenience Vars
15 |
16 | var appDelegate: AppDelegate! {
17 | return UIApplication.shared.delegate as! AppDelegate
18 | }
19 | var app: App! {
20 | return appDelegate.app
21 | }
22 | var realm: Realm! {
23 | return app.realm
24 | }
25 | var theme: Theme! {
26 | return app.theme
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/MyPlayground.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 | import UIKit
4 |
5 | let color: Int = 0xFF0000
6 |
7 | func itemsToFit(inWidth: Int, inHeight: Int, withMargin: Int, withSize: Int) -> Int {
8 | let rows = (inHeight - withMargin) / withSize
9 | let columns = (inWidth - withMargin * 2) / withSize
10 | print("Size \(withSize) = Rows \(rows) Columns \(columns) > \(columns * rows) items max")
11 | return columns * rows
12 | }
13 |
14 | let minSize = 50
15 | let maxSize = 220
16 | let maxWidth = 686
17 | let maxHeight = 514
18 | let margin = 8
19 |
20 | //
21 | for i in minSize...maxSize {
22 | itemsToFit(inWidth: maxWidth, inHeight: maxHeight, withMargin: margin, withSize: i)
23 | }
24 |
--------------------------------------------------------------------------------
/mobile/controllers/selectPack/BasePackCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BasePackCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 12/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SDWebImage
12 |
13 | class BasePackCell: BaseTableViewCell {
14 |
15 | static let cellHeight = CGFloat(100)
16 |
17 | @IBOutlet weak var packName: UILabel!
18 | @IBOutlet weak var separator: UIView!
19 |
20 | func configure(with item: EmojiPackViewModel) {
21 | packName.text = item.name.localized
22 | }
23 |
24 | override func applyTheme(_ theme: Theme) {
25 | super.applyTheme(theme)
26 | theme.separator(separator)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/mobile/core/StoryboardContext.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoryboardContext.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 09/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class StoryboardContext {
13 |
14 | var storyboard: UIStoryboard!
15 |
16 | init(){}
17 | init(name: String){
18 | storyboard = UIStoryboard(name: name, bundle: nil)
19 | }
20 |
21 | func controller(name: String) -> UIViewController {
22 | return storyboard.instantiateViewController(withIdentifier: name)
23 | }
24 |
25 | func firstController() -> UIViewController {
26 | return storyboard.instantiateInitialViewController()!
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/mobile/controllers/changeTheme/ChangeThemeViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChangeThemeViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ChangeThemeViewModel: BaseDataViewModel {
12 |
13 | var source = [Visuals]()
14 |
15 | override var itemsCount: Int! {
16 | return source.count
17 | }
18 |
19 | override func loadSource() {
20 | super.loadSource()
21 | source = Theme.available
22 | }
23 |
24 | func item(at: Int) -> Visuals {
25 | return source[at]
26 | }
27 |
28 | func changeVisuals(_ newVisuals: Visuals) {
29 | app.changeVisuals(newVisuals)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ui_tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/mobile/controllers/lists/BaseListCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseListCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class BaseListCell: BaseTableViewCell {
13 |
14 | @IBOutlet weak var listName: UILabel!
15 | @IBOutlet weak var backgroundOverlay: UIView!
16 | @IBOutlet weak var separatorView: UIView!
17 |
18 | func configure(with item: EmojiListViewModel) {
19 | listName.text = item.name
20 | }
21 |
22 | override func applyTheme(_ theme: Theme) {
23 | super.applyTheme(theme)
24 | theme.primaryText(listName)
25 | theme.cellBackground(backgroundOverlay)
26 | theme.separator(separatorView)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/mobile/controllers/lists/ListsViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListsViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ListsViewModel: BaseDataViewModel {
12 |
13 | var source = [EmojiListViewModel]()
14 |
15 | override var itemsCount: Int! {
16 | return source.count
17 | }
18 |
19 | override func loadSource() {
20 | super.loadSource()
21 |
22 | source = realm
23 | .objects(REmojiList.self)
24 | .sorted(byKeyPath: "name")
25 | .map { EmojiListViewModel(with: $0) }
26 | }
27 |
28 | func item(at indexPath: IndexPath) -> EmojiListViewModel {
29 | return source[indexPath.row]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mojilist
2 | 🛍 Create shopping lists with emojis!
3 |
4 |
5 |
6 | ## About
7 | Read more about this project [at my blog post](https://thiago.ricieri.com/open-sourcing-my-latest-ios-app).
8 |
9 | ## License
10 | ```
11 | Copyright 2018 (c) Thiago Ricieri
12 |
13 | Licensed under the Apache License, Version 2.0 (the "License");
14 | you may not use this file except in compliance with the License.
15 | You may obtain a copy of the License at
16 |
17 | http://www.apache.org/licenses/LICENSE-2.0
18 |
19 | Unless required by applicable law or agreed to in writing, software
20 | distributed under the License is distributed on an "AS IS" BASIS,
21 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 | See the License for the specific language governing permissions and
23 | limitations under the License.
24 | ```
25 |
--------------------------------------------------------------------------------
/mobile/controllers/using/AsciiEmojiCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AsciiEmojiCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 11/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class AsciiEmojiCell: BaseEmojiCell {
13 |
14 | static let identifier = "AsciiEmoji"
15 |
16 | @IBOutlet weak var emojiText: UILabel!
17 |
18 | override func configure(with emoji: EmojiPackItemViewModel) {
19 | super.configure(with: emoji)
20 | emojiText.text = emoji.name
21 | }
22 |
23 | override func configure(with emoji: EmojiViewModel) {
24 | super.configure(with: emoji)
25 | emojiText.text = emoji.name
26 | emojiText.alpha = emoji.alphaForCheckedStatus
27 | }
28 |
29 | override func uncheckEmoji() {
30 | emojiText.alpha = CGFloat(EmojiViewModel.uncheckedAlpha)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/mobile/controllers/selectPack/SelectPackViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelectPackViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class SelectPackViewModel: BaseDataViewModel {
12 |
13 | var source: [EmojiPackViewModel]!
14 |
15 | override var itemsCount: Int! {
16 | return source.count
17 | }
18 |
19 | override func loadSource() {
20 | super.loadSource()
21 | source = realm
22 | .objects(REmojiPack.self)
23 | .sorted(byKeyPath: "name")
24 | .map { EmojiPackViewModel(with: $0) }
25 | }
26 |
27 | func item(at indexPath: IndexPath) -> EmojiPackViewModel {
28 | return source[indexPath.row]
29 | }
30 |
31 | func trackChanged(toPack pack: EmojiPackViewModel) {
32 | Tracker.changedPack(packSlug: pack.slug)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/mobile/controllers/using/BaseEmojiCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseEmojiCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 11/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import Spring
12 |
13 | class BaseEmojiCell: BaseCollectionViewCell {
14 |
15 | @IBOutlet weak var protectionBackground: UIView!
16 | @IBOutlet weak var springView: SpringView!
17 |
18 | override func awakeFromNib() {
19 | super.awakeFromNib()
20 |
21 | protectionBackground.clipsToBounds = true
22 | protectionBackground.layer.cornerRadius = protectionBackground.bounds.width/2
23 | }
24 |
25 | override func applyTheme(_ theme: Theme) {
26 | theme.darkBackground(protectionBackground)
27 | }
28 |
29 | func configure(with emoji: EmojiPackItemViewModel) { }
30 | func configure(with emoji: EmojiViewModel) { }
31 | func uncheckEmoji() { }
32 | }
33 |
--------------------------------------------------------------------------------
/mobile/controllers/using/ImageEmojiCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageEmojiCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 11/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SDWebImage
12 |
13 | class ImageEmojiCell: BaseEmojiCell {
14 |
15 | static let identifier = "ImageEmoji"
16 |
17 | @IBOutlet weak var emojiImage: UIImageView!
18 |
19 | override func configure(with emoji: EmojiPackItemViewModel) {
20 | super.configure(with: emoji)
21 | emojiImage.sd_setImage(with: emoji.imageUrl)
22 | }
23 |
24 | override func configure(with emoji: EmojiViewModel) {
25 | super.configure(with: emoji)
26 | emojiImage.sd_setImage(with: emoji.imageUrl)
27 | emojiImage.alpha = emoji.alphaForCheckedStatus
28 | }
29 |
30 | override func uncheckEmoji() {
31 | emojiImage.alpha = CGFloat(EmojiViewModel.uncheckedAlpha)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/mobile/core/Aliases.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Aliases.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 04/01/18.
6 | // Copyright © 2018 GhostShip. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 | import AVFoundation
12 |
13 | public typealias InBackgroundTuple = (URLRequest?, HTTPURLResponse?, Result)
14 | public typealias InBackground = (URLRequest?, HTTPURLResponse?, Result) -> Void
15 | public typealias InBackgroundResponse = (DataResponse) -> Void
16 | public typealias ApiObject = NSObject
17 | public typealias OnProcessFinished = (Bool, Any?) -> (Void)
18 | public typealias Dict = [String: AnyObject]
19 | public typealias LaunchParams = [UIApplicationLaunchOptionsKey: Any]
20 |
21 | public protocol Parametizable {
22 | func parametize() -> Dict
23 | }
24 | public protocol Objectable {
25 | func toApiObject() -> ApiObject
26 | }
27 | public protocol Routable {
28 | }
29 |
30 | public protocol Visibility {
31 | var isVisible : Bool { get set }
32 | }
33 |
--------------------------------------------------------------------------------
/mobile/core/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 04/01/18.
6 | // Copyright © 2018 GhostShip. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 |
12 | // MARK: - ENVIRONMENT
13 | public struct Env {
14 |
15 | public enum Host : Int {
16 | case staging
17 | case production
18 | }
19 |
20 | public struct Key {
21 | static let userAgent = "User-Agent"
22 | }
23 |
24 | public struct Promo {
25 | static let shareUrl = "https://ghostship.co/mojilist"
26 | static let email = "buh@ghostship.co"
27 | }
28 |
29 | public struct App {
30 | static let theming = "app-theming"
31 | static let defaultPack = "default-pack"
32 | static let maxEmojisPerRow = 7
33 | }
34 | }
35 |
36 | // MARK: - Credentials
37 | protocol Credentials {
38 | static var clientId : String { get }
39 | static var clientSecret : String { get }
40 | }
41 |
--------------------------------------------------------------------------------
/mobile/controllers/selectPack/ImagePackCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImagePackCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class ImagePackCell: BasePackCell {
13 |
14 | static let identifier = "ImagePackCell"
15 |
16 | @IBOutlet var emojiImages: [UIImageView]!
17 |
18 | override func configure(with item: EmojiPackViewModel) {
19 | super.configure(with: item)
20 |
21 | emojiImages.forEach { imageView in
22 | let index = emojiImages.index(of: imageView)
23 | if let i = index,
24 | item.firstEmojis.indices.contains(i) {
25 |
26 | let emoji = item.firstEmojis[i]
27 | imageView.sd_setImage(with: emoji.imageUrl)
28 | }
29 | else {
30 | imageView.image = nil
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/mobile/controllers/common/WebViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 18/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 |
12 | class WebViewController: BaseViewController {
13 |
14 | @IBOutlet weak var webView: WKWebView!
15 |
16 | var url = ""
17 |
18 | override func instantiateDependencies() {
19 | baseViewModel = BaseViewModel()
20 | }
21 |
22 | override func prepareViewForUser() {
23 | super.prepareViewForUser()
24 |
25 | let myURL = URL(string: url)
26 | let myRequest = URLRequest(url: myURL!)
27 | webView.load(myRequest)
28 | }
29 |
30 | @IBAction func actionSafari(sender: Any) {
31 | let myURL = URL(string: url)
32 | UIApplication.shared.open(myURL!, options: [:], completionHandler: nil)
33 | navigationController?.popViewController(animated: true)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/mobile/controllers/lists/ImageListCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageListCell.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class ImageListCell: BaseListCell {
13 |
14 | static let identifier = "ImageListCell"
15 | static let cellHeight = CGFloat(100)
16 |
17 | @IBOutlet var emojiImages: [UIImageView]!
18 |
19 | override func configure(with item: EmojiListViewModel) {
20 | super.configure(with: item)
21 |
22 | emojiImages.forEach { imageView in
23 | let index = emojiImages.index(of: imageView)
24 | if let i = index,
25 | item.firstEmojis.indices.contains(i) {
26 |
27 | let emoji = item.firstEmojis[i]
28 | imageView.sd_setImage(with: emoji.imageUrl)
29 | }
30 | else {
31 | imageView.image = nil
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/fastlane/README.md:
--------------------------------------------------------------------------------
1 | fastlane documentation
2 | ================
3 | # Installation
4 |
5 | Make sure you have the latest version of the Xcode command line tools installed:
6 |
7 | ```
8 | xcode-select --install
9 | ```
10 |
11 | Install _fastlane_ using
12 | ```
13 | [sudo] gem install fastlane -NV
14 | ```
15 | or alternatively using `brew cask install fastlane`
16 |
17 | # Available Actions
18 | ## iOS
19 | ### ios test
20 | ```
21 | fastlane ios test
22 | ```
23 | Runs all the tests
24 | ### ios beta
25 | ```
26 | fastlane ios beta
27 | ```
28 | Submit a new Beta Build to Apple TestFlight
29 |
30 | This will also make sure the profile is up to date
31 | ### ios release
32 | ```
33 | fastlane ios release
34 | ```
35 | Deploy a new version to the App Store
36 |
37 | ----
38 |
39 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
40 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
41 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
42 |
--------------------------------------------------------------------------------
/mobile/core/AppConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // App.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 04/01/18.
6 | // Copyright © 2018 GhostShip. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /*
12 | App protocol will represent
13 | default configuration to build
14 | and consume the application
15 | */
16 | public protocol AppConfig {
17 | var environment: Env.Host { get }
18 | var name: String { get }
19 | var restUrl: String { get }
20 | var googleAnalytics: String { get }
21 | }
22 |
23 | // MARK: - Production App
24 | public struct ProductionAppConfigImpl: AppConfig {
25 |
26 | private(set) public var environment = Env.Host.production
27 | private(set) public var name: String = "Emojilist"
28 | private(set) public var restUrl: String = "https://"
29 | }
30 |
31 | // MARK: - Staging App
32 | public struct StagingAppConfigImpl: AppConfig {
33 |
34 | private(set) public var environment = Env.Host.staging
35 | private(set) public var name: String = "Emojilist Staging"
36 | private(set) public var restUrl: String = "https://"
37 | }
38 |
--------------------------------------------------------------------------------
/mobile/controllers/common/FloatingButtons.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FloatButton.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class PrimaryFloatingButton: UIButton {
13 |
14 | override func awakeFromNib() {
15 | super.awakeFromNib()
16 |
17 | self.layer.cornerRadius = 14
18 | self.layer.shadowColor = UIColorFromRGB(rgb: 0x000000).cgColor
19 | self.layer.shadowRadius = 5
20 | self.layer.shadowOpacity = 0.2
21 | self.layer.shadowOffset = CGSize(width: 0, height: 8)
22 | }
23 | }
24 |
25 | class SecondaryFloatingButton: UIButton {
26 |
27 | override func awakeFromNib() {
28 | super.awakeFromNib()
29 |
30 | self.layer.cornerRadius = 8
31 | self.layer.shadowColor = UIColorFromRGB(rgb: 0x000000).cgColor
32 | self.layer.shadowRadius = 3
33 | self.layer.shadowOpacity = 0.2
34 | self.layer.shadowOffset = CGSize(width: 0, height: 6)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/EmojilistTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmojilistTests.swift
3 | // EmojilistTests
4 | //
5 | // Created by Thiago Ricieri on 20/12/2017.
6 | // Copyright © 2017 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Emojilist
11 |
12 | class EmojilistTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/mobile/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/12/2017.
6 | // Copyright © 2017 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RealmSwift
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var launcher = Launcher()
16 | var window: UIWindow?
17 | var app: App = {
18 | #if DEBUG
19 | return StagingAppImpl()
20 | #else
21 | return ProductionAppImpl()
22 | #endif
23 | }()
24 |
25 | func application(_ application: UIApplication,
26 | didFinishLaunchingWithOptions launchOptions: LaunchParams?) -> Bool {
27 |
28 | launcher
29 | .setWindow(window)
30 | .shouldProvideCredentials(false)
31 | .setDefaultPack()
32 | .migrateRealm()
33 | .includeStandardPack()
34 | .setFabric()
35 | .setFacebook()
36 | .setTwitter()
37 | .setLaunchOptions(launchOptions)
38 | .startWith(app: app)
39 |
40 | return true
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/mobile/models/local/REmojiPackItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // REmojiPackItem.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | class REmojiPackItem: Object {
13 |
14 | @objc dynamic var name = ""
15 | @objc dynamic var imageUrl = ""
16 | @objc dynamic var pack = ""
17 | }
18 |
19 | class EmojiPackItemViewModel: BaseViewModel {
20 |
21 | private var model: REmojiPackItem!
22 |
23 | var name: String! {
24 | return model.name
25 | }
26 | var pack: String! {
27 | return model.pack
28 | }
29 | var imageUrl: URL! {
30 | return URL(string: model.imageUrl)!
31 | }
32 | var hasImage: Bool! {
33 | return !model.imageUrl.isEmpty
34 | }
35 |
36 | init(with model: REmojiPackItem) {
37 | self.model = model
38 | }
39 |
40 | init(with viewModel: EmojiViewModel) {
41 | self.model = REmojiPackItem()
42 | self.model.name = viewModel.name
43 | self.model.imageUrl = viewModel.hasImage ? model.imageUrl : ""
44 | self.model.pack = viewModel.pack
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/mobile/views/MainStoryboard.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainStoryboard.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 11/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class MainStoryboard: StoryboardContext {
12 |
13 | struct Segue {
14 | static let toCreate = "toCreate"
15 | static let toSelectEmojis = "toSelectEmojis"
16 | static let toSelectPack = "toSelectPack"
17 | static let toUsingList = "toUsingList"
18 | static let toShare = "toShare"
19 | static let toSettings = "toSettings"
20 | static let toEditList = "toEditList"
21 | }
22 |
23 | func createListViewController() -> CreateListViewController {
24 | return controller(name: "CreateList") as! CreateListViewController
25 | }
26 |
27 | func selectEmojisViewController() -> SelectEmojisViewController {
28 | return controller(name: "SelectEmojis") as! SelectEmojisViewController
29 | }
30 |
31 | func selectPackViewController() -> SelectPackViewController {
32 | return controller(name: "SelectPack") as! SelectPackViewController
33 | }
34 |
35 | func usingListViewController() -> UsingListViewController {
36 | return controller(name: "UsingList") as! UsingListViewController
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ui_tests/EmojilistUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmojilistUITests.swift
3 | // EmojilistUITests
4 | //
5 | // Created by Thiago Ricieri on 20/12/2017.
6 | // Copyright © 2017 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class EmojilistUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 |
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 |
18 | // In UI tests it is usually best to stop immediately when a failure occurs.
19 | continueAfterFailure = false
20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
21 | XCUIApplication().launch()
22 |
23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
24 | }
25 |
26 | override func tearDown() {
27 | // Put teardown code here. This method is called after the invocation of each test method in the class.
28 | super.tearDown()
29 | }
30 |
31 | func testExample() {
32 | // Use recording to get started writing UI tests.
33 | // Use XCTAssert and related functions to verify your tests produce the correct results.
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/.circleci-not-used/config.yml:
--------------------------------------------------------------------------------
1 | # iOS CircleCI 2.0 configuration file
2 | #
3 | # Check https://circleci.com/docs/2.0/ios-migrating-from-1-2/ for more details
4 |
5 | version: 2
6 | jobs:
7 | build:
8 |
9 | macos:
10 | xcode: "9.2.0"
11 |
12 | steps:
13 | - checkout
14 |
15 | - run:
16 | name: Update CocoaPods
17 | command: sudo gem install cocoapods
18 |
19 | - run:
20 | name: Update Fastlane
21 | command: sudo gem install fastlane
22 |
23 | - run:
24 | name: Run Match
25 | command: match appstore
26 |
27 | - run:
28 | name: Fastlane Beta
29 | command: fastlane beta
30 |
31 | # Build the app and run tests
32 | # - run:
33 | # name: Build and run tests
34 | # command: fastlane scan
35 | # environment:
36 | # SCAN_DEVICE: iPhone 6
37 | # SCAN_SCHEME: WebTests
38 |
39 | # Collect XML test results data to show in the UI,
40 | # and save the same XML files under test-results folder
41 | # in the Artifacts tab
42 | # - store_test_results:
43 | # path: test_output/report.xml
44 | # - store_artifacts:
45 | # path: /tmp/test-results
46 | # destination: scan-test-results
47 | # - store_artifacts:
48 | # path: ~/Library/Logs/scan
49 | # destination: scan-logs
50 |
51 |
--------------------------------------------------------------------------------
/mobile/models/local/REmoji.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmojiItem.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | class REmoji: Object {
13 |
14 | @objc dynamic var name = ""
15 | @objc dynamic var imageUrl = ""
16 | @objc dynamic var pack = ""
17 | @objc dynamic var checked = false
18 | }
19 |
20 | class EmojiViewModel: BaseViewModel {
21 |
22 | private var model: REmoji!
23 |
24 | static let uncheckedAlpha = CGFloat(0.2)
25 | static let checkedAlpha = CGFloat(1.0)
26 |
27 | var name: String! {
28 | return model.name
29 | }
30 | var imageUrl: URL! {
31 | return URL(string: model.imageUrl)!
32 | }
33 | var pack: String! {
34 | return model.pack
35 | }
36 | var isChecked: Bool! {
37 | return model.checked
38 | }
39 | var hasImage: Bool! {
40 | return !model.imageUrl.isEmpty
41 | }
42 | var alphaForCheckedStatus: CGFloat! {
43 | return isChecked ? EmojiViewModel.checkedAlpha : EmojiViewModel.uncheckedAlpha
44 | }
45 |
46 | init(with model: REmoji) {
47 | self.model = model
48 | }
49 |
50 | func change(checked: Bool) {
51 | try! realm.write {
52 | model.checked = checked
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/mobile/controllers/using/UsingListViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UsingListViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class UsingListViewModel: BaseDataViewModel {
12 |
13 | var source: EmojiListViewModel!
14 | var name: String! {
15 | return source.name
16 | }
17 |
18 | override var itemsCount: Int! {
19 | return source.items.count
20 | }
21 |
22 | init(list: EmojiListViewModel) {
23 | self.source = list
24 | }
25 |
26 | func item(at indexPath: IndexPath) -> EmojiViewModel {
27 | return source.items[indexPath.row]
28 | }
29 |
30 | func toggleEmoji(at indexPath: IndexPath) {
31 | let emoji = item(at: indexPath)
32 | emoji.change(checked: !emoji.isChecked)
33 | }
34 |
35 | func reuseList() {
36 | Tracker.reusedList(itemsCount: itemsCount)
37 | source.uncheckCheckedEmojis()
38 | }
39 |
40 | func deleteList() {
41 | Tracker.deleteList(itemsCount: itemsCount)
42 | source.delete()
43 | }
44 |
45 | func trackSharing() {
46 | Tracker.shareImage()
47 | }
48 |
49 | func trackCompletedList() {
50 | Tracker.completeList(
51 | itemsCount: itemsCount,
52 | itemsUsed: source.checkedEmojis.count)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | use_frameworks!
2 |
3 | def shared_pods
4 | pod 'Spring', :git => 'https://github.com/MengTo/Spring.git'
5 | pod 'MBProgressHUD'
6 | pod 'SDWebImage', '~> 3.7.3'
7 | pod 'Alamofire'
8 | pod 'Fabric'
9 | pod 'Crashlytics', '~> 3.8'
10 | pod 'RealmSwift'
11 | pod 'HTTPStatusCodes', '~> 3.1.0'
12 | pod 'DateToolsSwift'
13 | pod 'PopupDialog', '~> 0.6'
14 | pod 'Firebase/Core'
15 | end
16 |
17 | target 'Emojilist' do
18 | shared_pods
19 |
20 | target 'EmojilistTests' do
21 | inherit! :search_paths
22 | end
23 |
24 | target 'EmojilistUITests' do
25 | inherit! :search_paths
26 | end
27 | end
28 |
29 | #post_install do |installer|
30 | # installer.aggregate_targets.each do |target|
31 | # copy_pods_resources_path = "Pods/Target Support Files/#{target.name}/#{target.name}-resources.sh"
32 | # string_to_replace = '--compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"'
33 | # assets_compile_with_app_icon_arguments = '--compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${BUILD_DIR}/assetcatalog_generated_info.plist"'
34 | # text = File.read(copy_pods_resources_path)
35 | # new_contents = text.gsub(string_to_replace, assets_compile_with_app_icon_arguments)
36 | # File.open(copy_pods_resources_path, "w") {|file| file.puts new_contents }
37 | # end
38 | #end
39 |
40 |
--------------------------------------------------------------------------------
/mobile/core/Sharing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sharing.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 15/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class Marketing {
13 |
14 | static func share(app controllerReady: (UIActivityViewController) -> Void) {
15 | let copyToShare = "About.Share.Copy".localized + " " + Env.Promo.shareUrl
16 | let urlToShare = URL(string: Env.Promo.shareUrl)!
17 | let activityViewController = UIActivityViewController(
18 | activityItems: [copyToShare, urlToShare],
19 | applicationActivities: nil)
20 | controllerReady(activityViewController)
21 | }
22 |
23 | static func share(list viewModel: EmojiListViewModel,
24 | controllerReady: (UIActivityViewController) -> Void) {
25 |
26 | let firstActivityItem = "\(viewModel.name) #mojilist"
27 | let secondActivityItem = URL(string: Env.Promo.shareUrl)!
28 | let shareView = Bundle.loadView(fromNib: Xibs.resources, withType: ShareSnippetView.self)
29 | shareView.configure(with: viewModel)
30 |
31 | let image = UIImage(view: shareView)
32 | let activityViewController = UIActivityViewController(
33 | activityItems: [firstActivityItem, secondActivityItem, image],
34 | applicationActivities: nil)
35 |
36 | controllerReady(activityViewController)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/mobile/models/local/REmojiPack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // REmojiPack.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | class REmojiPack: Object {
13 |
14 | @objc dynamic var slug = ""
15 | @objc dynamic var name = ""
16 | @objc dynamic var url = ""
17 | @objc dynamic var ascii = false
18 |
19 | let emojis = List()
20 | }
21 |
22 | class EmojiPackViewModel: BaseViewModel {
23 |
24 | private var model: REmojiPack!
25 |
26 | var name: String! {
27 | return model.name
28 | }
29 | var slug: String! {
30 | return model.slug
31 | }
32 | var url: String! {
33 | return model.url
34 | }
35 | var isTextual: Bool! {
36 | return model.ascii
37 | }
38 | var items: [EmojiPackItemViewModel]! {
39 | return model.emojis.map { EmojiPackItemViewModel(with: $0) }
40 | }
41 | var firstEmojis: [EmojiPackItemViewModel]! {
42 | let maxEmojis = Env.App.maxEmojisPerRow
43 | return items.count > maxEmojis ?
44 | items[0...maxEmojis].map { $0 } : items
45 | }
46 | var inlineEmojis: String! {
47 | return firstEmojis.reduce("") { soFar, emoji -> String! in
48 | return soFar + " " + emoji.name
49 | }
50 | }
51 |
52 | init(with model: REmojiPack) {
53 | self.model = model
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/mobile/controllers/changeTheme/ChangeThemeViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChangeThemeViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 18/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class ChangeThemeViewController: BaseTableViewController {
13 |
14 | var viewModel: ChangeThemeViewModel!
15 |
16 | override func instantiateDependencies() {
17 | baseViewModel = ChangeThemeViewModel()
18 | viewModel = baseViewModel as! ChangeThemeViewModel
19 | }
20 |
21 | override func setViewStyle() {
22 | title = "ChangeTheme.Title".localized
23 | }
24 |
25 | // MARK: - Table View
26 |
27 | override func tableView(_ tableView: UITableView,
28 | cellForRowAt indexPath: IndexPath) -> UITableViewCell {
29 |
30 | let cell = tableView.dequeueReusableCell(withIdentifier: "Theme")!
31 | let newVisuals = viewModel.item(at: indexPath.row)
32 |
33 | let theme = viewModel.theme!
34 | theme.cellBackground(cell)
35 | theme.primaryText(cell.textLabel!)
36 |
37 | cell.textLabel?.text = newVisuals.identifier.localized
38 | return cell
39 | }
40 |
41 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
42 | return 60.0
43 | }
44 |
45 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
46 | tableView.deselectRow(at: indexPath, animated: true)
47 |
48 | let newVisuals = viewModel.item(at: indexPath.row)
49 | viewModel.changeVisuals(newVisuals)
50 | applyTheme(viewModel.theme)
51 | reload()
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/mobile/controllers/common/BaseTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseTableViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class BaseTableViewController: BaseViewController,
13 | UITableViewDelegate,
14 | UITableViewDataSource {
15 |
16 | @IBOutlet weak var table: UITableView!
17 |
18 | override func applyTheme(_ theme: Theme) {
19 | super.applyTheme(theme)
20 | theme.background(table)
21 | }
22 |
23 | // MARK: - View Rendering
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 | table.delegate = self
28 | table.dataSource = self
29 | }
30 |
31 | override func viewWillAppear(_ animated: Bool) {
32 | super.viewWillAppear(animated)
33 |
34 | if let viewModel = baseViewModel as? BaseDataViewModel {
35 | viewModel.loadSource()
36 | reload()
37 | }
38 | }
39 |
40 | // MARK: - Table Delegate
41 | func reload() {
42 | table.reloadData()
43 | }
44 |
45 | func tableView(_ tableView: UITableView,
46 | numberOfRowsInSection section: Int) -> Int {
47 | if let viewModel = baseViewModel as? BaseDataViewModel {
48 | return viewModel.itemsCount
49 | }
50 | return 0
51 | }
52 |
53 | func tableView(_ tableView: UITableView,
54 | willDisplay cell: UITableViewCell,
55 | forRowAt indexPath: IndexPath) {
56 | if let c = cell as? BaseTableViewCell {
57 | c.applyTheme(baseViewModel.theme)
58 | }
59 | }
60 |
61 | func tableView(_ tableView: UITableView,
62 | cellForRowAt indexPath: IndexPath) -> UITableViewCell {
63 | return BaseTableViewCell()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | # Customize this file, documentation can be found here:
2 | # https://docs.fastlane.tools/actions/
3 | # All available actions: https://docs.fastlane.tools/actions
4 | # can also be listed using the `fastlane actions` command
5 |
6 | update_fastlane
7 | min_fastlane_version("2.72.0")
8 | default_platform(:ios)
9 |
10 | platform :ios do
11 | before_all do
12 | # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..."
13 | cocoapods
14 | end
15 |
16 | desc "Runs all the tests"
17 | lane :test do
18 | run_tests
19 | end
20 |
21 | desc "Submit a new Beta Build to Apple TestFlight"
22 | desc "This will also make sure the profile is up to date"
23 | lane :beta do
24 | # sync_code_signing(type: "appstore") # more information: https://codesigning.guide
25 | build_app(scheme: "Emojilist") # more options available
26 | upload_to_testflight
27 |
28 | # sh "your_script.sh"
29 | # You can also use other beta testing services here (run `fastlane actions`)
30 | end
31 |
32 | desc "Deploy a new version to the App Store"
33 | lane :release do
34 | # sync_code_signing(type: "appstore")
35 | capture_screenshots
36 | build_app(scheme: "Emojilist") # more options available
37 | upload_to_app_store(force: true)
38 | # frame_screenshots
39 | end
40 |
41 | # You can define as many lanes as you want
42 |
43 | after_all do |lane|
44 | # This block is called, only if the executed lane was successful
45 |
46 | # slack(
47 | # message: "Successfully deployed new App Update."
48 | # )
49 | end
50 |
51 | error do |lane, exception|
52 | # slack(
53 | # message: exception.message,
54 | # success: false
55 | # )
56 | end
57 | end
58 |
59 | # More information about multiple platforms in fastlane: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
60 | # All available actions: https://docs.fastlane.tools/actions
61 |
62 | # fastlane reports which actions are used. No personal data is recorded.
63 | # Learn more at https://docs.fastlane.tools/#metrics
64 |
--------------------------------------------------------------------------------
/mobile/controllers/common/BaseCollectionViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseCollectionViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 11/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class BaseCollectionViewController: BaseViewController,
13 | UICollectionViewDelegate,
14 | UICollectionViewDataSource {
15 |
16 | @IBOutlet weak var collection: UICollectionView!
17 |
18 | override func applyTheme(_ theme: Theme) {
19 | super.applyTheme(theme)
20 | theme.background(collection)
21 | }
22 |
23 | // MARK: - View Rendering
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 | collection.delegate = self
28 | collection.dataSource = self
29 | }
30 |
31 | override func viewWillAppear(_ animated: Bool) {
32 | super.viewWillAppear(animated)
33 |
34 | if let viewModel = baseViewModel as? BaseDataViewModel {
35 | viewModel.loadSource()
36 | reload()
37 | }
38 | }
39 |
40 | // MARK: - Collection Delegate
41 |
42 | func reload() {
43 | collection.reloadData()
44 | }
45 |
46 | func collectionView(_ collectionView: UICollectionView,
47 | numberOfItemsInSection section: Int) -> Int {
48 | if let viewModel = baseViewModel as? BaseDataViewModel {
49 | return viewModel.itemsCount
50 | }
51 | return 0
52 | }
53 |
54 | func collectionView(_ collectionView: UICollectionView,
55 | willDisplay cell: UICollectionViewCell,
56 | forItemAt indexPath: IndexPath) {
57 |
58 | if let c = cell as? BaseEmojiCell {
59 | c.applyTheme(baseViewModel.theme)
60 | }
61 | }
62 |
63 | func collectionView(_ collectionView: UICollectionView,
64 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
65 | return UICollectionViewCell()
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/mobile/controllers/selectPack/SelectPackViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelectPackViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/12/2017.
6 | // Copyright © 2017 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol SelectPackDelegate {
12 | func packSelected(pack: EmojiPackViewModel)
13 | func selectedPack() -> EmojiPackViewModel
14 | }
15 |
16 | class SelectPackViewController: BaseTableViewController {
17 |
18 | @IBOutlet weak var newListButton: PrimaryFloatingButton!
19 | @IBOutlet weak var storeButton: PrimaryFloatingButton!
20 |
21 | var viewModel: SelectPackViewModel!
22 | var delegate: SelectPackDelegate!
23 |
24 | override func instantiateDependencies() {
25 | baseViewModel = SelectPackViewModel()
26 | viewModel = baseViewModel as! SelectPackViewModel
27 | }
28 |
29 | override func setViewStyle() {
30 | title = "SelectPack.Title".localized
31 | }
32 |
33 | // MARK: - Table View Boilerplate
34 |
35 | override func tableView(_ tableView: UITableView,
36 | cellForRowAt indexPath: IndexPath) -> UITableViewCell {
37 |
38 | let item = viewModel.item(at: indexPath)
39 | let cell: BasePackCell!
40 |
41 | if item.isTextual {
42 | cell = tableView.dequeueReusableCell(
43 | withIdentifier: AsciiPackCell.identifier) as! AsciiPackCell
44 | } else {
45 | cell = tableView.dequeueReusableCell(
46 | withIdentifier: ImagePackCell.identifier) as! ImagePackCell
47 | }
48 | cell.configure(with: item)
49 |
50 | return cell
51 | }
52 |
53 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
54 | let item = viewModel.item(at: indexPath)
55 | viewModel.trackChanged(toPack: item)
56 | delegate.packSelected(pack: item)
57 | }
58 |
59 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
60 | return BasePackCell.cellHeight
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/mobile/core/Operators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Operators.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 04/01/18.
6 | // Copyright © 2018 GhostShip. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - REGULAR EXPRESSIONS -
12 | struct Regex {
13 | var pattern: String{
14 | didSet{
15 | //updateRegex()
16 | }
17 | }
18 | var expressionOptions: NSRegularExpression.Options{
19 | didSet{
20 |
21 | }
22 | }
23 | var matchingOptions: NSRegularExpression.MatchingOptions
24 | var regex : NSRegularExpression?
25 | init(pattern: String, expressionOptions: NSRegularExpression.Options, matchingOptions: NSRegularExpression.MatchingOptions) {
26 | self.pattern = pattern
27 | self.expressionOptions = expressionOptions
28 | self.matchingOptions = matchingOptions
29 | }
30 | init(pattern:String) {
31 | self.pattern = pattern
32 | expressionOptions = NSRegularExpression.Options(rawValue: 0)
33 | matchingOptions = NSRegularExpression.MatchingOptions(rawValue: 0)
34 | }
35 | }
36 |
37 | // MARK: - Operator for Comparing Regex -
38 | precedencegroup ComparingRegexPrecedenceGroup {
39 | associativity: left
40 | higherThan: LogicalConjunctionPrecedence
41 | }
42 | infix operator =~ : ComparingRegexPrecedenceGroup
43 | func =~ (left: String, right: Regex) -> Bool {
44 | let range: NSRange = NSMakeRange(0, left.count)
45 | if (right.regex != nil) {
46 | let matches:[AnyObject] = right.regex!.matches(in: left, options: right.matchingOptions, range: range)
47 | return matches.count > 0
48 | }
49 | return false
50 | }
51 | func =~(left: String, right: String) -> Bool {
52 | return left =~ Regex(pattern: right)
53 | }
54 |
55 | // MARK: - Operator for Replacing Regex -
56 | precedencegroup ReplacingRegexPrecedenceGroup {
57 | associativity: left
58 | higherThan: LogicalConjunctionPrecedence
59 | }
60 | infix operator >< : ReplacingRegexPrecedenceGroup
61 | func >< (left:String, right: (regex:Regex,template:String) ) -> String{
62 | if left =~ right.regex {
63 | let range: NSRange = NSMakeRange(0, left.count)
64 | if (right.regex.regex != nil) {
65 | return right.regex.regex!.stringByReplacingMatches(in: left, options: right.regex.matchingOptions, range: range, withTemplate: right.template)
66 | }
67 | }
68 | return left
69 | }
70 | func >< (left:String, right: (pattern:String,template:String) ) -> String{
71 | return left >< (Regex(pattern: right.pattern),right.template)
72 | }
73 |
--------------------------------------------------------------------------------
/mobile/controllers/common/EmojiDropView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmojiDropView.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 13/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import Spring
12 | import SDWebImage
13 |
14 | class EmojiDropView: SpringView {
15 |
16 | @IBOutlet weak var emojiText: UILabel!
17 | @IBOutlet weak var emojiImage: UIImageView!
18 | @IBOutlet weak var protectedArea: UIView!
19 |
20 | override func awakeFromNib() {
21 | emojiText.adjustsFontSizeToFitWidth = true
22 | protectedArea.layer.cornerRadius = protectedArea.bounds.width/2
23 | protectedArea.backgroundColor = UIColorFromRGB(rgb: 0xF1F1F1)
24 | //protectedArea.layer.shadowColor = UIColorFromRGB(rgb: 0x000000).cgColor
25 | //protectedArea.layer.shadowRadius = 3
26 | //protectedArea.layer.shadowOpacity = 0.2
27 | //protectedArea.layer.shadowOffset = CGSize(width: 0, height: 6)
28 | }
29 |
30 | func applyTheme(_ theme: Theme) {
31 | theme.darkBackground(protectedArea)
32 | theme.primaryText(emojiText)
33 | }
34 |
35 | func configure(with emoji: EmojiPackItemViewModel) {
36 | if !emoji.hasImage {
37 | emojiImage.isHidden = true
38 | emojiText.isHidden = false
39 | emojiText.text = emoji.name
40 | } else {
41 | emojiImage.isHidden = false
42 | emojiText.isHidden = true
43 | emojiImage.sd_setImage(with: emoji.imageUrl)
44 | }
45 | }
46 |
47 | func configure(with emoji: EmojiViewModel) {
48 | if !emoji.hasImage {
49 | emojiImage.isHidden = true
50 | emojiText.isHidden = false
51 | emojiText.text = emoji.name
52 | } else {
53 | emojiImage.isHidden = false
54 | emojiText.isHidden = true
55 | emojiImage.sd_setImage(with: emoji.imageUrl)
56 | }
57 | }
58 |
59 | func resize(square itemSize: Int) {
60 | protectedArea.layer.cornerRadius = CGFloat(itemSize/2)
61 | emojiText.font = UIFont(name: emojiText.font.fontName, size: CGFloat(itemSize) * 0.6)
62 | }
63 |
64 | func dropAnimation(toX: CGFloat, toY: CGFloat) {
65 | x = toX
66 | animateToNext {
67 | self.y = toY
68 | self.animateTo()
69 | self.animateToNext {
70 | self.removeFromSuperview()
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/mobile/core/Tracker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Analytics.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 15/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Firebase
11 |
12 | class Tracker {
13 |
14 | static func newList(itemsCount: Int) {
15 | Analytics.logEvent("new_list", parameters: [
16 | "items_count": itemsCount as NSObject
17 | ])
18 | }
19 |
20 | static func changedPack(packSlug: String) {
21 | Analytics.logEvent("changed_pack", parameters: [
22 | "slug": packSlug as NSObject
23 | ])
24 | }
25 |
26 | static func completeList(itemsCount: Int, itemsUsed: Int) {
27 | Analytics.logEvent("complete_list", parameters: [
28 | "items_count": itemsCount as NSObject,
29 | "items_used": itemsUsed as NSObject
30 | ])
31 | }
32 |
33 | static func deleteList(itemsCount: Int) {
34 | Analytics.logEvent("delete_list", parameters: [
35 | "items_count": itemsCount as NSObject,
36 | ])
37 | }
38 |
39 | static func reusedList(itemsCount: Int) {
40 | Analytics.logEvent("reused_list", parameters: [
41 | "items_count": itemsCount as NSObject,
42 | ])
43 | }
44 |
45 | static func changedTheme(themeSlug: String) {
46 | Analytics.logEvent("changed_theme", parameters: [
47 | "slug": themeSlug as NSObject
48 | ])
49 | }
50 |
51 | static func shareImage() {
52 | Analytics.logEvent("share_image", parameters: nil)
53 | }
54 |
55 | static func shareApp() {
56 | Analytics.logEvent("share_app", parameters: nil)
57 | }
58 |
59 | static func rateApp() {
60 | Analytics.logEvent("rate_app", parameters: nil)
61 | }
62 |
63 | static func followInstagram() {
64 | Analytics.logEvent("follow_instagram", parameters: nil)
65 | }
66 |
67 | static func followTwitter() {
68 | Analytics.logEvent("follow_twitter", parameters: nil)
69 | }
70 |
71 | static func followFacebook() {
72 | Analytics.logEvent("follow_facebook", parameters: nil)
73 | }
74 |
75 | static func followBlog() {
76 | Analytics.logEvent("follow_blog", parameters: nil)
77 | }
78 |
79 | static func signupNewsletter() {
80 | Analytics.logEvent("signup_newsletter", parameters: nil)
81 | }
82 |
83 | static func contactDeveloper() {
84 | Analytics.logEvent("contact_developer", parameters: nil)
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/mobile/core/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Foundation+Extensions.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 04/01/18.
6 | // Copyright © 2018 GhostShip. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | // MARK: - Optionals
13 | extension Optional {
14 | func unwrapOrElse(_ val:Wrapped) -> Wrapped {
15 | if self != nil {
16 | return self!
17 | } else {
18 | return val
19 | }
20 | }
21 |
22 | func isNil() -> Bool {
23 | return self == nil
24 | }
25 |
26 | func isNotNil() -> Bool {
27 | return self != nil
28 | }
29 | }
30 |
31 | extension UIImage {
32 | convenience init(view: UIView) {
33 | UIGraphicsBeginImageContextWithOptions(view.frame.size, false, 0)
34 | view.layer.render(in: UIGraphicsGetCurrentContext()!)
35 |
36 | let image = UIGraphicsGetImageFromCurrentImageContext()
37 | UIGraphicsEndImageContext()
38 | self.init(cgImage: image!.cgImage!)
39 | }
40 | }
41 |
42 | extension Bundle {
43 |
44 | static func loadView(fromNib name: String, withType type: T.Type) -> T {
45 | guard let nibs = Bundle.main.loadNibNamed(name, owner: nil, options: nil) as? [UIView] else {
46 | fatalError("Could not load view with type")
47 | }
48 |
49 | for nib in nibs {
50 | if let view = nib as? T {
51 | return view
52 | }
53 | }
54 |
55 | fatalError("Could not load view with type " + String(describing: type))
56 | }
57 | }
58 |
59 | // MARK: - Debug
60 | extension NSObject {
61 |
62 | // Print + Name
63 | func pn(_ message: String) {
64 | #if DEBUG
65 | print("[\(type(of: self))] \(message)")
66 | #endif
67 | }
68 |
69 | // Just print
70 | func pr(_ message: String) {
71 | #if DEBUG
72 | print("\(message)")
73 | #endif
74 | }
75 |
76 | // Print + Name + Date
77 | func pd(_ message: String) {
78 | #if DEBUG
79 | let date = Date()
80 | pn("at \(date): \(message)")
81 | #endif
82 | }
83 | }
84 |
85 | // MARK: - Localizable
86 | extension String {
87 | var localized: String {
88 | return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
89 | }
90 | func localizedWithComment(_ comment:String) -> String {
91 | return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: comment)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/mobile/localized/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "是";
12 | "No" = "没有";
13 | "Warning" = "警告!";
14 | "Success" = "成功!";
15 | "Dismiss" = "解雇";
16 | "Cancel" = "取消";
17 | "Shared" = "共享!";
18 |
19 | // Errors
20 | "Email.Error" = "不幸的是,您的设备未配置为发送电子邮件。 请发送邮件至buh@ghostship.co与我们联系。";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "事情'Emojis!";
24 | "Pack.AllEmojis" = "所有的表情符号!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "新";
29 | "Lists.Settings" = "设置";
30 | "Lists.Empty" = "空!";
31 | "Lists.Empty.Msg" = "通过创建可视化列表,您将节省更多的时间。 使用这些名单为您的杂货,购物项目和更多!";
32 |
33 | // Using List
34 | "UsingList.Done" = "完成!";
35 | "UsingList.Settings.Title" = "列表的设置";
36 | "UsingList.Settings.Msg" = "您可以从设备中删除此列表,也可以通过文字与朋友分享图片或在社交网络上分享。";
37 | "UsingList.Settings.Redo" = "重新启动此列表。";
38 | "UsingList.Settings.DeleteList" = "删除这个列表";
39 | "UsingList.Delete.Msg" = "为防万一,请再次确认您要删除此列表。 此操作无法撤消。";
40 | "UsingList.Settings.DeleteListConfirmation" = "删除列表";
41 | "UsingList.Settings.Edit" = "编辑列表";
42 |
43 | // Share
44 | "Share.Credits1" = "与Mojilist创建";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "新列表";
49 | "CreateList.Text.Placeholder" = "杂货";
50 | "CreateList.Label" = "你想怎么称呼它?";
51 | "CreateList.Hint" = "食品杂货,超市或购物物品可以快速创建视觉。 您将在下一步中选择表情符号项目。 玩的开心!";
52 | "CreateList.Next" = "下一个";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "什么进入名单?";
56 | "SelectEmojis.Create" = "创建";
57 | "SelectEmojis.Update" = "更新";
58 | "SelectEmojis.Clear" = "明确";
59 | "SelectEmojis.SelectPack" = "选定的包: ";
60 | "SelectEmojis.ItemsInList" = "列表中的项目。 如果需要,触摸一下将其删除。";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "包";
64 |
65 | // About
66 | "About.Title" = "设置";
67 | "About.Settings" = "自定义您的应用程序!";
68 | "About.About" = "关于";
69 | "About.Promo" = "分享";
70 | "About.Follow" = "跟随";
71 | "About.MoreApps" = "更多应用程序!";
72 | "About.Settings.DefaultPack" = "默认包: ";
73 | "About.Settings.Theme" = "使用的主题: ";
74 | "About.Promo.Signup" = "注册我们的名单!";
75 | "About.Promo.Share" = "与朋友分享这个应用程序";
76 | "About.Promo.Rate" = "评价应用程序 ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "访问博客";
81 | "About.About.Contact" = "联系开发人员";
82 | "About.About.Feature" = "发表您的意见";
83 | "About.About.Version" = "应用版本";
84 | "About.Share.Copy" = "下载Mojilist,为您的日常列表的视觉替代!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "主题";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 天";
91 | "Theme.Dark" = "🌚 晚";
92 | "Theme.Greeny" = "🍁 绿叶";
93 | "Theme.Blue" = "🌊 海浪";
94 | "Theme.Drag" = "💄 口红";
95 | "Theme.Dracula" = "💥 甜蜜的梦";
96 | "Theme.Candy" = "🍭 糖抢";
97 |
--------------------------------------------------------------------------------
/mobile/controllers/creating/CreateListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CreateListViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 09/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CreateListViewController: BaseViewController {
12 |
13 | @IBOutlet weak var listNameField: UITextField!
14 | @IBOutlet weak var listNameLabel: UILabel!
15 | @IBOutlet weak var hintLabel: UILabel!
16 | @IBOutlet weak var nextBarButton: UIBarButtonItem!
17 |
18 | var viewModel: CreateListViewModel!
19 | var passListViewModel: EmojiListViewModel?
20 |
21 | override func instantiateDependencies() {
22 | baseViewModel = CreateListViewModel()
23 | viewModel = baseViewModel as! CreateListViewModel
24 | }
25 |
26 | override func applyTheme(_ theme: Theme) {
27 | super.applyTheme(theme)
28 | theme.darkBackground(listNameField)
29 | theme.primaryText(listNameLabel)
30 | theme.primaryText(listNameField)
31 | theme.secondaryText(hintLabel)
32 | }
33 |
34 | override func setViewStyle() {
35 | title = "CreateList.Title".localized
36 | listNameField.placeholder = "CreateList.Text.Placeholder".localized
37 | listNameLabel.text = "CreateList.Label".localized
38 | hintLabel.text = "CreateList.Hint".localized
39 | nextBarButton.title = "CreateList.Next".localized
40 | }
41 |
42 | override func prepareViewForUser() {
43 | listNameField.becomeFirstResponder()
44 | if let listVM = passListViewModel {
45 | listNameField.text = listVM.name
46 | }
47 | navigationController?.isNavigationBarHidden = false
48 | }
49 |
50 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
51 | if segue.identifier == MainStoryboard.Segue.toSelectEmojis {
52 | let dest = segue.destination as! SelectEmojisViewController
53 |
54 | if let listVM = passListViewModel {
55 | listVM.pendingName = listNameField.text!
56 | dest.passListViewModel = listVM
57 | } else {
58 | dest.passListViewModel = EmojiListViewModel(named: listNameField.text!)
59 | }
60 | }
61 | }
62 |
63 | @IBAction func actionNext(sender: Any?) {
64 | if viewModel.validateInput(listName: listNameField.text) {
65 | goToSelectEmojis()
66 | }
67 | }
68 |
69 | func goToSelectEmojis() {
70 | performSegue(withIdentifier: MainStoryboard.Segue.toSelectEmojis, sender: nil)
71 | }
72 | }
73 |
74 | // MARK: - Text Field Delegate
75 | extension CreateListViewController: UITextFieldDelegate {
76 |
77 | func textFieldShouldReturn(_ textField: UITextField) -> Bool {
78 | actionNext(sender: nil)
79 | return true
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Alamofire (4.7.1)
3 | - Crashlytics (3.10.1):
4 | - Fabric (~> 1.7.5)
5 | - DateToolsSwift (4.0.0)
6 | - DynamicBlurView (2.0.2)
7 | - Fabric (1.7.6)
8 | - Firebase/Core (4.13.0):
9 | - FirebaseAnalytics (= 4.2.0)
10 | - FirebaseCore (= 4.0.20)
11 | - FirebaseAnalytics (4.2.0):
12 | - FirebaseCore (~> 4.0)
13 | - FirebaseInstanceID (~> 2.0)
14 | - GoogleToolboxForMac/NSData+zlib (~> 2.1)
15 | - nanopb (~> 0.3)
16 | - FirebaseCore (4.0.20):
17 | - GoogleToolboxForMac/NSData+zlib (~> 2.1)
18 | - FirebaseInstanceID (2.0.10):
19 | - FirebaseCore (~> 4.0)
20 | - GoogleToolboxForMac/Defines (2.1.3)
21 | - GoogleToolboxForMac/NSData+zlib (2.1.3):
22 | - GoogleToolboxForMac/Defines (= 2.1.3)
23 | - HTTPStatusCodes (3.1.2)
24 | - MBProgressHUD (1.1.0)
25 | - nanopb (0.3.8):
26 | - nanopb/decode (= 0.3.8)
27 | - nanopb/encode (= 0.3.8)
28 | - nanopb/decode (0.3.8)
29 | - nanopb/encode (0.3.8)
30 | - PopupDialog (0.7.1):
31 | - DynamicBlurView (~> 2.0)
32 | - Realm (3.3.2):
33 | - Realm/Headers (= 3.3.2)
34 | - Realm/Headers (3.3.2)
35 | - RealmSwift (3.3.2):
36 | - Realm (= 3.3.2)
37 | - SDWebImage (3.7.6):
38 | - SDWebImage/Core (= 3.7.6)
39 | - SDWebImage/Core (3.7.6)
40 | - Spring (1.0.5)
41 |
42 | DEPENDENCIES:
43 | - Alamofire
44 | - Crashlytics (~> 3.8)
45 | - DateToolsSwift
46 | - Fabric
47 | - Firebase/Core
48 | - HTTPStatusCodes (~> 3.1.0)
49 | - MBProgressHUD
50 | - PopupDialog (~> 0.6)
51 | - RealmSwift
52 | - SDWebImage (~> 3.7.3)
53 | - Spring (from `https://github.com/MengTo/Spring.git`)
54 |
55 | EXTERNAL SOURCES:
56 | Spring:
57 | :git: https://github.com/MengTo/Spring.git
58 |
59 | CHECKOUT OPTIONS:
60 | Spring:
61 | :commit: 19b04d730d3edabbb02a39e58121d1d3220690ee
62 | :git: https://github.com/MengTo/Spring.git
63 |
64 | SPEC CHECKSUMS:
65 | Alamofire: 68d7d521118d49c615a8d2214d87cdf525599d30
66 | Crashlytics: aee1a064cbbf99b32efa3f056a5f458d846bc8ff
67 | DateToolsSwift: 875d97ff9e3a5d54abdd67a269b3f51c757b71ab
68 | DynamicBlurView: aa6db4defc6e537eb86fc5e58dd3991666178399
69 | Fabric: f8d42c893bb187326a7968b62abe55c36a987a46
70 | Firebase: 5ec5e863d269d82d66b4bf56856726f8fb8f0fb3
71 | FirebaseAnalytics: 7ef69e76a5142f643aeb47c780e1cdce4e23632e
72 | FirebaseCore: 90cb1c53d69b556f112a1bf72b5fcfaad7650790
73 | FirebaseInstanceID: 8d20d890d65c917f9f7d9950b6e10a760ad34321
74 | GoogleToolboxForMac: 2501e2ad72a52eb3dfe7bd9aee7dad11b858bd20
75 | HTTPStatusCodes: 88155ba36826366a898a7b4f34d311f49083d546
76 | MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
77 | nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
78 | PopupDialog: 39598b7d41719f5c025b4b70e62728bae7734fef
79 | Realm: d927fbf66df5532cfafc08afb5f7e53ded37b894
80 | RealmSwift: 4e903a494e05d866581d69ad6f7e7b879d446baf
81 | SDWebImage: c325cf02c30337336b95beff20a13df489ec0ec9
82 | Spring: 48aad470f3ead043b2da98b64e0bf0630b7ebc42
83 |
84 | PODFILE CHECKSUM: c803d265eb34fe0432eefc82780c49c4438409d5
85 |
86 | COCOAPODS: 1.4.0
87 |
--------------------------------------------------------------------------------
/mobile/localized/ko.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "예";
12 | "No" = "아니";
13 | "Warning" = "경고!";
14 | "Success" = "성공!";
15 | "Dismiss" = "버리다";
16 | "Cancel" = "취소";
17 | "Shared" = "공유 된!";
18 |
19 | // Errors
20 | "Email.Error" = "안타깝게도 기기가 이메일을 보내도록 설정되어 있지 않습니다. buh@ghostship.co로 연락하여 저희에게 연락하십시오.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "이모티콘!";
24 | "Pack.AllEmojis" = "모든 이모티콘!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "새로운";
29 | "Lists.Settings" = "설정";
30 | "Lists.Empty" = "목록이 없습니다!";
31 | "Lists.Empty.Msg" = "시각적 목록을 작성하면 훨씬 더 많은 시간을 절약 할 수 있습니다. 식료품 점, 쇼핑 용품 등을 위해이 목록을 사용하십시오!";
32 |
33 | // Using List
34 | "UsingList.Done" = "끝난!";
35 | "UsingList.Settings.Title" = "목록 설정";
36 | "UsingList.Settings.Msg" = "이 목록을 장치에서 제거하거나 텍스트로 친구와 이미지를 공유하거나 소셜 네트워크에서 공유 할 수 있습니다.";
37 | "UsingList.Settings.Redo" = "이 목록을 다시 시작하십시오.";
38 | "UsingList.Settings.DeleteList" = "이 목록 삭제";
39 | "UsingList.Delete.Msg" = "이 경우 해당 목록을 삭제할 것인지 다시 확인하십시오. 이 작업은 실행 취소 할 수 없습니다.";
40 | "UsingList.Settings.DeleteListConfirmation" = "목록 삭제";
41 | "UsingList.Settings.Edit" = "목록 수정";
42 |
43 | // Share
44 | "Share.Credits1" = "Mojilist로 만들었습니다.";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "새 목록";
49 | "CreateList.Text.Placeholder" = "식료 잡화류";
50 | "CreateList.Label" = "어떻게 부를까요?";
51 | "CreateList.Hint" = "팁 : 식료품 점, 슈퍼마켓 또는 쇼핑 아이템을 시각적으로 신속하게 만들 수 있습니다. 다음 단계에서 그림 이모티콘을 선택합니다. 재미있어!";
52 | "CreateList.Next" = "다음 것";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "무엇이 목록에 포함됩니까?";
56 | "SelectEmojis.Create" = "새로운";
57 | "SelectEmojis.Update" = "최신 정보";
58 | "SelectEmojis.Clear" = "명확한";
59 | "SelectEmojis.SelectPack" = "선택된 팩: ";
60 | "SelectEmojis.ItemsInList" = "목록의 항목. 필요한 경우 하나를 터치하여 제거하십시오.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "팩";
64 |
65 | // About
66 | "About.Title" = "설정";
67 | "About.Settings" = "앱을 맞춤 설정하십시오!";
68 | "About.About" = "약";
69 | "About.Promo" = "몫";
70 | "About.Follow" = "따르다";
71 | "About.MoreApps" = "더 많은 애플 리케이션!";
72 | "About.Settings.DefaultPack" = "기본 팩: ";
73 | "About.Settings.Theme" = "사용한 테마: ";
74 | "About.Promo.Signup" = "우리 명부에 등록하십시오!";
75 | "About.Promo.Share" = "친구와이 앱 공유";
76 | "About.Promo.Rate" = "앱 평가하기 ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "블로그 방문";
81 | "About.About.Contact" = "개발자에게 문의하십시오.";
82 | "About.About.Feature" = "의견 보내기";
83 | "About.About.Version" = "앱 버전";
84 | "About.Share.Copy" = "Mojilist를 다운로드하십시오. 일상의 목록에 대한 시각적 대안!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "테마";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 일";
91 | "Theme.Dark" = "🌚 밤";
92 | "Theme.Greeny" = "🍁 나무";
93 | "Theme.Blue" = "🌊 파도";
94 | "Theme.Drag" = "💄 매력";
95 | "Theme.Dracula" = "💥 달콤한 꿈";
96 | "Theme.Candy" = "🍭 설탕 러시";
97 |
--------------------------------------------------------------------------------
/mobile/localized/ja.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "はい";
12 | "No" = "いいえ";
13 | "Warning" = "警告!";
14 | "Success" = "成功!";
15 | "Dismiss" = "却下する";
16 | "Cancel" = "キャンセル";
17 | "Shared" = "共有!";
18 |
19 | // Errors
20 | "Email.Error" = "残念ながら、お使いの端末は電子メールを送信するように設定されていません。 buh@ghostship.coまでお問い合わせください。";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "物事エモジス!";
24 | "Pack.AllEmojis" = "すべてのEmojis!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "新しい";
29 | "Lists.Settings" = "設定";
30 | "Lists.Empty" = "空の!";
31 | "Lists.Empty.Msg" = "ビジュアルリストを作成することで、はるかに多くの時間を節約できます。 あなたの食料品、ショッピングアイテムなどにこれらのリストを使用してください!";
32 |
33 | // Using List
34 | "UsingList.Done" = "完了!";
35 | "UsingList.Settings.Title" = "リストの設定";
36 | "UsingList.Settings.Msg" = "このリストを端末から削除したり、友だちと画像をテキストで共有したり、ソーシャルネットワーク上で共有することができます。";
37 | "UsingList.Settings.Redo" = "このリストを再起動します。";
38 | "UsingList.Settings.DeleteList" = "このリストを削除する";
39 | "UsingList.Delete.Msg" = "その場合は、もう一度このリストを削除することを確認してください。 この操作は元に戻すことはできません。";
40 | "UsingList.Settings.DeleteListConfirmation" = "リストを削除する";
41 | "UsingList.Settings.Edit" = "リストを編集する";
42 |
43 | // Share
44 | "Share.Credits1" = "Mojilistで作成";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "新しいリスト";
49 | "CreateList.Text.Placeholder" = "食料品";
50 | "CreateList.Label" = "どのようにそれを呼びたいのですか?";
51 | "CreateList.Hint" = "食料品、スーパーマーケットまたはショッピングアイテムを視覚的に迅速に作成することができます。 次のステップで、絵文字アイテムを選択します。 楽しむ!";
52 | "CreateList.Next" = "次";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "何がリストに入りますか?";
56 | "SelectEmojis.Create" = "作成する";
57 | "SelectEmojis.Update" = "更新";
58 | "SelectEmojis.Clear" = "クリア";
59 | "SelectEmojis.SelectPack" = "選択したパック: ";
60 | "SelectEmojis.ItemsInList" = "リスト内の項目。 必要に応じて1つをタップして削除します.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "パック";
64 |
65 | // About
66 | "About.Title" = "設定";
67 | "About.Settings" = "あなたのアプリケーションをカスタマイズ!";
68 | "About.About" = "約";
69 | "About.Promo" = "シェア";
70 | "About.Follow" = "フォローする";
71 | "About.MoreApps" = "より多くのアプリケーション!";
72 | "About.Settings.DefaultPack" = "デフォルトパック: ";
73 | "About.Settings.Theme" = "使用テーマ: ";
74 | "About.Promo.Signup" = "私たちのリストにサインアップする!";
75 | "About.Promo.Share" = "このアプリを友達と共有";
76 | "About.Promo.Rate" = "アプリを評価する ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "ブログをご覧ください";
81 | "About.About.Contact" = "開発者に連絡する";
82 | "About.About.Feature" = "意見を述べる";
83 | "About.About.Version" = "アプリ版";
84 | "About.Share.Copy" = "あなたの毎日のリストの視覚的な代替物であるMojilistをダウンロードしてください!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "テーマ";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 日";
91 | "Theme.Dark" = "🌚 夜";
92 | "Theme.Greeny" = "🍁 青葉";
93 | "Theme.Blue" = "🌊 海洋波";
94 | "Theme.Drag" = "💄 チャーム";
95 | "Theme.Dracula" = "💥 良い夢を";
96 | "Theme.Candy" = "🍭 シュガーラッシュ";
97 |
--------------------------------------------------------------------------------
/mobile/controllers/selectEmojis/SelectEmojisViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelectEmojisViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class SelectEmojisViewModel: BaseDataViewModel {
12 |
13 | private var listViewModel: EmojiListViewModel!
14 | private var selectedItems = [EmojiPackItemViewModel]()
15 |
16 | var source: EmojiPackViewModel!
17 |
18 | override var itemsCount: Int! {
19 | return source.items.count
20 | }
21 | var listName: String! {
22 | return listViewModel.pendingName ?? listViewModel.name
23 | }
24 | var selectedCount: Int! {
25 | return selectedItems.count
26 | }
27 | var selectedCountText: String! {
28 | return String(selectedCount)
29 | }
30 | var localizedPackName: String! {
31 | return "SelectEmojis.SelectPack".localized + " \(source.name!.localized)"
32 | }
33 | var shouldUpdateList: Bool! {
34 | return listViewModel.pendingName != nil
35 | }
36 | var createButtonLabel: String! {
37 | return listViewModel.pendingName == nil ? "SelectEmojis.Create" : "SelectEmojis.Update"
38 | }
39 |
40 | init(listViewModel: EmojiListViewModel) {
41 | super.init()
42 | self.listViewModel = listViewModel
43 |
44 | let defaults = UserDefaults.standard
45 | if let pack = defaults.string(forKey: Env.App.defaultPack) {
46 | let predicate = NSPredicate(format: "slug = %@", pack)
47 | let packs = realm.objects(REmojiPack.self).filter(predicate)
48 | if packs.count > 0 {
49 | source = EmojiPackViewModel(with: packs.first!)
50 | }
51 | } else {
52 | source = app.standardEmojiPack()
53 | }
54 |
55 | if listViewModel.items.count > 0 {
56 | selectedItems = listViewModel
57 | .items.map { viewModel -> EmojiPackItemViewModel in
58 | EmojiPackItemViewModel(with: viewModel)
59 | }
60 | }
61 | }
62 |
63 | func item(at indexPath: IndexPath) -> EmojiPackItemViewModel {
64 | return source.items[indexPath.row]
65 | }
66 |
67 | // MARK: - Handle Selection
68 |
69 | func item(selectedAt indexPath: IndexPath) -> EmojiPackItemViewModel {
70 | return selectedItems[indexPath.row]
71 | }
72 |
73 | func select(emojiAt indexPath: IndexPath) {
74 | let emoji = item(at: indexPath)
75 | selectedItems.insert(emoji, at: 0)
76 | }
77 |
78 | func updateList() {
79 | listViewModel.update(withPackItems: selectedItems)
80 | Tracker.newList(itemsCount: selectedItems.count)
81 | }
82 |
83 | func createList() {
84 | listViewModel.create(withPackItems: selectedItems)
85 | Tracker.newList(itemsCount: selectedItems.count)
86 | }
87 |
88 | func remove(emojiAt indexPath: IndexPath) {
89 | selectedItems.remove(at: indexPath.row)
90 | }
91 |
92 | func clearList() {
93 | selectedItems = []
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/mobile/models/local/REmojiList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmojiList.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | class REmojiList: Object {
13 |
14 | @objc dynamic var name = ""
15 | @objc dynamic var dateCreated: Date? = nil
16 | @objc dynamic var timesUsed = 0
17 |
18 | let emojis = List()
19 | }
20 |
21 | class EmojiListViewModel: BaseViewModel {
22 |
23 | private var model: REmojiList!
24 |
25 | var pendingName: String?
26 |
27 | var name: String! {
28 | return model.name
29 | }
30 | var dateCreated: Date? {
31 | return model.dateCreated
32 | }
33 | var timesUsed: Int! {
34 | return model.timesUsed
35 | }
36 | var items: [EmojiViewModel]! {
37 | return model.emojis.map { EmojiViewModel(with: $0) }
38 | }
39 | var checkedEmojis: [EmojiViewModel]! {
40 | return items.filter { $0.isChecked }
41 | }
42 | var firstEmojis: [EmojiViewModel]! {
43 | let maxEmojis = Env.App.maxEmojisPerRow
44 | return items.count > maxEmojis ?
45 | items[0...maxEmojis].map { $0 } : items
46 | }
47 | var inlineEmojis: String! {
48 | return firstEmojis.reduce("") { soFar, emoji -> String! in
49 | return soFar + " " + emoji.name
50 | }
51 | }
52 |
53 | init(named: String) {
54 | self.model = REmojiList()
55 | self.model.name = named
56 | }
57 | init(with model: REmojiList) {
58 | self.model = model
59 | }
60 |
61 | func uncheckCheckedEmojis() {
62 | let allChecked = model.emojis.filter("checked = true")
63 | try! realm.write {
64 | allChecked.setValue(false, forKey: "checked")
65 | }
66 | }
67 |
68 | func delete() {
69 | try! realm.write {
70 | realm.delete(model)
71 | }
72 | }
73 |
74 | func update(withPackItems itemModels: [EmojiPackItemViewModel]) {
75 | try! realm.write {
76 | model.name = pendingName!
77 | realm.delete(model.emojis)
78 | model.emojis.append(objectsIn: itemModels.map { item -> REmoji in
79 | return self.emojiModel(from: item)
80 | })
81 | }
82 | }
83 |
84 | func create(withPackItems itemModels: [EmojiPackItemViewModel]) {
85 | try! realm.write {
86 | model.timesUsed = 0
87 | model.dateCreated = Date()
88 | model.emojis.append(objectsIn: itemModels.map { item -> REmoji in
89 | return self.emojiModel(from: item)
90 | })
91 | realm.add(model)
92 | }
93 | }
94 |
95 | func emojiModel(from viewModel: EmojiPackItemViewModel) -> REmoji {
96 | let emoji = REmoji()
97 | emoji.name = viewModel.name
98 | emoji.checked = false
99 | emoji.imageUrl = viewModel.hasImage ? viewModel.imageUrl.absoluteString : ""
100 | emoji.pack = viewModel.pack
101 | return emoji
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/mobile/localized/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "Yes";
12 | "No" = "No";
13 | "Warning" = "Warning!";
14 | "Success" = "Success!";
15 | "Dismiss" = "Dismiss";
16 | "Cancel" = "Cancel";
17 | "Shared" = "Shared!";
18 |
19 | // Errors
20 | "Email.Error" = "Unfortunately, your device is not configured to send emails. Please, send a message to buh@ghostship.co to contact us.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "Things' Emojis!";
24 | "Pack.AllEmojis" = "All Emojis!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "New";
29 | "Lists.Settings" = "Settings";
30 | "Lists.Empty" = "No list!";
31 | "Lists.Empty.Msg" = "You will save much more time by creating visual list. Use these lists for your groceries, shopping items and more!";
32 |
33 | // Using List
34 | "UsingList.Done" = "Done!";
35 | "UsingList.Settings.Title" = "List's Settings";
36 | "UsingList.Settings.Msg" = "You can remove this list from your device or share an image of it with friends by text or share it on your social networks.";
37 | "UsingList.Settings.Redo" = "Restart this list.";
38 | "UsingList.Settings.DeleteList" = "Delete this list";
39 | "UsingList.Delete.Msg" = "Just in case, please confirm again you want to delete this list. This action cannot be undone.";
40 | "UsingList.Settings.DeleteListConfirmation" = "Delete the list";
41 | "UsingList.Settings.Edit" = "Edit the list";
42 |
43 | // Share
44 | "Share.Credits1" = "Created with Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "New List";
49 | "CreateList.Text.Placeholder" = "Groceries";
50 | "CreateList.Label" = "How do you want to call it?";
51 | "CreateList.Hint" = "Tip: groceries, supermarket or shopping items can be quickly created visually. You will select emoji items in the next step. Have fun!";
52 | "CreateList.Next" = "Next";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "What goes into the list?";
56 | "SelectEmojis.Create" = "Create";
57 | "SelectEmojis.Update" = "Update";
58 | "SelectEmojis.Clear" = "Clear";
59 | "SelectEmojis.SelectPack" = "Selected Pack: ";
60 | "SelectEmojis.ItemsInList" = "Items in the list. Touch in one to remove it, if needed.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "Packs";
64 |
65 | // About
66 | "About.Title" = "Settings";
67 | "About.Settings" = "Customize your App!";
68 | "About.About" = "About";
69 | "About.Promo" = "Share";
70 | "About.Follow" = "Follow";
71 | "About.MoreApps" = "More apps!";
72 | "About.Settings.DefaultPack" = "Default pack: ";
73 | "About.Settings.Theme" = "Theme used: ";
74 | "About.Promo.Signup" = "Signup to our list!";
75 | "About.Promo.Share" = "Share this app with friends";
76 | "About.Promo.Rate" = "Rate the app ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Visit the Blog";
81 | "About.About.Contact" = "Contact the developer";
82 | "About.About.Feature" = "Send an opinion";
83 | "About.About.Version" = "App version";
84 | "About.Share.Copy" = "Download Mojilist, a visual alternative for your everyday's lists!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Themes";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 Mojilist by Day";
91 | "Theme.Dark" = "🌚 Mojilist by Night";
92 | "Theme.Greeny" = "🍁 Green leaves";
93 | "Theme.Blue" = "🌊 Ocean waves";
94 | "Theme.Drag" = "💄 Charm";
95 | "Theme.Dracula" = "💥 Sweet Dreams";
96 | "Theme.Candy" = "🍭 Sugar Rush";
97 |
--------------------------------------------------------------------------------
/mobile/localized/it.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "Sì";
12 | "No" = "No";
13 | "Warning" = "Attenzione!";
14 | "Success" = "Successo!";
15 | "Dismiss" = "Respingere";
16 | "Cancel" = "Annullare";
17 | "Shared" = "Condivisa!";
18 |
19 | // Errors
20 | "Email.Error" = "Il tuo dispositivo non supporta l'invio di email. Si prega di inviare una mail a buh@ghostship.co per entrare in contatto.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "Emojis delle Cose!";
24 | "Pack.AllEmojis" = "Tutti gli Emoji!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "Nuova";
29 | "Lists.Settings" = "Aggiustamenti";
30 | "Lists.Empty" = "Nessuna lista!";
31 | "Lists.Empty.Msg" = "Puoi risparmiare molto più tempo creando elenchi visivi. Usa le liste della spesa della settimana, lavoretti e altro!";
32 |
33 | // Using List
34 | "UsingList.Done" = "Pronto!";
35 | "UsingList.Settings.Title" = "Aggiustamenti";
36 | "UsingList.Settings.Msg" = "Puoi rimuovere l'intero elenco dal tuo dispositivo o condividerne un'immagine sui tuoi social network.";
37 | "UsingList.Settings.Redo" = "Riavvia la lista.";
38 | "UsingList.Settings.DeleteList" = "Cancella la lista";
39 | "UsingList.Delete.Msg" = "Conferma la rimozione della lista";
40 | "UsingList.Settings.DeleteListConfirmation" = "Rimuovi la lista";
41 | "UsingList.Settings.Edit" = "Modifica lista";
42 |
43 | // Share
44 | "Share.Credits1" = "Creato con Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "Nuova Lista";
49 | "CreateList.Text.Placeholder" = "Shopping della settimana";
50 | "CreateList.Label" = "Come sarà chiamata la nuova lista?";
51 | "CreateList.Hint" = "Liste di cose da fare, negozi di alimentari o materiale scolastico possono essere creati rapidamente visivamente. Seleziona emoji nel passaggio successivo e divertiti!";
52 | "CreateList.Next" = "Seguente";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "Cosa c'è nella lista?";
56 | "SelectEmojis.Create" = "Creare";
57 | "SelectEmojis.Update" = "Aggiornare";
58 | "SelectEmojis.Clear" = "Per svuotare";
59 | "SelectEmojis.SelectPack" = "Pacchetto selezionato:";
60 | "SelectEmojis.ItemsInList" = "Toccare una volta per rimuovere.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "Pacchetti";
64 |
65 | // About
66 | "About.Title" = "Aggiustamenti";
67 | "About.Settings" = "Personalizza la tua app!";
68 | "About.About" = "Circa";
69 | "About.Promo" = "Condividi questo";
70 | "About.Follow" = "Segui sui social network";
71 | "About.MoreApps" = "Altre apps!";
72 | "About.Settings.DefaultPack" = "Pacchetto standard: ";
73 | "About.Settings.Theme" = "Tema usato: ";
74 | "About.Promo.Signup" = "Iscriviti alla Newsletter!";
75 | "About.Promo.Share" = "Condividi questa app";
76 | "About.Promo.Rate" = "Valuta questa app ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Visita il blog";
81 | "About.About.Contact" = "Contatta lo sviluppatore";
82 | "About.About.Feature" = "Scrivi una recensione";
83 | "About.About.Version" = "Versione dell'app";
84 | "About.Share.Copy" = "Scarica Mojilist, un'alternativa visiva alle tue liste giornaliere!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Temi";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 Giorno";
91 | "Theme.Dark" = "🌚 Notte";
92 | "Theme.Greeny" = "🍁 Fogliame";
93 | "Theme.Blue" = "🌊 Onde del mare";
94 | "Theme.Drag" = "💄 Bellezza rara";
95 | "Theme.Dracula" = "💥 Sogni d'oro";
96 | "Theme.Candy" = "🍭 Più zucchero!";
97 |
--------------------------------------------------------------------------------
/mobile/localized/de.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "Ja";
12 | "No" = "Nicht";
13 | "Warning" = "Achtung!";
14 | "Success" = "Erfolg!";
15 | "Dismiss" = "Entlassen";
16 | "Cancel" = "Abbrechen";
17 | "Shared" = "Geteilt!";
18 |
19 | // Errors
20 | "Email.Error" = "Ihr Gerät unterstützt das Senden von E-Mails nicht. Bitte senden Sie eine E-Mail an buh@ghostship.co, um sich zu melden..";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "Emoji der Dinge!";
24 | "Pack.AllEmojis" = "Alle Emojis!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "Neu";
29 | "Lists.Settings" = "Einstellungen";
30 | "Lists.Empty" = "Keine Liste!";
31 | "Lists.Empty.Msg" = "Sie sparen viel Zeit, indem Sie visuelle Listen erstellen. Verwenden Sie Ihre Einkaufslisten der Woche, Schulbedarf, Hausarbeiten und vieles mehr!";
32 |
33 | // Using List
34 | "UsingList.Done" = "Erledigt!";
35 | "UsingList.Settings.Title" = "Listeneinstellungen";
36 | "UsingList.Settings.Msg" = "Sie können Ihre gesamte Liste von Ihrem Gerät entfernen oder ein Bild davon in Ihren sozialen Netzwerken teilen.";
37 | "UsingList.Settings.Redo" = "Starten Sie die Liste neu.";
38 | "UsingList.Settings.DeleteList" = "Löschen Sie diese Liste";
39 | "UsingList.Delete.Msg" = "Bitte bestätigen Sie das Entfernen der folgenden Liste:";
40 | "UsingList.Settings.DeleteListConfirmation" = "Löschen Sie die Liste";
41 | "UsingList.Settings.Edit" = "Liste bearbeiten";
42 |
43 | // Share
44 | "Share.Credits1" = "Erstellt mit Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "Neue Liste";
49 | "CreateList.Text.Placeholder" = "Einkaufen der Woche";
50 | "CreateList.Label" = "Wie wird die neue Liste heißen?";
51 | "CreateList.Hint" = "Aufgabenlisten, Lebensmittel oder Schulbedarf können schnell visuell erstellt werden. Wählen Sie Emojis im nächsten Schritt und genießen Sie!";
52 | "CreateList.Next" = "Weiter";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "Was ist auf der Liste?";
56 | "SelectEmojis.Create" = "Erstellen";
57 | "SelectEmojis.Update" = "Aktualisieren";
58 | "SelectEmojis.Clear" = "Um zu leeren";
59 | "SelectEmojis.SelectPack" = "Ausgewähltes Paket:";
60 | "SelectEmojis.ItemsInList" = "Tippen Sie einmal auf, um zu entfernen.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "Pakete";
64 |
65 | // About
66 | "About.Title" = "Einstellungen";
67 | "About.Settings" = "Passen Sie Ihre App an!";
68 | "About.About" = "Über";
69 | "About.Promo" = "Teilen Sie dies";
70 | "About.Follow" = "Folge in sozialen Netzwerken";
71 | "About.MoreApps" = "Mehr Apps!";
72 | "About.Settings.DefaultPack" = "Standardpaket: ";
73 | "About.Settings.Theme" = "Thema: ";
74 | "About.Promo.Signup" = "Abonniere den Newsletter!";
75 | "About.Promo.Share" = "Teilen Sie diese App";
76 | "About.Promo.Rate" = "Bewerten Sie diese App ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Besuchen Sie den Blog";
81 | "About.About.Contact" = "Kontakt Entwickler";
82 | "About.About.Feature" = "Überprüfen Sie die App";
83 | "About.About.Version" = "App-Version";
84 | "About.Share.Copy" = "Laden Sie Mojilist herunter, eine visuelle Alternative zu Ihren täglichen Listen!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Thema";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 Tageszeit";
91 | "Theme.Dark" = "🌚 Nacht";
92 | "Theme.Greeny" = "🍁 Blätter";
93 | "Theme.Blue" = "🌊 Meereswellen";
94 | "Theme.Drag" = "💄 Fußgängerbrücke";
95 | "Theme.Dracula" = "💥 Schöne Träume";
96 | "Theme.Candy" = "🍭 Mehr Zucker, bitte!";
97 |
--------------------------------------------------------------------------------
/mobile/controllers/lists/ListsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/12/2017.
6 | // Copyright © 2017 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ListsViewController: BaseTableViewController {
12 |
13 | @IBOutlet weak var newListButton: PrimaryFloatingButton!
14 | @IBOutlet weak var storeButton: PrimaryFloatingButton!
15 | @IBOutlet weak var settingsBarItem: UIBarButtonItem!
16 | @IBOutlet weak var emptyView: UIView!
17 | @IBOutlet weak var emptyTitle: UILabel!
18 | @IBOutlet weak var emptyMsg: UILabel!
19 | @IBOutlet weak var downDecoration: UIImageView!
20 | @IBOutlet weak var emptyDecoration: UIImageView!
21 |
22 | var viewModel: ListsViewModel!
23 |
24 | override func instantiateDependencies() {
25 | baseViewModel = ListsViewModel()
26 | viewModel = baseViewModel as! ListsViewModel
27 | }
28 |
29 | override func setViewStyle() {
30 | title = "Lists.Title".localized
31 | newListButton.setTitle("Lists.New".localized, for: .normal)
32 | emptyTitle.text = "Lists.Empty".localized
33 | emptyMsg.text = "Lists.Empty.Msg".localized
34 | }
35 |
36 | override func applyTheme(_ theme: Theme) {
37 | super.applyTheme(theme)
38 | theme.actionButton(newListButton)
39 | theme.primaryText(emptyTitle)
40 | theme.secondaryText(emptyMsg)
41 |
42 | downDecoration.image = UIImage(named: theme.downArrowDecoration())
43 | emptyDecoration.image = UIImage(named: theme.emptyBoxDecoration())
44 | }
45 |
46 | override func reload() {
47 | super.reload()
48 | emptyView.isHidden = viewModel.itemsCount > 0
49 | }
50 |
51 | // MARK: - Table View
52 |
53 | override func tableView(_ tableView: UITableView,
54 | cellForRowAt indexPath: IndexPath) -> UITableViewCell {
55 |
56 | let cell = tableView.dequeueReusableCell(
57 | withIdentifier: AsciiListCell.identifier) as! AsciiListCell
58 |
59 | let item = viewModel.item(at: indexPath)
60 | cell.configure(with: item)
61 |
62 | return cell
63 | }
64 |
65 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
66 | tableView.deselectRow(at: indexPath, animated: true)
67 |
68 | performSegue(
69 | withIdentifier: MainStoryboard.Segue.toUsingList,
70 | sender: viewModel.item(at: indexPath))
71 | }
72 |
73 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
74 | return AsciiListCell.cellHeight
75 | }
76 |
77 | // MARK: - Segue
78 |
79 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
80 | if segue.identifier == MainStoryboard.Segue.toUsingList {
81 | let dest = segue.destination as! UINavigationController
82 | let vc = dest.viewControllers.first! as! UsingListViewController
83 | vc.passListViewModel = sender as! EmojiListViewModel
84 | }
85 | }
86 |
87 | // MARK: - Button Actions
88 |
89 | @IBAction func actionNewList(_ sender: UIView) {
90 | performSegue(withIdentifier: MainStoryboard.Segue.toCreate, sender: nil)
91 | }
92 |
93 | @IBAction func actionStore(_ sender: UIView) {
94 | successAlert(message: "Open Store!")
95 | }
96 |
97 | @IBAction func actionSettings(_ sender: Any) {
98 | performSegue(withIdentifier: MainStoryboard.Segue.toSettings, sender: nil)
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/mobile/localized/ru.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "да";
12 | "No" = "нет";
13 | "Warning" = "Предупреждение!";
14 | "Success" = "Успех!";
15 | "Dismiss" = "отклонять";
16 | "Cancel" = "Отмена";
17 | "Shared" = "Общий!";
18 |
19 | // Errors
20 | "Email.Error" = "К сожалению, ваше устройство не настроено на отправку писем. Пожалуйста, отправьте сообщение на адрес buh@ghostship.co, чтобы связаться с нами.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "Эможи!";
24 | "Pack.AllEmojis" = "Все эмоции!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "новый";
29 | "Lists.Settings" = "настройки";
30 | "Lists.Empty" = "Пустое!";
31 | "Lists.Empty.Msg" = "Вы сможете сэкономить гораздо больше времени, создав визуальный список. Используйте эти списки для своих продуктов, покупок и многое другое!";
32 |
33 | // Using List
34 | "UsingList.Done" = "Готово!";
35 | "UsingList.Settings.Title" = "Настройки списка";
36 | "UsingList.Settings.Msg" = "Вы можете удалить этот список с вашего устройства или поделиться им с друзьями по тексту или поделиться им в своих социальных сетях.";
37 | "UsingList.Settings.Redo" = "Перезагрузите этот список.";
38 | "UsingList.Settings.DeleteList" = "Удалить этот список";
39 | "UsingList.Delete.Msg" = "На всякий случай, пожалуйста, подтвердите, что хотите удалить этот список. Это действие не может быть отменено.";
40 | "UsingList.Settings.DeleteListConfirmation" = "Удалить список";
41 | "UsingList.Settings.Edit" = "Редактировать список";
42 |
43 | // Share
44 | "Share.Credits1" = "Создано с Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "Новый список";
49 | "CreateList.Text.Placeholder" = "бакалейные товары";
50 | "CreateList.Label" = "Как вы хотите это назвать?";
51 | "CreateList.Hint" = "бакалея, супермаркет или магазины могут быть быстро созданы визуально. На следующем шаге вы выберете элементы emoji. Повеселись!";
52 | "CreateList.Next" = "следующий";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "Что входит в список?";
56 | "SelectEmojis.Create" = "Создайте";
57 | "SelectEmojis.Update" = "Обновить";
58 | "SelectEmojis.Clear" = "Очистить";
59 | "SelectEmojis.SelectPack" = "Выбранный пакет: ";
60 | "SelectEmojis.ItemsInList" = "Нажмите один, чтобы удалить его.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "пакеты";
64 |
65 | // About
66 | "About.Title" = "настройки";
67 | "About.Settings" = "Настройте свое приложение!";
68 | "About.About" = "Около";
69 | "About.Promo" = "Поделиться";
70 | "About.Follow" = "следить";
71 | "About.MoreApps" = "Больше приложений!";
72 | "About.Settings.DefaultPack" = "Пакет по умолчанию: ";
73 | "About.Settings.Theme" = "Используемая тема: ";
74 | "About.Promo.Signup" = "Зарегистрируйтесь в нашем списке!";
75 | "About.Promo.Share" = "Поделиться с друзьями";
76 | "About.Promo.Rate" = "Оценить приложение ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Посетите блог";
81 | "About.About.Contact" = "Связаться с разработчиком";
82 | "About.About.Feature" = "Отправить мнение";
83 | "About.About.Version" = "Версия приложения";
84 | "About.Share.Copy" = "Загрузите Mojilist, визуальную альтернативу для ваших ежедневных списков!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Темы";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 День";
91 | "Theme.Dark" = "🌚 Ночь";
92 | "Theme.Greeny" = "🍁 Зеленые листья";
93 | "Theme.Blue" = "🌊 Океанские волны";
94 | "Theme.Drag" = "💄 Это очаровательно";
95 | "Theme.Dracula" = "💥 Сладкие Мечты";
96 | "Theme.Candy" = "🍭 Сахарная лихорадка.";
97 |
--------------------------------------------------------------------------------
/mobile/localized/pt-BR.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "Sim";
12 | "No" = "Não";
13 | "Warning" = "Atenção!";
14 | "Success" = "Sucesso!";
15 | "Dismiss" = "Dispensar";
16 | "Cancel" = "Cancelar";
17 | "Shared" = "Compartilhamento pronto!";
18 |
19 | // Errors
20 | "Email.Error" = "Aparentemente seu aparelho não suporta envio de e-mail. Por favor, envie um e-mail para buh@ghostship.co para entrar em contato.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "Emojis de Coisas!";
24 | "Pack.AllEmojis" = "Todos os Emojis!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "Nova";
29 | "Lists.Settings" = "Ajustes";
30 | "Lists.Empty" = "Nenhuma lista!";
31 | "Lists.Empty.Msg" = "Você economiza muito mais tempo criando listas visuais. Use suas listas para compras da semana, material escolar, afazeres e mais!";
32 |
33 | // Using List
34 | "UsingList.Done" = "Pronto!";
35 | "UsingList.Settings.Title" = "Ajustes da lista";
36 | "UsingList.Settings.Msg" = "Você pode remover sua lista por completo do seu aparelho ou compartilhar uma imagem dela nas suas redes sociais.";
37 | "UsingList.Settings.Redo" = "Começar a lista do zero.";
38 | "UsingList.Settings.DeleteList" = "Deletar esta lista";
39 | "UsingList.Delete.Msg" = "Só para assegurar-se que você não tocou no botão de deletar sem querer, confirme a remoção da lista a seguir:";
40 | "UsingList.Settings.DeleteListConfirmation" = "Deletar a lista";
41 | "UsingList.Settings.Edit" = "Editar a lista";
42 |
43 | // Share
44 | "Share.Credits1" = "Criado com Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "Nova Lista";
49 | "CreateList.Text.Placeholder" = "Compras da semana";
50 | "CreateList.Label" = "Como vai se chamar a nova lista?";
51 | "CreateList.Hint" = "Dica: listas de afazeres, supermercado ou material escolar podem ser criadas rapidamente visualmente. Selecione emojis na próxima etapa e divirta-se!";
52 | "CreateList.Next" = "Seguinte";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "O que vai na lista?";
56 | "SelectEmojis.Create" = "Criar";
57 | "SelectEmojis.Update" = "Atualizar";
58 | "SelectEmojis.Clear" = "Esvaziar";
59 | "SelectEmojis.SelectPack" = "Pacote selecionado:";
60 | "SelectEmojis.ItemsInList" = "Itens na lista. Se precisar, toque uma vez para remover.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "Pacotes";
64 |
65 | // About
66 | "About.Title" = "Ajustes";
67 | "About.Settings" = "Personalize seu App!";
68 | "About.About" = "Sobre";
69 | "About.Promo" = "Compartilhe";
70 | "About.Follow" = "Siga nas redes sociais";
71 | "About.MoreApps" = "Mais apps!";
72 | "About.Settings.DefaultPack" = "Pacote padrão: ";
73 | "About.Settings.Theme" = "Tema usado: ";
74 | "About.Promo.Signup" = "Inscreva-se na Newsletter!";
75 | "About.Promo.Share" = "Compartilhe este app";
76 | "About.Promo.Rate" = "Avalie este app ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Visite o Blog";
81 | "About.About.Contact" = "E-mail para o desenvolvedor";
82 | "About.About.Feature" = "Opine sobre o app";
83 | "About.About.Version" = "Versão do app";
84 | "About.Share.Copy" = "Baixe o Mojilist, uma alternativa visual para suas listas do dia-a-dia!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Temas";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 Mojilist Diurno";
91 | "Theme.Dark" = "🌚 Mojilist Noturno";
92 | "Theme.Greeny" = "🍁 Maresia";
93 | "Theme.Blue" = "🌊 Deu Onda";
94 | "Theme.Drag" = "💄 Que Tiro Foi Esse?";
95 | "Theme.Dracula" = "💥 Sweet Dreams";
96 | "Theme.Candy" = "🍭 Agora Eu Fiquei Doce";
97 |
--------------------------------------------------------------------------------
/mobile/localized/pt-PT.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "Sim";
12 | "No" = "Não";
13 | "Warning" = "Atenção!";
14 | "Success" = "Sucesso!";
15 | "Dismiss" = "Dispensar";
16 | "Cancel" = "Cancelar";
17 | "Shared" = "Compartilhamento pronto!";
18 |
19 | // Errors
20 | "Email.Error" = "Aparentemente seu aparelho não suporta envio de e-mail. Por favor, envie um e-mail para buh@ghostship.co para entrar em contato.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "Emojis de Coisas!";
24 | "Pack.AllEmojis" = "Todos os Emojis!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "Nova";
29 | "Lists.Settings" = "Ajustes";
30 | "Lists.Empty" = "Nenhuma lista!";
31 | "Lists.Empty.Msg" = "Você economiza muito mais tempo criando listas visuais. Use suas listas para compras da semana, material escolar, afazeres e mais!";
32 |
33 | // Using List
34 | "UsingList.Done" = "Pronto!";
35 | "UsingList.Settings.Title" = "Ajustes da lista";
36 | "UsingList.Settings.Msg" = "Você pode remover sua lista por completo do seu aparelho ou compartilhar uma imagem dela nas suas redes sociais.";
37 | "UsingList.Settings.Redo" = "Começar a lista do zero.";
38 | "UsingList.Settings.DeleteList" = "Deletar esta lista";
39 | "UsingList.Delete.Msg" = "Só para assegurar-se que você não tocou no botão de deletar sem querer, confirme a remoção da lista a seguir:";
40 | "UsingList.Settings.DeleteListConfirmation" = "Deletar a lista";
41 | "UsingList.Settings.Edit" = "Editar a lista";
42 |
43 | // Share
44 | "Share.Credits1" = "Criado com Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "Nova Lista";
49 | "CreateList.Text.Placeholder" = "Compras da semana";
50 | "CreateList.Label" = "Como vai se chamar a nova lista?";
51 | "CreateList.Hint" = "Dica: listas de afazeres, supermercado ou material escolar podem ser criadas rapidamente visualmente. Selecione emojis na próxima etapa e divirta-se!";
52 | "CreateList.Next" = "Seguinte";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "O que vai na lista?";
56 | "SelectEmojis.Create" = "Criar";
57 | "SelectEmojis.Update" = "Atualizar";
58 | "SelectEmojis.Clear" = "Esvaziar";
59 | "SelectEmojis.SelectPack" = "Pacote selecionado:";
60 | "SelectEmojis.ItemsInList" = "Itens na lista. Se precisar, toque uma vez para remover.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "Pacotes";
64 |
65 | // About
66 | "About.Title" = "Ajustes";
67 | "About.Settings" = "Personalize seu App!";
68 | "About.About" = "Sobre";
69 | "About.Promo" = "Compartilhe";
70 | "About.Follow" = "Siga nas redes sociais";
71 | "About.MoreApps" = "Mais apps!";
72 | "About.Settings.DefaultPack" = "Pacote padrão: ";
73 | "About.Settings.Theme" = "Tema usado: ";
74 | "About.Promo.Signup" = "Inscreva-se na Newsletter!";
75 | "About.Promo.Share" = "Compartilhe este app";
76 | "About.Promo.Rate" = "Avalie este app ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Visite o Blog";
81 | "About.About.Contact" = "E-mail para o desenvolvedor";
82 | "About.About.Feature" = "Opine sobre o app";
83 | "About.About.Version" = "Versão do app";
84 | "About.Share.Copy" = "Baixe o Mojilist, uma alternativa visual para suas listas do dia-a-dia!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Temas";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 Mojilist Diurno";
91 | "Theme.Dark" = "🌚 Mojilist Noturno";
92 | "Theme.Greeny" = "🍁 Maresia";
93 | "Theme.Blue" = "🌊 Deu Onda";
94 | "Theme.Drag" = "💄 Que Tiro Foi Esse?";
95 | "Theme.Dracula" = "💥 Sweet Dreams";
96 | "Theme.Candy" = "🍭 Agora Eu Fiquei Doce";
97 |
--------------------------------------------------------------------------------
/mobile/localized/es.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "Sí";
12 | "No" = "No";
13 | "Warning" = "¡Atención!";
14 | "Success" = "¡Éxito!";
15 | "Dismiss" = "Descartar";
16 | "Cancel" = "Cancelar";
17 | "Shared" = "Compartir listo!";
18 |
19 | // Errors
20 | "Email.Error" = "Aparentemente su aparato no soporta el envío de correo electrónico. Por favor, envíe un correo electrónico a buh@ghostship.co para ponerse en contacto.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "¡Emojis de cosas!";
24 | "Pack.AllEmojis" = "¡Todos los Emojis!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "Nueva";
29 | "Lists.Settings" = "Configuraciones";
30 | "Lists.Empty" = "¡Sin listas!";
31 | "Lists.Empty.Msg" = "Usted ahorra mucho más tiempo creando listas visuales. ¡Utilice sus listas para las compras de la semana, tareas y más!";
32 |
33 | // Using List
34 | "UsingList.Done" = "¡Listo!";
35 | "UsingList.Settings.Title" = "Configuraciones";
36 | "UsingList.Settings.Msg" = "Usted puede quitar su lista por completo de su dispositivo o compartir una imagen de ella en sus redes sociales.";
37 | "UsingList.Settings.Redo" = "Comenzar la lista de cero.";
38 | "UsingList.Settings.DeleteList" = "Borrar esta lista";
39 | "UsingList.Delete.Msg" = "Solo para estar seguro, confirme nuevamente que desea eliminar esta lista.";
40 | "UsingList.Settings.DeleteListConfirmation" = "Borrar lista";
41 | "UsingList.Settings.Edit" = "Editar la lista";
42 |
43 | // Share
44 | "Share.Credits1" = "Creado con Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "Nueva Lista";
49 | "CreateList.Text.Placeholder" = "Lista de compras";
50 | "CreateList.Label" = "¿Cómo se llamará la nueva lista?";
51 | "CreateList.Hint" = "Sugerencia: listas de tareas, supermercados o material escolar se pueden crear rápidamente visualmente. Selecciona emojis en el siguiente paso y diviértete!";
52 | "CreateList.Next" = "Siguiente";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "¿Qué va en la lista?";
56 | "SelectEmojis.Create" = "Crear";
57 | "SelectEmojis.Update" = "Actualizar";
58 | "SelectEmojis.Clear" = "Vaciar";
59 | "SelectEmojis.SelectPack" = "Paquete seleccionado:";
60 | "SelectEmojis.ItemsInList" = "Elementos en la lista. Si lo necesita, toque una vez para quitar.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "Paquetes";
64 |
65 | // About
66 | "About.Title" = "Configuraciones";
67 | "About.Settings" = "¡Personalice su App!";
68 | "About.About" = "Acerca";
69 | "About.Promo" = "Comparte";
70 | "About.Follow" = "Sigue en las redes sociales";
71 | "About.MoreApps" = "¡Más apps!";
72 | "About.Settings.DefaultPack" = "Paquete estándar: ";
73 | "About.Settings.Theme" = "Tema usado: ";
74 | "About.Promo.Signup" = "Suscríbete a nuestro boletín!";
75 | "About.Promo.Share" = "Compartir esta app";
76 | "About.Promo.Rate" = "Evaluar esta app ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Visita el Blog";
81 | "About.About.Contact" = "E-mail para el desarrollador";
82 | "About.About.Feature" = "Opina sobre la aplicación";
83 | "About.About.Version" = "Versión de la app";
84 | "About.Share.Copy" = "¡Descargue o Mojilist, una alternativa visual para tus listas del día a día!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Temas";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 Mojilist Día";
91 | "Theme.Dark" = "🌚 Mojilist Noche";
92 | "Theme.Greeny" = "🍁 Hojas verdes";
93 | "Theme.Blue" = "🌊 Ola Ola";
94 | "Theme.Drag" = "💄 Encantador";
95 | "Theme.Dracula" = "💥 Dulces sueños";
96 | "Theme.Candy" = "🍭 Pico de azúcar";
97 |
--------------------------------------------------------------------------------
/mobile/localized/es-419.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "Sí";
12 | "No" = "No";
13 | "Warning" = "¡Atención!";
14 | "Success" = "¡Éxito!";
15 | "Dismiss" = "Descartar";
16 | "Cancel" = "Cancelar";
17 | "Shared" = "Compartir listo!";
18 |
19 | // Errors
20 | "Email.Error" = "Aparentemente su aparato no soporta el envío de correo electrónico. Por favor, envíe un correo electrónico a buh@ghostship.co para ponerse en contacto.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "¡Emojis de cosas!";
24 | "Pack.AllEmojis" = "¡Todos los Emojis!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "Nueva";
29 | "Lists.Settings" = "Configuraciones";
30 | "Lists.Empty" = "¡Sin listas!";
31 | "Lists.Empty.Msg" = "Usted ahorra mucho más tiempo creando listas visuales. ¡Utilice sus listas para las compras de la semana, tareas y más!";
32 |
33 | // Using List
34 | "UsingList.Done" = "¡Listo!";
35 | "UsingList.Settings.Title" = "Configuraciones";
36 | "UsingList.Settings.Msg" = "Usted puede quitar su lista por completo de su dispositivo o compartir una imagen de ella en sus redes sociales.";
37 | "UsingList.Settings.Redo" = "Comenzar la lista de cero.";
38 | "UsingList.Settings.DeleteList" = "Borrar esta lista";
39 | "UsingList.Delete.Msg" = "Solo para estar seguro, confirme nuevamente que desea eliminar esta lista.";
40 | "UsingList.Settings.DeleteListConfirmation" = "Borrar lista";
41 | "UsingList.Settings.Edit" = "Editar la lista";
42 |
43 | // Share
44 | "Share.Credits1" = "Creado con Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "Nueva Lista";
49 | "CreateList.Text.Placeholder" = "Lista de compras";
50 | "CreateList.Label" = "¿Cómo se llamará la nueva lista?";
51 | "CreateList.Hint" = "Sugerencia: listas de tareas, supermercados o material escolar se pueden crear rápidamente visualmente. Selecciona emojis en el siguiente paso y diviértete!";
52 | "CreateList.Next" = "Siguiente";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "¿Qué va en la lista?";
56 | "SelectEmojis.Create" = "Crear";
57 | "SelectEmojis.Update" = "Actualizar";
58 | "SelectEmojis.Clear" = "Vaciar";
59 | "SelectEmojis.SelectPack" = "Paquete seleccionado:";
60 | "SelectEmojis.ItemsInList" = "Elementos en la lista. Si lo necesita, toque una vez para quitar.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "Paquetes";
64 |
65 | // About
66 | "About.Title" = "Configuraciones";
67 | "About.Settings" = "¡Personalice su App!";
68 | "About.About" = "Acerca";
69 | "About.Promo" = "Comparte";
70 | "About.Follow" = "Sigue en las redes sociales";
71 | "About.MoreApps" = "¡Más apps!";
72 | "About.Settings.DefaultPack" = "Paquete estándar: ";
73 | "About.Settings.Theme" = "Tema usado: ";
74 | "About.Promo.Signup" = "Suscríbete a nuestro boletín!";
75 | "About.Promo.Share" = "Compartir esta app";
76 | "About.Promo.Rate" = "Evaluar esta app ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Visita el Blog";
81 | "About.About.Contact" = "E-mail para el desarrollador";
82 | "About.About.Feature" = "Opina sobre la aplicación";
83 | "About.About.Version" = "Versión de la app";
84 | "About.Share.Copy" = "¡Descargue o Mojilist, una alternativa visual para tus listas del día a día!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Temas";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 Mojilist Día";
91 | "Theme.Dark" = "🌚 Mojilist Noche";
92 | "Theme.Greeny" = "🍁 Hojas verdes";
93 | "Theme.Blue" = "🌊 Ola Ola";
94 | "Theme.Drag" = "💄 Encantador";
95 | "Theme.Dracula" = "💥 Dulces sueños";
96 | "Theme.Candy" = "🍭 Pico de azúcar";
97 |
98 |
--------------------------------------------------------------------------------
/mobile/localized/fr.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Emojilist
4 |
5 | Created by Thiago Ricieri on 10/01/2018.
6 | Copyright © 2018 Ghost Ship. All rights reserved.
7 | */
8 |
9 | // Base
10 | "OK" = "OK";
11 | "Yes" = "Oui";
12 | "No" = "Non";
13 | "Warning" = "Attention!";
14 | "Success" = "Succès!";
15 | "Dismiss" = "Rejeter";
16 | "Cancel" = "Annuler";
17 | "Shared" = "Partagé!";
18 |
19 | // Errors
20 | "Email.Error" = "Votre appareil ne peut pas envoi d'e-mails. S'il vous plaît, envoyez un courriel à buh@ghostship.co pour entrer en contact.";
21 |
22 | // Pack
23 | "Pack.EmojiThings" = "Emojis des Choses!";
24 | "Pack.AllEmojis" = "Tous les Emojis!";
25 |
26 | // Lists
27 | "Lists.Title" = "MOJILIST";
28 | "Lists.New" = "Nouvelle";
29 | "Lists.Settings" = "Paramètres";
30 | "Lists.Empty" = "Pas de liste!";
31 | "Lists.Empty.Msg" = "Vous économisez beaucoup plus de temps en créant des listes visuelles. Utilisez vos listes de courses, les affaires scolaires et plus encore!";
32 |
33 | // Using List
34 | "UsingList.Done" = "Terminé!";
35 | "UsingList.Settings.Title" = "Paramètres de liste";
36 | "UsingList.Settings.Msg" = "Vous pouvez supprimer votre liste de votre appareil ou partager une photo de celui-ci sur vos réseaux sociaux.";
37 | "UsingList.Settings.Redo" = "Démarrer la liste.";
38 | "UsingList.Settings.DeleteList" = "Supprimer la liste.";
39 | "UsingList.Delete.Msg" = "Confirmez la suppression de la liste suivante:";
40 | "UsingList.Settings.DeleteListConfirmation" = "Supprimer la liste";
41 | "UsingList.Settings.Edit" = "Modifier la liste";
42 |
43 | // Share
44 | "Share.Credits1" = "Créé avec Mojilist";
45 | "Share.Credits2" = "https://ghostship.co/mojilist";
46 |
47 | // Create List
48 | "CreateList.Title" = "Nouvelle Liste";
49 | "CreateList.Text.Placeholder" = "Les Courses";
50 | "CreateList.Label" = "Quelle sera la nouvelle liste appelée?";
51 | "CreateList.Hint" = "Les listes de choses à faire, les épiceries ou les affaires scolaires peuvent être créées rapidement et visuellement. Sélectionnez emojis dans la prochaine étape et profitez-en!";
52 | "CreateList.Next" = "Prochain";
53 |
54 | // Select emojis
55 | "SelectEmojis.Title" = "Qu'est-ce qui est sur la liste?";
56 | "SelectEmojis.Create" = "Créer";
57 | "SelectEmojis.Update" = "Mettre à jour";
58 | "SelectEmojis.Clear" = "Vider";
59 | "SelectEmojis.SelectPack" = "Package sélectionné:";
60 | "SelectEmojis.ItemsInList" = "Articles dans la liste. Si nécessaire, appuyez une fois pour supprimer.";
61 |
62 | // Select Pack
63 | "SelectPack.Title" = "Packages";
64 |
65 | // About
66 | "About.Title" = "Paramètres";
67 | "About.Settings" = "Personnalisez votre application!";
68 | "About.About" = "Environ";
69 | "About.Promo" = "Partager";
70 | "About.Follow" = "Suivez sur les réseaux sociaux";
71 | "About.MoreApps" = "Plus des apps!";
72 | "About.Settings.DefaultPack" = "Package sélectionné: ";
73 | "About.Settings.Theme" = "Thème utilisé: ";
74 | "About.Promo.Signup" = "Abonnez-vous à la newsletter!";
75 | "About.Promo.Share" = "Partagez cette application";
76 | "About.Promo.Rate" = "Noter cette application ❤️";
77 | "About.Follow.Instagram" = "Instagram";
78 | "About.Follow.Facebook" = "Facebook";
79 | "About.Follow.Twitter" = "Twitter";
80 | "About.Follow.Blog" = "Visitez le blog";
81 | "About.About.Contact" = "Contacter le développeur";
82 | "About.About.Feature" = "Donnez votre avis";
83 | "About.About.Version" = "Version de l'application";
84 | "About.Share.Copy" = "Téléchargez Mojilist, une alternative visuelle à vos listes des cours!";
85 |
86 | // Change Theme
87 | "ChangeTheme.Title" = "Thème";
88 |
89 | // Themes
90 | "Theme.Basic" = "🌞 Mojilist du Jour";
91 | "Theme.Dark" = "🌚 Mojilist de la Nuit";
92 | "Theme.Greeny" = "🍁 Feuilles";
93 | "Theme.Blue" = "🌊 Vagues de la mer";
94 | "Theme.Drag" = "💄 La mode est choquante";
95 | "Theme.Dracula" = "💥 Beaux Rêves";
96 | "Theme.Candy" = "🍭 Plus de sucre, svp!";
97 |
--------------------------------------------------------------------------------
/mobile/resources/assets.xcassets/Mojilist.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "57x57",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-57x57@1x.png",
49 | "scale" : "1x"
50 | },
51 | {
52 | "size" : "57x57",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-57x57@2x.png",
55 | "scale" : "2x"
56 | },
57 | {
58 | "size" : "60x60",
59 | "idiom" : "iphone",
60 | "filename" : "Icon-App-60x60@2x.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "60x60",
65 | "idiom" : "iphone",
66 | "filename" : "Icon-App-60x60@3x.png",
67 | "scale" : "3x"
68 | },
69 | {
70 | "size" : "20x20",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-20x20@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "20x20",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-20x20@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "29x29",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-29x29@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "29x29",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-29x29@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "40x40",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-40x40@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "40x40",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-40x40@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "50x50",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-Small-50x50@1x.png",
109 | "scale" : "1x"
110 | },
111 | {
112 | "size" : "50x50",
113 | "idiom" : "ipad",
114 | "filename" : "Icon-Small-50x50@2x.png",
115 | "scale" : "2x"
116 | },
117 | {
118 | "size" : "72x72",
119 | "idiom" : "ipad",
120 | "filename" : "Icon-App-72x72@1x.png",
121 | "scale" : "1x"
122 | },
123 | {
124 | "size" : "72x72",
125 | "idiom" : "ipad",
126 | "filename" : "Icon-App-72x72@2x.png",
127 | "scale" : "2x"
128 | },
129 | {
130 | "size" : "76x76",
131 | "idiom" : "ipad",
132 | "filename" : "Icon-App-76x76@1x.png",
133 | "scale" : "1x"
134 | },
135 | {
136 | "size" : "76x76",
137 | "idiom" : "ipad",
138 | "filename" : "Icon-App-76x76@2x.png",
139 | "scale" : "2x"
140 | },
141 | {
142 | "size" : "83.5x83.5",
143 | "idiom" : "ipad",
144 | "filename" : "Icon-App-83.5x83.5@2x.png",
145 | "scale" : "2x"
146 | },
147 | {
148 | "size" : "1024x1024",
149 | "idiom" : "ios-marketing",
150 | "filename" : "ItunesArtwork@2x.png",
151 | "scale" : "1x"
152 | }
153 | ],
154 | "info" : {
155 | "version" : 1,
156 | "author" : "xcode"
157 | }
158 | }
--------------------------------------------------------------------------------
/mobile/views/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 |
55 |
56 |
--------------------------------------------------------------------------------
/mobile/core/Emojis.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Emojis.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 14/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | func thingsEmoji() -> [Character] {
12 | return "🍏🍎🍐🍊🍋🍌🍉🍇🍓🍈🍒🍑🍍🥥🥝🍅🍆🥑🥦🥒🌶🌽🥕🥔🍠🥐🍞🥖🥨🧀🥚🥞🥓🥩🍗🍖🌭🍔🍟🍕🥪🥙🌮🌯🥗🥘🥫🍝🍜🍲🍛🍣🍱🥟🍤🍙🍚🍘🍥🥠🍢🍡🍧🍨🍦🥧🍰🎂🍮🍭🍬🍫🍿🍩🍪🌰🥜🍯🥛☕️🍵🥤🍶🍺🍷🥃🍸🍹🥂🍾🍴🥣💧☂️🔥🎄🌲🌹🌻🌸🍄🍁🦀🐠🦑🐙🐟🐌👔👖👚👕👢👗👙🧦🧤🧣🎩🧢🎒👛🌂👞👟👡👠👓🕶💄👀👅⚽️🏀🏈⚾️🎾🏐🏉🎱🏓🏸🏒🏑🏏🎣🥊⛸🛷🎿🎫🎟🎭🎤🎧🎹🥁🎷🎺🎸🎻🎲🎯🎳🎮🛴🛵🚲🚗🚕🚙🏍✈️🗺⛱🏝⌚️📱💻⌨️🖥🖱🖨🕹📷📹📞☎️⏰🔦🕯🗑💵💶💴💷💎🔧🔨🔪🔩⚙️💊🛍🎁🖼🎈✉️📁🗞📔📎📌✂️🖊🖌✏️🖍".map { return $0 }
13 | }
14 |
15 | func allEmojis() -> [Character] {
16 | return "😀😃😄😁😆😅😂🤣☺️😊😇🙂🙃😉😌😍😘😗😙😚😋😜😝😛🤑🤗🤓😎🤡🤠😏😒😞😔😟😕🙁☹️😣😖😫😩😤😠😡😶😐😑😯😦😧😮😲😵😳😱😨😰😢😥🤤😭😓😪😴🙄🤔🤥😬🤐🤢🤧😷🤒🤕😈👿👹👺💩👻💀☠️👽👾🤖🎃😺😸😹😻😼😽🙀😿😾👐🙌👏🙏🤝👍👎👊✊🤛🤜🤞✌️🤘👌👈👉👆👇☝️✋🤚🖐🖖👋🤙💪🖕✍️🤳💅💍💄💋👄👅👂👃👣👁👀🗣👤👥👶👦👧👨👩👱♀️👱👴👵👲👳♀️👳👮♀️👮👷♀️👷💂♀️💂🕵️♀️🕵️👩⚕️👨⚕️👩🌾👨🌾👩🍳👨🍳👩🎓👨🎓👩🎤👨🎤👩🏫👨🏫👩🏭👨🏭👩💻👨💻👩💼👨💼👩🔧👨🔧👩🔬👨🔬👩🎨👨🎨👩🚒👨🚒👩✈️👨✈️👩🚀👨🚀👩⚖️👨⚖️🤶🎅👸🤴👰🤵👼🤰🙇♀️🙇💁💁♂️🙅🙅♂️🙆🙆♂️🙋🙋♂️🤦♀️🤦♂️🤷♀️🤷♂️🙎🙎♂️🙍🙍♂️💇💇♂️💆💆♂️🕴💃🕺👯👯♂️🚶♀️🚶🏃♀️🏃👫👭👬💑👩❤️👩👨❤️👨💏👩❤️💋👩👨❤️💋👨👪👨👩👧👨👩👧👦👨👩👦👦👨👩👧👧👩👩👦👩👩👧👩👩👧👦👩👩👦👦👩👩👧👧👨👨👦👨👨👧👨👨👧👦👨👨👦👦👨👨👧👧👩👦👩👧👩👧👦👩👦👦👩👧👧👨👦👨👧👨👧👦👨👦👦👨👧👧👚👕👖👔👗👙👘👠👡👢👞👟👒🎩🎓👑⛑🎒👝👛👜💼👓🕶🌂☂️🐶🐱🐭🐹🐰🦊🐻🐼🐨🐯🦁🐮🐷🐽🐸🐵🙈🙉🙊🐒🐔🐧🐦🐤🐣🐥🦆🦅🦉🦇🐺🐗🐴🦄🐝🐛🦋🐌🐚🐞🐜🕷🕸🐢🐍🦎🦂🦀🦑🐙🦐🐠🐟🐡🐬🦈🐳🐋🐊🐆🐅🐃🐂🐄🦌🐪🐫🐘🦏🦍🐎🐖🐐🐏🐑🐕🐩🐈🐓🦃🕊🐇🐁🐀🐿🐾🐉🐲🌵🎄🌲🌳🌴🌱🌿☘️🍀🎍🎋🍃🍂🍁🍄🌾💐🌷🌹🥀🌻🌼🌸🌺🌎🌍🌏🌕🌖🌗🌘🌑🌒🌓🌔🌚🌝🌞🌛🌜🌙💫⭐️🌟✨⚡️🔥💥☄️☀️🌤⛅️🌥🌦🌈☁️🌧⛈🌩🌨☃️⛄️❄️🌬💨🌪🌫🌊💧💦☔️🍏🍎🍐🍊🍋🍌🍉🍇🍓🍈🍒🍑🍍🥝🥑🍅🍆🥒🥕🌽🌶🥔🍠🌰🥜🍯🥐🍞🥖🧀🥚🍳🥓🥞🍤🍗🍖🍕🌭🍔🍟🥙🌮🌯🥗🥘🍝🍜🍲🍥🍣🍱🍛🍚🍙🍘🍢🍡🍧🍨🍦🍰🎂🍮🍭🍬🍫🍿🍩🍪🥛🍼☕️🍵🍶🍺🍻🥂🍷🥃🍸🍹🍾🥄🍴🍽⚽️🏀🏈⚾️🎾🏐🏉🎱🏓🏸🥅🏒🏑🏏⛳️🏹🎣🥊🥋⛸🎿⛷🏂🏋️♀️🏋️🤺🤼♀️🤼♂️🤸♀️🤸♂️⛹️♀️⛹️🤾♀️🤾♂️🏌️♀️🏌️🏄♀️🏄🏊♀️🏊🤽♀️🤽♂️🚣♀️🚣🏇🚴♀️🚴🚵♀️🚵🎽🏅🎖🥇🥈🥉🏆🏵🎗🎫🎟🎪🤹♀️🤹♂️🎭🎨🎬🎤🎧🎼🎹🥁🎷🎺🎸🎻🎲🎯🎳🎮🎰🚗🚕🚙🚌🚎🏎🚓🚑🚒🚐🚚🚛🚜🛴🚲🛵🏍🚨🚔🚍🚘🚖🚡🚠🚟🚃🚋🚞🚝🚄🚅🚈🚂🚆🚇🚊🚉🚁🛩✈️🛫🛬🚀🛰💺🛶⛵️🛥🚤🛳⛴🚢⚓️🚧⛽️🚏🚦🚥🗺🗿🗽⛲️🗼🏰🏯🏟🎡🎢🎠⛱🏖🏝⛰🏔🗻🌋🏜🏕⛺️🛤🛣🏗🏭🏠🏡🏘🏚🏢🏬🏣🏤🏥🏦🏨🏪🏫🏩💒🏛⛪️🕌🕍🕋⛩🗾🎑🏞🌅🌄🌠🎇🎆🌇🌆🏙🌃🌌🌉🌁⌚️📱📲💻⌨️🖥🖨🖱🖲🕹🗜💽💾💿📀📼📷📸📹🎥📽🎞📞☎️📟📠📺📻🎙🎚🎛⏱⏲⏰🕰⌛️⏳📡🔋🔌💡🔦🕯🗑🛢💸💵💴💶💷💰💳💎⚖️🔧🔨⚒🛠⛏🔩⚙️⛓🔫💣🔪🗡⚔️🛡🚬⚰️⚱️🏺🔮📿💈⚗️🔭🔬🕳💊💉🌡🚽🚰🚿🛁🛀🛎🔑🗝🚪🛋🛏🛌🖼🛍🛒🎁🎈🎏🎀🎊🎉🎎🏮🎐✉️📩📨📧💌📥📤📦🏷📪📫📬📭📮📯📜📃📄📑📊📈📉🗒🗓📆📅📇🗃🗳🗄📋📁📂🗂🗞📰📓📔📒📕📗📘📙📚📖🔖🔗📎🖇📐📏📌📍✂️🖊🖋✒️🖌🖍📝✏️🔍🔎🔏🔐🔒🔓❤️💛💚💙💜🖤💔❣️💕💞💓💗💖💘💝💟☮️✝️☪️🕉☸️✡️🔯🕎☯️☦️🛐⛎♈️♉️♊️♋️♌️♍️♎️♏️♐️♑️♒️♓️🆔⚛️🉑☢️☣️📴📳🈶🈚️🈸🈺🈷️✴️🆚💮🉐㊙️㊗️🈴🈵🈹🈲🅰️🅱️🆎🆑🅾️🆘❌⭕️🛑⛔️📛🚫💯💢♨️🚷🚯🚳🚱🔞📵🚭❗️❕❓❔‼️⁉️🔅🔆〽️⚠️🚸🔱⚜️🔰♻️✅🈯️💹❇️✳️❎🌐💠Ⓜ️🌀💤🏧🚾♿️🅿️🈳🈂️🛂🛃🛄🛅🚹🚺🚼🚻🚮🎦📶🈁🔣ℹ️🔤🔡🔠🆖🆗🆙🆒🆕🆓0️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣🔟🔢#️⃣*️⃣▶️⏸⏯⏹⏺⏭⏮⏩⏪⏫⏬◀️🔼🔽➡️⬅️⬆️⬇️↗️↘️↙️↖️↕️↔️↪️↩️⤴️⤵️🔀🔁🔂🔄🔃🎵🎶➕➖➗✖️💲💱™️©️®️〰️➰➿🔚🔙🔛🔝🔜✔️☑️🔘⚪️⚫️🔴🔵🔺🔻🔸🔹🔶🔷🔳🔲▪️▫️◾️◽️◼️◻️⬛️⬜️🔈🔇🔉🔊🔔🔕📣📢👁🗨💬💭🗯♠️♣️♥️♦️🃏🎴🀄️🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚🕛🕜🕝🕞🕟🕠🕡🕢🕣🕤🕥🕦🕧🏳️🏴🏁🚩🏳️🌈🇦🇫🇦🇽🇦🇱🇩🇿🇦🇸🇦🇩🇦🇴🇦🇮🇦🇶🇦🇬🇦🇷🇦🇲🇦🇼🇦🇺🇦🇹🇦🇿🇧🇸🇧🇭🇧🇩🇧🇧🇧🇾🇧🇪🇧🇿🇧🇯🇧🇲🇧🇹🇧🇴🇧🇶🇧🇦🇧🇼🇧🇷🇮🇴🇻🇬🇧🇳🇧🇬🇧🇫🇧🇮🇨🇻🇰🇭🇨🇲🇨🇦🇮🇨🇰🇾🇨🇫🇹🇩🇨🇱🇨🇳🇨🇽🇨🇨🇨🇴🇰🇲🇨🇬🇨🇩🇨🇰🇨🇷🇨🇮🇭🇷🇨🇺🇨🇼🇨🇾🇨🇿🇩🇰🇩🇯🇩🇲🇩🇴🇪🇨🇪🇬🇸🇻🇬🇶🇪🇷🇪🇪🇪🇹🇪🇺🇫🇰🇫🇴🇫🇯🇫🇮🇫🇷🇬🇫🇵🇫🇹🇫🇬🇦🇬🇲🇬🇪🇩🇪🇬🇭🇬🇮🇬🇷🇬🇱🇬🇩🇬🇵🇬🇺🇬🇹🇬🇬🇬🇳🇬🇼🇬🇾🇭🇹🇭🇳🇭🇰🇭🇺🇮🇸🇮🇳🇮🇩🇮🇷🇮🇶🇮🇪🇮🇲🇮🇱🇮🇹🇯🇲🇯🇵🎌🇯🇪🇯🇴🇰🇿🇰🇪🇰🇮🇽🇰🇰🇼🇰🇬🇱🇦🇱🇻🇱🇧🇱🇸🇱🇷🇱🇾🇱🇮🇱🇹🇱🇺🇲🇴🇲🇰🇲🇬🇲🇼🇲🇾🇲🇻🇲🇱🇲🇹🇲🇭🇲🇶🇲🇷🇲🇺🇾🇹🇲🇽🇫🇲🇲🇩🇲🇨🇲🇳🇲🇪🇲🇸🇲🇦🇲🇿🇲🇲🇳🇦🇳🇷🇳🇵🇳🇱🇳🇨🇳🇿🇳🇮🇳🇪🇳🇬🇳🇺🇳🇫🇲🇵🇰🇵🇳🇴🇴🇲🇵🇰🇵🇼🇵🇸🇵🇦🇵🇬🇵🇾🇵🇪🇵🇭🇵🇳🇵🇱🇵🇹🇵🇷🇶🇦🇷🇪🇷🇴🇷🇺🇷🇼🇧🇱🇸🇭🇰🇳🇱🇨🇵🇲🇻🇨🇼🇸🇸🇲🇸🇹🇸🇦🇸🇳🇷🇸🇸🇨🇸🇱🇸🇬🇸🇽🇸🇰🇸🇮🇸🇧🇸🇴🇿🇦🇬🇸🇰🇷🇸🇸🇪🇸🇱🇰🇸🇩🇸🇷🇸🇿🇸🇪🇨🇭🇸🇾🇹🇼🇹🇯🇹🇿🇹🇭🇹🇱🇹🇬🇹🇰🇹🇴🇹🇹🇹🇳🇹🇷🇹🇲🇹🇨🇹🇻🇺🇬🇺🇦🇦🇪🇬🇧🇺🇸🇻🇮🇺🇾🇺🇿🇻🇺🇻🇦🇻🇪🇻🇳🇼🇫🇪🇭🇾🇪🇿🇲🇿🇼".map { $0 }
17 | }
18 |
--------------------------------------------------------------------------------
/mobile/controllers/common/BaseViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 10/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import MBProgressHUD
12 | import RealmSwift
13 |
14 | class BaseViewController : UIViewController {
15 |
16 | var baseViewModel: BaseViewModel!
17 | var currentTheme: String?
18 |
19 | fileprivate(set) var currentHud: MBProgressHUD?
20 |
21 | @IBOutlet weak var scrollView: UIScrollView?
22 |
23 | // MARK: - View Boilerplate
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 | instantiateDependencies()
28 | checkTheme()
29 | setViewStyle()
30 | }
31 |
32 | override func viewWillAppear(_ animated: Bool) {
33 | super.viewWillAppear(animated)
34 | checkTheme()
35 | prepareViewForUser()
36 | }
37 |
38 | // MARK: - Workflow
39 |
40 | func instantiateDependencies() {
41 | }
42 |
43 | func checkTheme() {
44 | let appTheme = baseViewModel.theme.identifier()
45 | if currentTheme == nil || currentTheme != appTheme {
46 | currentTheme = appTheme
47 | applyTheme(baseViewModel.theme)
48 | }
49 | }
50 |
51 | func applyTheme(_ theme: Theme) {
52 | theme.background(self.view)
53 | theme.tintAccent(self.view)
54 |
55 | if let nc = navigationController {
56 | theme.styleNavigationBar(nc.navigationBar)
57 | }
58 | setNeedsStatusBarAppearanceUpdate()
59 | }
60 |
61 | func setViewStyle() {
62 | }
63 |
64 | func prepareViewForUser() {
65 | }
66 |
67 | override var preferredStatusBarStyle: UIStatusBarStyle {
68 | return baseViewModel == nil ? .default : baseViewModel.theme.statusBarStyle()
69 | }
70 | }
71 |
72 | // MARK: - HUD
73 | extension BaseViewController {
74 |
75 | func hudShow (message: String? = nil) {
76 | currentHud = MBProgressHUD.showAdded(to: self.view, animated: true)
77 | currentHud?.mode = .indeterminate
78 |
79 | if message != nil {
80 | currentHud?.label.text = message!
81 | }
82 | }
83 |
84 | func hudDismiss() {
85 | if currentHud != nil {
86 | currentHud?.hide(animated: true)
87 | currentHud = nil
88 | }
89 | }
90 | }
91 |
92 | // MARK: - Impact Generator
93 | extension BaseViewController {
94 |
95 | func heavyImpact() {
96 | let generator = UIImpactFeedbackGenerator(style: .heavy)
97 | generator.impactOccurred()
98 | }
99 |
100 | func mediumImpact() {
101 | let generator = UIImpactFeedbackGenerator(style: .medium)
102 | generator.impactOccurred()
103 | }
104 |
105 | func lightImpact() {
106 | let generator = UIImpactFeedbackGenerator(style: .light)
107 | generator.impactOccurred()
108 | }
109 | }
110 |
111 | // MARK: - Observing Keyboard
112 | extension BaseViewController {
113 |
114 | func keyboardAppear(notification: Notification) {
115 | if let safeScroll = self.scrollView {
116 | var contentInset = UIEdgeInsets.zero
117 | if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
118 | contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
119 | }
120 | safeScroll.contentInset = contentInset
121 | }
122 | }
123 |
124 | func keyboardDisappear(notification: Notification) {
125 | if let safeScroll = self.scrollView {
126 | safeScroll.contentInset = UIEdgeInsets.zero
127 | }
128 | }
129 |
130 | func alertViewWithTitle(title: String, message: String) {
131 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
132 | alert.addAction(UIAlertAction(title: "OK".localized, style: .default, handler: nil))
133 | self.present(alert, animated: true, completion: nil)
134 | }
135 |
136 | func errorAlert(message: String) {
137 | alertViewWithTitle(title: "Warning".localized, message: message)
138 | }
139 |
140 | func successAlert(message: String) {
141 | alertViewWithTitle(title: "Success".localized, message: message)
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/fastlane/report.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/mobile/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Emojilist
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIcons
12 |
13 | CFBundlePrimaryIcon
14 |
15 | CFBundleIconFiles
16 |
17 | Mojilist
18 |
19 | UIPrerenderedIcon
20 |
21 |
22 | CFBundleAlternateIcons
23 |
24 | MojilistBlue
25 |
26 | CFBundleIconFiles
27 |
28 | MojilistBlue
29 |
30 | UIPrerenderedIcon
31 |
32 |
33 | MojilistCandy
34 |
35 | CFBundleIconFiles
36 |
37 | MojilistCandy
38 |
39 | UIPrerenderedIcon
40 |
41 |
42 | MojilistDark
43 |
44 | CFBundleIconFiles
45 |
46 | MojilistDark
47 |
48 | UIPrerenderedIcon
49 |
50 |
51 | MojilistDracula
52 |
53 | CFBundleIconFiles
54 |
55 | MojilistDracula
56 |
57 | UIPrerenderedIcon
58 |
59 |
60 | MojilistDrag
61 |
62 | CFBundleIconFiles
63 |
64 | MojilistDrag
65 |
66 | UIPrerenderedIcon
67 |
68 |
69 | MojilistGreeny
70 |
71 | CFBundleIconFiles
72 |
73 | MojilistGreeny
74 |
75 | UIPrerenderedIcon
76 |
77 |
78 |
79 |
80 | CFBundleIdentifier
81 | $(PRODUCT_BUNDLE_IDENTIFIER)
82 | CFBundleInfoDictionaryVersion
83 | 6.0
84 | CFBundleName
85 | $(PRODUCT_NAME)
86 | CFBundlePackageType
87 | APPL
88 | CFBundleShortVersionString
89 | 1.0
90 | CFBundleVersion
91 | 1
92 | LSRequiresIPhoneOS
93 |
94 | NSAppTransportSecurity
95 |
96 | NSAllowsArbitraryLoads
97 |
98 | NSAllowsArbitraryLoadsInWebContent
99 |
100 |
101 | NSPhotoLibraryAddUsageDescription
102 | We need to access your photos to save image of your list to share.
103 | NSPhotoLibraryUsageDescription
104 | We need to access your photos to save image of your list to share.
105 | UILaunchStoryboardName
106 | LaunchScreen
107 | UIMainStoryboardFile
108 | Main
109 | UIRequiredDeviceCapabilities
110 |
111 | armv7
112 |
113 | UISupportedInterfaceOrientations
114 |
115 | UIInterfaceOrientationPortrait
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/Emojilist.xcodeproj/xcshareddata/xcschemes/Emojilist.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
44 |
50 |
51 |
52 |
53 |
54 |
60 |
61 |
62 |
63 |
64 |
65 |
76 |
78 |
84 |
85 |
86 |
87 |
90 |
91 |
92 |
93 |
94 |
95 |
101 |
103 |
109 |
110 |
111 |
112 |
114 |
115 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/mobile/controllers/share/ShareSnippetView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShareStudioViewController.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 14/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import RealmSwift
12 |
13 | class ShareSnippetView: UIView,
14 | UICollectionViewDelegate,
15 | UICollectionViewDataSource {
16 |
17 | @IBOutlet weak var listName: UILabel!
18 | @IBOutlet weak var collection: UICollectionView!
19 | @IBOutlet weak var layout: UICollectionViewFlowLayout!
20 | @IBOutlet weak var creditsName: UILabel!
21 | @IBOutlet weak var creditsUrl: UILabel!
22 |
23 | var list: EmojiListViewModel?
24 |
25 | var maxWidth = 686
26 | var maxHeight = 514
27 | let margin = 8
28 | var itemSize = 0
29 |
30 | override func awakeFromNib() {
31 | let nib = UINib(nibName: ShareCell.identifier, bundle: nil)
32 |
33 | collection.register(nib, forCellWithReuseIdentifier: ShareCell.identifier)
34 | collection.delegate = self
35 | collection.dataSource = self
36 |
37 | let del = UIApplication.shared.delegate as! AppDelegate
38 | applyTheme(del.app.theme)
39 | }
40 |
41 | func applyTheme(_ theme: Theme) {
42 | theme.primaryText(listName)
43 | theme.secondaryText(creditsName)
44 | theme.secondaryText(creditsUrl)
45 | theme.background(self)
46 | theme.background(collection)
47 | }
48 |
49 | func configure(with emojiList: EmojiListViewModel) {
50 | list = emojiList
51 |
52 | creditsName.text = "Share.Credits1".localized
53 | creditsUrl.text = "Share.Credits2".localized
54 | listName.text = list?.name
55 |
56 | maxWidth = Int(collection.bounds.width)
57 | maxHeight = Int(collection.bounds.height)
58 |
59 | itemSize = recalculateBasedOnItems(list!.items) - margin
60 | layout.itemSize = CGSize(width: itemSize, height: itemSize)
61 |
62 | collection.reloadData()
63 | }
64 |
65 | func recalculateBasedOnItems(_ emojis: [EmojiViewModel]) -> Int {
66 | let minSize = 50
67 | let maxSize = 220
68 |
69 | let maxItemsMinSize = itemsToFit(
70 | inWidth: maxWidth, inHeight: maxHeight,
71 | withMargin: margin, withSize: minSize)
72 |
73 | let maxItemsMaxSize = itemsToFit(
74 | inWidth: maxWidth, inHeight: maxHeight,
75 | withMargin: margin, withSize: maxSize)
76 |
77 | if emojis.count >= maxItemsMinSize.2 {
78 | return minSize
79 | }
80 |
81 | if emojis.count <= maxItemsMaxSize.2 {
82 | return maxSize
83 | }
84 |
85 | for i in minSize...maxSize {
86 | let dynamicSize = itemsToFit(
87 | inWidth: maxWidth, inHeight: maxHeight,
88 | withMargin: margin, withSize: i)
89 |
90 | if emojis.count > dynamicSize.2 {
91 | return i
92 | }
93 | }
94 |
95 | // Never used
96 | return maxSize
97 | }
98 |
99 | func itemsToFit(inWidth: Int, inHeight: Int, withMargin: Int, withSize: Int) -> (Int, Int, Int) {
100 | let rows = (inHeight - withMargin) / withSize
101 | let columns = (inWidth - withMargin * 2) / withSize
102 | return (columns, rows, columns * rows)
103 | }
104 |
105 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
106 | return list != nil ? list!.items.count : 0
107 | }
108 |
109 | func collectionView(_ collectionView: UICollectionView,
110 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
111 |
112 | let cell = collectionView.dequeueReusableCell(
113 | withReuseIdentifier: ShareCell.identifier,
114 | for: indexPath) as! ShareCell
115 | let del = UIApplication.shared.delegate as! AppDelegate
116 |
117 | if let item = list?.items[indexPath.row] {
118 | cell.configure(with: item, squareSize: itemSize)
119 | cell.applyTheme(del.app.theme)
120 | }
121 |
122 | return cell
123 | }
124 | }
125 |
126 | class ShareCell: UICollectionViewCell {
127 |
128 | static let identifier = "ShareCell"
129 |
130 | @IBOutlet weak var emojiView: EmojiDropView!
131 |
132 | override func awakeFromNib() {
133 | clipsToBounds = false
134 | }
135 |
136 | func configure(with emoji: EmojiViewModel, squareSize: Int) {
137 | emojiView.configure(with: emoji)
138 | emojiView.resize(square: squareSize)
139 | }
140 |
141 | func applyTheme(_ theme: Theme) {
142 | emojiView.applyTheme(theme)
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/mobile/core/Launcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Launcher.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 09/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import RealmSwift
12 | import Firebase
13 |
14 | class Launcher {
15 |
16 | // Vars
17 | private var launchParams: LaunchParams?
18 | private var provideCredentials = false
19 | private var app: App!
20 |
21 | // Weak references
22 | private weak var window: UIWindow?
23 |
24 | func startWith(app usingApp: App) {
25 | app = usingApp
26 | window = UIWindow(frame: UIScreen.main.bounds)
27 | window?.makeKeyAndVisible()
28 | }
29 |
30 | // MARK: - CHAINED CALLS
31 |
32 | // MARK: - UI
33 |
34 | func setWindow(_ window: UIWindow?) -> Self {
35 | self.window = window
36 | return self
37 | }
38 |
39 | func initialViewController(vc: UIViewController) -> Self {
40 | window?.rootViewController = vc
41 | return self
42 | }
43 |
44 | // MARK: - Configurable options
45 |
46 | func shouldProvideCredentials(_ requirement: Bool) -> Self {
47 | provideCredentials = requirement
48 | return self
49 | }
50 |
51 | func setDefaultPack() -> Self {
52 | let defaults = UserDefaults.standard
53 | if defaults.string(forKey: Env.App.defaultPack) == nil {
54 | defaults.set("things", forKey: Env.App.defaultPack)
55 | }
56 | return self
57 | }
58 |
59 | // MARK: - Deeplink
60 |
61 | func setLaunchOptions(_ launchOptions: LaunchParams?) -> Self {
62 | self.launchParams = launchOptions
63 | return self
64 | }
65 |
66 | // MARK: - Migrate Realm
67 |
68 | func migrateRealm() -> Self {
69 |
70 | var config = Realm.Configuration(
71 | // Set the new schema version. This must be greater than the previously used
72 | // version (if you've never set a schema version before, the version is 0).
73 | schemaVersion: 0,
74 |
75 | // Set the block which will be called automatically when opening a Realm with
76 | // a schema version lower than the one set above
77 | migrationBlock: { migration, oldSchemaVersion in
78 | // We haven’t migrated anything yet, so oldSchemaVersion == 0
79 | if (oldSchemaVersion < 0) {
80 | // Nothing to do!
81 | // Realm will automatically detect new properties and removed properties
82 | // And will update the schema on disk automatically
83 | }
84 | })
85 |
86 | config.fileURL = config.fileURL!
87 | .deletingLastPathComponent()
88 | .appendingPathComponent("Mojilist.realm")
89 |
90 | // Tell Realm to use this new configuration object for the default Realm
91 | Realm.Configuration.defaultConfiguration = config
92 | return self
93 | }
94 |
95 | func includeStandardPack() -> Self {
96 | let realm = try! Realm()
97 | let query = realm.objects(REmojiPack.self).filter("ascii = true")
98 |
99 | guard query.count < 1 else {
100 | print("Standard pack is already included")
101 | return self
102 | }
103 |
104 | try! realm.write {
105 | [
106 | (
107 | name: "Pack.EmojiThings",
108 | emojis: thingsEmoji(),
109 | slug: "things"
110 | ), (
111 | name: "Pack.AllEmojis",
112 | emojis: allEmojis(),
113 | slug: "all"
114 | )
115 | ].forEach { emojiPack in
116 | let pack = REmojiPack()
117 | pack.name = emojiPack.name
118 | pack.ascii = true
119 | pack.slug = emojiPack.slug
120 | pack.url = ""
121 |
122 | for ec in emojiPack.emojis {
123 | let e = REmojiPackItem()
124 | e.name = String(ec)
125 | e.pack = pack.slug
126 | pack.emojis.append(e)
127 | }
128 |
129 | realm.add(pack)
130 | }
131 | }
132 |
133 | return self
134 | }
135 |
136 | // MARK: - Third-Party Integrations
137 |
138 | func setFirebase() -> Self {
139 | FirebaseApp.configure()
140 | return self
141 | }
142 |
143 | func setFacebook() -> Self {
144 | // TODO: Add credentials
145 | return self
146 | }
147 |
148 | func setFabric() -> Self {
149 | // TODO: Add credentials
150 | return self
151 | }
152 |
153 | func setTwitter() -> Self {
154 | // TODO: Add credentials
155 | return self
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/mobile/controllers/about/SettingsViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsViewModel.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 20/01/2018.
6 | // Copyright © 2018 Ghost Ship. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class SettingsViewModel: BaseDataViewModel {
12 |
13 | var source = [SettingsGroup]()
14 |
15 | var defaultPackName: String! {
16 | let defaults = UserDefaults.standard
17 | if let slug = defaults.string(forKey: Env.App.defaultPack) {
18 | let predicate = NSPredicate(format: "slug = %@", slug)
19 | let packs = realm.objects(REmojiPack.self).filter(predicate)
20 | if packs.count > 0 {
21 | return packs.first!.name
22 | }
23 | }
24 | return app.standardEmojiPack().name!
25 | }
26 | override var itemsCount: Int! {
27 | return source.count
28 | }
29 |
30 | override func loadSource() {
31 |
32 | // Settings
33 | var settings = SettingsGroup()
34 | settings.title = "About.Settings"
35 |
36 | var theme = SettingsOption()
37 | theme.name = "About.Settings.Theme"
38 | theme.icon = "theme"
39 |
40 | var defaultPack = SettingsOption()
41 | defaultPack.name = "About.Settings.DefaultPack"
42 | defaultPack.icon = "pack"
43 |
44 | settings.items = [theme, defaultPack]
45 |
46 | // Promotion
47 | var promo = SettingsGroup()
48 | promo.title = "About.Promo"
49 |
50 | var signup = SettingsOption()
51 | signup.name = "About.Promo.Signup"
52 | signup.icon = "inbox"
53 | signup.metadata = [
54 | "url": "https://ghostship.us17.list-manage.com/subscribe?u=c95fc7c29b150bc1b79053748&id=ddd4ee4e1f" as AnyObject
55 | ]
56 |
57 | var share = SettingsOption()
58 | share.name = "About.Promo.Share"
59 | share.icon = "like"
60 |
61 | var rate = SettingsOption()
62 | rate.name = "About.Promo.Rate"
63 | rate.icon = "rate"
64 |
65 | promo.items = [signup, share, rate]
66 |
67 | // Follow
68 | var follow = SettingsGroup()
69 | follow.title = "About.Follow"
70 |
71 | var instagram = SettingsOption()
72 | instagram.name = "About.Follow.Instagram"
73 | instagram.icon = "instagram"
74 | instagram.metadata = [
75 | "url": "https://instagram.com/_ghostship_" as AnyObject
76 | ]
77 |
78 | var twitter = SettingsOption()
79 | twitter.name = "About.Follow.Twitter"
80 | twitter.icon = "twitter"
81 | twitter.metadata = [
82 | "url": "https://twitter.com/ghostship__" as AnyObject
83 | ]
84 |
85 | var facebook = SettingsOption()
86 | facebook.name = "About.Follow.Facebook"
87 | facebook.icon = "facebook"
88 | facebook.metadata = [
89 | "url": "https://facebook.com/ghostshiptech" as AnyObject
90 | ]
91 |
92 | var blog = SettingsOption()
93 | blog.name = "About.Follow.Blog"
94 | blog.icon = "safari"
95 | blog.metadata = [
96 | "url": "https://ghostship.co" as AnyObject
97 | ]
98 |
99 | follow.items = [instagram, twitter, facebook, blog]
100 |
101 | // About
102 | var about = SettingsGroup()
103 | about.title = "About.About"
104 |
105 | var feature = SettingsOption()
106 | feature.name = "About.About.Feature"
107 | feature.icon = "mail"
108 | feature.metadata = ["subject": "" as AnyObject]
109 |
110 | var contact = SettingsOption()
111 | contact.name = "About.About.Contact"
112 | contact.icon = "contact"
113 | contact.metadata = ["subject": "" as AnyObject]
114 |
115 | var version = SettingsOption()
116 | version.name = "About.About.Version"
117 | version.cellIdentifier = .simple
118 | let nsObject = Bundle.main.infoDictionary?["CFBundleShortVersionString"]
119 | let appVersion = nsObject as! String
120 | version.metadata = ["version": appVersion as AnyObject]
121 |
122 | //var moreApps = SettingsOption()
123 | //moreApps.name = "About.MoreApps".localized
124 | //moreApps.cellIdentifier = ""
125 | //moreApps.icon = ""
126 |
127 | about.items = [feature, contact, version]
128 |
129 | // Assemble
130 | source = [settings, promo, follow, about]
131 | }
132 |
133 | func item(at indexPath: IndexPath) -> SettingsOption {
134 | return source[indexPath.section].items[indexPath.row]
135 | }
136 |
137 | func section(at: Int) -> SettingsGroup {
138 | return source[at]
139 | }
140 | }
141 |
142 | enum SettingsCellTypes: String {
143 | case simple = "SettingsSimple"
144 | case withIcon = "SettingsIcon"
145 | case actionable = "SettingsActionable"
146 | }
147 |
148 | struct SettingsGroup {
149 |
150 | var title = ""
151 | var items = [SettingsOption]()
152 | }
153 |
154 | struct SettingsOption {
155 |
156 | var name = ""
157 | var icon: String? = nil
158 | var cellIdentifier: SettingsCellTypes = .withIcon
159 | var metadata: [String: AnyObject]? = nil
160 | }
161 |
--------------------------------------------------------------------------------
/mobile/core/App.swift:
--------------------------------------------------------------------------------
1 | //
2 | // App.swift
3 | // Emojilist
4 | //
5 | // Created by Thiago Ricieri on 04/01/18.
6 | // Copyright © 2018 GhostShip. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import DateToolsSwift
11 | import RealmSwift
12 |
13 | protocol App {
14 |
15 | var config: AppConfig { get }
16 | var documentPath: String { get }
17 | var realm: Realm { get }
18 | var theme: Theme { get set }
19 |
20 | var humamFormatter: DateFormatter { get }
21 | var posixFormatter: DateFormatter { get }
22 | var sqlFormatter: DateFormatter { get }
23 | var currencyFormatter: NumberFormatter { get }
24 |
25 | func convertToDate(fromSql: String) -> Date?
26 | func convertToDate(fromSimpleSql: String) -> Date?
27 | func dateToSql(fromHuman: String) -> String
28 | func dateToSimpleSql(fromHuman: String) -> String
29 | func dateToHuman(fromSql: String) -> String
30 | func dateToHuman(from: String, toFormat: String) -> String
31 | func dateToHuman(fromSimpleSql: String) -> String
32 |
33 | func changeVisuals(_ visuals: Visuals)
34 | func standardEmojiPack() -> EmojiPackViewModel
35 | }
36 |
37 | // MARK: - Production App
38 | class ProductionAppImpl: App {
39 |
40 | lazy var theme: Theme = self.initTheme()
41 |
42 | fileprivate(set) var config: AppConfig
43 | fileprivate(set) public var documentPath: String
44 |
45 | fileprivate(set) lazy var realm: Realm = self.initRealm()
46 | fileprivate(set) lazy var humamFormatter: DateFormatter = self.initHumanFormatter()
47 | fileprivate(set) lazy var posixFormatter: DateFormatter = self.initPosixFormatter()
48 | fileprivate(set) lazy var sqlFormatter: DateFormatter = self.initSqlFormatter()
49 | fileprivate(set) lazy var simpleSqlFormatter: DateFormatter = self.initSimpleSqlFormatter()
50 | fileprivate(set) lazy var currencyFormatter: NumberFormatter = self.initCurrencyFormatter()
51 |
52 | init() {
53 | self.config = ProductionAppConfigImpl()
54 | self.documentPath = ""
55 | }
56 |
57 | fileprivate func initRealm() -> Realm {
58 | return try! Realm()
59 | }
60 |
61 | fileprivate func initTheme() -> Theme {
62 | let defaults = UserDefaults.standard
63 | if let theme = defaults.string(forKey: Env.App.theming) {
64 | return Theme(visualString: theme)
65 | }
66 | return Theme(visuals: BasicVisual())
67 | }
68 |
69 | // Init formatters
70 | fileprivate func initHumanFormatter() -> DateFormatter {
71 | let df = DateFormatter()
72 | df.dateFormat = "dd/MM/yyyy"
73 | return df
74 | }
75 |
76 | fileprivate func initPosixFormatter() -> DateFormatter {
77 | let df = DateFormatter()
78 | df.dateFormat = "yyyyMMdd'T'HHmmssZZZ"
79 | df.locale = Locale(identifier:"en_US_POSIX")
80 | return df
81 | }
82 |
83 | fileprivate func initSqlFormatter() -> DateFormatter {
84 | let df = DateFormatter()
85 | df.dateFormat = "yyyy-MM-dd HH:mm:ss"
86 | return df
87 | }
88 |
89 | fileprivate func initSimpleSqlFormatter() -> DateFormatter {
90 | let df = DateFormatter()
91 | df.dateFormat = "yyyy-MM-dd"
92 | return df
93 | }
94 |
95 | fileprivate func initCurrencyFormatter() -> NumberFormatter {
96 | let df = NumberFormatter()
97 | df.numberStyle = .currency
98 | df.locale = Locale(identifier: "pt_BR")
99 | return df
100 | }
101 |
102 | func dateToSql(fromHuman: String) -> String {
103 | let date = humamFormatter.date(from: fromHuman)
104 | return sqlFormatter.string(from: date!)
105 | }
106 |
107 | func dateToSimpleSql(fromHuman: String) -> String {
108 | let date = humamFormatter.date(from: fromHuman)
109 | return simpleSqlFormatter.string(from: date!)
110 | }
111 |
112 | func dateToHuman(fromSql: String) -> String {
113 | let date = sqlFormatter.date(from: fromSql)
114 | return humamFormatter.string(from: date!)
115 | }
116 |
117 | func convertToDate(fromSql: String) -> Date? {
118 | return sqlFormatter.date(from: fromSql)
119 | }
120 |
121 | func dateToHuman(fromSimpleSql: String) -> String {
122 | let date = simpleSqlFormatter.date(from: fromSimpleSql)
123 | return humamFormatter.string(from: date!)
124 | }
125 |
126 | func dateToHuman(from: String, toFormat: String) -> String {
127 | let df = DateFormatter()
128 | df.dateFormat = toFormat
129 | if let date = sqlFormatter.date(from: from) {
130 | return df.string(from: date)
131 | }
132 | return ""
133 | }
134 |
135 | func convertToDate(fromSimpleSql: String) -> Date? {
136 | return simpleSqlFormatter.date(from: fromSimpleSql)
137 | }
138 |
139 | // MARK: - Others
140 |
141 | func changeVisuals(_ visuals: Visuals) {
142 | theme.visuals = visuals
143 | let defaults = UserDefaults.standard
144 | defaults.set(visuals.identifier, forKey: Env.App.theming)
145 |
146 | if UIApplication.shared.supportsAlternateIcons {
147 | UIApplication.shared.setAlternateIconName(visuals.icon)
148 | }
149 | }
150 |
151 | func standardEmojiPack() -> EmojiPackViewModel {
152 | let pack = realm.objects(REmojiPack.self).filter("ascii = true").first!
153 | return EmojiPackViewModel(with: pack)
154 | }
155 | }
156 |
157 | // MARK: - Staging App
158 | class StagingAppImpl: ProductionAppImpl {
159 |
160 | public override init() {
161 | super.init()
162 | self.config = StagingAppConfigImpl()
163 | }
164 | }
165 |
--------------------------------------------------------------------------------