Skip to content

Commit

Permalink
Merge branch 'release/1.2' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
mouredev committed Sep 6, 2021
2 parents 0c99b5f + 6c9321e commit b45defd
Show file tree
Hide file tree
Showing 40 changed files with 829 additions and 242 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Twitimer es una App gratuita para **[iOS](https://apps.apple.com/us/app/twitimer

* Por razones de seguridad debes añadir tu propio fichero de configuración de Firebase `GoogleService-Info.plist` con Realtime Database activo y las claves de acceso al API de Twitch en Remote Config con los valores `TwitchClientID` y `TwitchClientSecret`.
* Es suficiente con ejecutar el archivo `Twitimer.xcodeproj` en Xcode.
* [Puedes ver todo el proceso en este tutorial en YouTube](https://youtu.be/_FLHGY_ATWA)

<a href="https://youtu.be/_FLHGY_ATWA"><img src="http://i3.ytimg.com/vi/_FLHGY_ATWA/maxresdefault.jpg" style="height: 50%; width:50%;"/></a>

### ¿De qué forma utilizamos Twitimer para aprender programación?
* Puedes acceder a su código fuente libremente.
Expand Down
187 changes: 116 additions & 71 deletions Twitimer.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Twitimer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import UIKit
import SwiftUI
import Firebase
import FirebaseMessaging
import IQKeyboardManagerSwift
import UserNotifications

Expand Down
23 changes: 23 additions & 0 deletions Twitimer/Assets.xcassets/Icon/settings.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "settings.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "settings@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "settings@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Twitimer/Assets.xcassets/Networks/tiktok.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "tiktok.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
2 changes: 2 additions & 0 deletions Twitimer/Assets.xcassets/Networks/tiktok.imageset/tiktok.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion Twitimer/Model/Domain/DatabaseUser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ struct DatabaseUser: Codable {
let streamer: Int?
let schedule: [DatabaseUserSchedule]?
let followedUsers: [String]?
let settings: DatabaseUserSettings?

func toUser() -> User {

let schedule = self.schedule?.map({ (dbSchedule) -> UserSchedule in
return dbSchedule.toUserSchedule()
})

return User(id: id, login: login, displayName: displayName, broadcasterType: BroadcasterType(rawValue: broadcasterType ?? ""), descr: descr, profileImageUrl: profileImageUrl, offlineImageUrl: offlineImageUrl, streamer: streamer == 1,schedule: schedule, followedUsers: followedUsers ?? [])
return User(id: id, login: login, displayName: displayName, broadcasterType: BroadcasterType(rawValue: broadcasterType ?? ""), descr: descr, profileImageUrl: profileImageUrl, offlineImageUrl: offlineImageUrl, streamer: streamer == 1,schedule: schedule, followedUsers: followedUsers ?? [], settings: settings?.toUserSettings())
}

}
Expand All @@ -46,3 +47,18 @@ struct DatabaseUserSchedule: Codable {
}

}

struct DatabaseUserSettings: Codable {

var discord: String?
var youtube: String?
var twitter: String?
var instagram: String?
var tiktok: String?

func toUserSettings() -> UserSettings {

return UserSettings(discord: discord ?? "", youtube: youtube ?? "", twitter: twitter ?? "", instagram: instagram ?? "", tiktok: tiktok ?? "")
}

}
32 changes: 32 additions & 0 deletions Twitimer/Model/Domain/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct User: Codable {
var streamer: Bool?
var schedule: [UserSchedule]?
var followedUsers: [String]?
var settings: UserSettings?

enum CodingKeys: String, CodingKey {

Expand All @@ -51,6 +52,7 @@ struct User: Codable {
DatabaseField.followedUsers.rawValue:followedUsers ?? []]

JSON[DatabaseField.schedule.rawValue] = scheduleToJSON()
JSON[DatabaseField.settings.rawValue] = settingsToJSON()

return JSON
}
Expand All @@ -65,6 +67,10 @@ struct User: Codable {
return scheduleJSON
}

func settingsToJSON() -> [String:Any] {
return settings?.toJSON() ?? [:]
}

// Actualiza datos mutables del usuario. Esto ocurre cuando recuperamos de nuevo el usuario de Twitch para actualizarlo en Twitimer.
mutating func override(user: User) -> Bool {

Expand Down Expand Up @@ -217,3 +223,29 @@ enum WeekdayType: Int, Codable, CaseIterable {
}

}

struct UserSettings: Codable, Equatable {

var discord: String
var youtube: String
var twitter: String
var instagram: String
var tiktok: String

func toJSON() -> [String:Any] {

return [DatabaseField.discord.rawValue:discord,
DatabaseField.youtube.rawValue:youtube,
DatabaseField.twitter.rawValue:twitter,
DatabaseField.instagram.rawValue:instagram,
DatabaseField.tiktok.rawValue:tiktok]
}

}

// Empty UserSettings init
extension UserSettings {
init() {
self.init(discord: "", youtube: "", twitter: "", instagram: "", tiktok: "")
}
}
13 changes: 13 additions & 0 deletions Twitimer/Model/Session/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ final class Session {
}
}
}

func save(settings: UserSettings) {

if user?.settings != settings {
user?.settings = settings
if let user = user {
UserDefaultsProvider.setCodable(key: .authUser, value: user)
FirebaseRDBService.shared.saveSettings(user: user)
}
}
}


func save(followedUser: User) {

Expand Down Expand Up @@ -336,6 +348,7 @@ final class Session {
private func mergeUsers(user: User, oldFollowers: Set<String>, success: @escaping () -> Void) {

self.user?.schedule = user.schedule
self.user?.settings = user.settings

// Merge followers
let mergedFollowers = Array(oldFollowers.union(Set(user.followedUsers ?? [])))
Expand Down
8 changes: 8 additions & 0 deletions Twitimer/Provider/Services/Firebase/FirebaseRDBService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum DatabaseField: String {
case streamer
case schedule, enable, weekDay, date, duration, title // Schedule
case followedUsers
case settings, discord, youtube, twitter, instagram, tiktok // Settings
}

final class FirebaseRDBService {
Expand Down Expand Up @@ -47,6 +48,13 @@ final class FirebaseRDBService {
streamersRef.child(login).child(DatabaseField.schedule.rawValue).setValue(user.scheduleToJSON())
}
}

func saveSettings(user: User) {

if let login = user.login {
streamersRef.child(login).child(DatabaseField.settings.rawValue).setValue(user.settingsToJSON())
}
}

func search(query: String, success: @escaping (_ users: [User]?) -> Void, failure: @escaping (_ error: Error?) -> Void) {

Expand Down
14 changes: 12 additions & 2 deletions Twitimer/SupportingFiles/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"user.closesession.alert.body" = "Are you sure you want to log out?";
"user.saveschedule.alert.body" = "If you save these new schedules, your viewers will be notified.";
"user.seechannel" = "See channel";
"user.streamer" = "I'm streamer";
"user.streamer" = "I'm a streamer";
"user.syncschedule.alert.title" = "Synchronize Twitch schedule";
"user.syncschedule.alert.body" = "Do you want to sync your recurring Twitch calendar? The Twitimer schedule will be overwritten.";

Expand All @@ -82,7 +82,7 @@
"info.channel.share" = "Hello @[%@]! Do you know #Twitimer?\n\nIt is an App for streamers to add their broadcast schedules on Twitch and notify their audience every time they do.\n\nIt can be downloaded for iOS and Android for free from https://twitimer.com\n\nThank you!";

"info.streamer.title" = "Are you a streamer?";
"info.streamer.body" = "If you make live broadcasts, activate the option \"I'm streamer\" to configure your schedules and notify viewers.";
"info.streamer.body" = "If you make live broadcasts, activate the option \"I'm a streamer\" to configure your schedules, social media and notify viewers.";
"info.streamer.advice.1" = "Set, update and save the schedules of your weekly broadcasts and specific events.";

"info.auth.title" = "Log in securely with your Twitch account";
Expand All @@ -100,3 +100,13 @@
"menu.site" = "Visit twitimer.com to check all the official information";
"menu.onboarding" = "Visualize the welcome tutorial again";
"menu.version" = "Version %@. Twitimer has no relationship with Twitch Interactive, Inc. and all of its services.";

// Settings

"settings.socialmedia" = "Social media";
"settings.discord.placeholder" = "Discord invitation link code";
"settings.youtube.placeholder" = "YouTube user";
"settings.twitter.placeholder" = "Twitter user";
"settings.instagram.placeholder" = "Instagram user";
"settings.tiktok.placeholder" = "TikTok user";
"settings.savesettings" = "Save settings";
12 changes: 11 additions & 1 deletion Twitimer/SupportingFiles/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"info.channel.share" = "¡Hola @[%@]! ¿Conoces #Twitimer?\n\nEs una App para que los streamers puedan añadir sus horarios de emisión en Twitch y notificar a su audiencia cada vez que hagan.\n\nSe puede descargar para iOS y Android de forma gratuita desde https://twitimer.com\n\n¡Gracias!";

"info.streamer.title" = "¿Eres streamer?";
"info.streamer.body" = "Si realizas transmisiones en directo activa la opción \"Soy streamer\" para configurar tus horarios y notificar a los espectadores.";
"info.streamer.body" = "Si realizas transmisiones en directo activa la opción \"Soy streamer\" para configurar tus horarios, redes sociales y notificar a los espectadores.";
"info.streamer.advice.1" = "Establece, actualiza y guarda los horarios de tus transmisiones semanales y eventos puntuales.";

"info.auth.title" = "Inicia sesión de forma segura con tu cuenta de Twitch";
Expand All @@ -100,3 +100,13 @@
"menu.site" = "Visita twitimer.com para consultar toda la información oficial";
"menu.onboarding" = "Visualiza de nuevo el tutorial de bienvenida";
"menu.version" = "Versión %@. Twitimer no posee ningún tipo de relación con Twitch Interactive, Inc. y todos sus servicios.";

// Settings

"settings.socialmedia" = "Redes sociales";
"settings.discord.placeholder" = "Código de invitación de Discord";
"settings.youtube.placeholder" = "Usuario de YouTube";
"settings.twitter.placeholder" = "Usuario de Twitter";
"settings.instagram.placeholder" = "Usuario de Instagram";
"settings.tiktok.placeholder" = "Usuario de TikTok";
"settings.savesettings" = "Guardar ajustes";
4 changes: 3 additions & 1 deletion Twitimer/UseCases/Common/Controls/ActionButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import SwiftUI
struct ActionButton: View {

let image: Image
let action: () -> Void
var padding: Size = .none
let action: () -> Void

var body: some View {
Button(action: {
action()
}) {
image.template.resizable()
.aspectRatio(contentMode: .fit)
.padding(padding.rawValue)
.frame(width: Size.mediumBig.rawValue, height: Size.mediumBig.rawValue)
.foregroundColor(.lightColor)
}
Expand Down
34 changes: 34 additions & 0 deletions Twitimer/UseCases/Common/Controls/IconTextField.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// IconTextField.swift
// Twitimer
//
// Created by Brais Moure on 25/8/21.
//

import SwiftUI

struct IconTextField: View {

// Properties

let image: String
@Binding var title: String
let placeholder: LocalizedStringKey

// Body

var body: some View {
HStack(spacing: Size.medium.rawValue) {
Image(image).template.resizable().aspectRatio(contentMode: .fit)
.foregroundColor(.textColor)
.frame(width: Size.mediumBig.rawValue, height: Size.big.rawValue)
MainTextField(title: $title, placeholder: placeholder)
}
}
}

struct IconTextField_Previews: PreviewProvider {
static var previews: some View {
IconTextField(image: "discord", title: .constant(""), placeholder: "My placeholder")
}
}
53 changes: 53 additions & 0 deletions Twitimer/UseCases/Common/Controls/MainTextField.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// MainTextField.swift
// Twitimer
//
// Created by Brais Moure on 25/8/21.
//

import SwiftUI
import Combine

struct MainTextField: View {

// Properties

@Binding var title: String
let placeholder: LocalizedStringKey
var readOnly = false
var enable = true

private let kTextLimit = 100

// Body

var body: some View {

TextField("", text: $title).onReceive(Just(title)) { _ in
limitText(kTextLimit)
}
.modifier(TextFieldPlaceholderStyle(showPlaceHolder: title.isEmpty, placeholder: placeholder))
.font(size:.body)
.foregroundColor(readOnly ? .lightColor : .textColor)
.accentColor(readOnly ? .lightColor : .textColor)
.padding(Size.small.rawValue)
.background(readOnly ? Color.secondaryColor : Color.backgroundColor)
.clipShape(Capsule())
.disabled(!enable || readOnly)
}

// MARK: Functions

private func limitText(_ upper: Int) {
if title.count > upper {
title = String(title.prefix(upper))
}
}

}

struct MainTextField_Previews: PreviewProvider {
static var previews: some View {
MainTextField(title: .constant(""), placeholder: "My placeholder")
}
}
Loading

0 comments on commit b45defd

Please sign in to comment.