Compare commits
52 Commits
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 354 KiB |
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
@ -1,41 +0,0 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Emre on 17/12/2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x3C",
|
||||
"green" : "0x3C",
|
||||
"red" : "0x3C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "blueFlame.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 3.4 KiB |
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "globe.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 39 KiB |
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "pinkFlame.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 3.5 KiB |
@ -0,0 +1,168 @@
|
||||
//
|
||||
// BinaryBetLine.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Lucas Delanier on 10/06/2024.
|
||||
//
|
||||
|
||||
|
||||
import SwiftUI
|
||||
import Model
|
||||
|
||||
struct BinaryBetLine: View {
|
||||
|
||||
@State var showInfos: Bool = false
|
||||
var bet: BetDetail
|
||||
|
||||
var value: CGFloat {
|
||||
let totalParticipations = bet.participations.count
|
||||
let numberOfYes = bet.participations.filter { $0.answer.uppercased() == "YES" }.count
|
||||
let numberOfNo = bet.participations.filter { $0.answer.uppercased() == "NO" }.count
|
||||
if(numberOfNo == 0 && numberOfYes == 0){
|
||||
return 0.5
|
||||
}
|
||||
|
||||
return totalParticipations > 0 ? CGFloat(numberOfYes) / CGFloat(totalParticipations) : 0.0
|
||||
}
|
||||
|
||||
var yesParticipations: [Participation] {
|
||||
bet.participations.filter { $0.answer.uppercased() == "YES" }
|
||||
}
|
||||
|
||||
var noParticipations: [Participation] {
|
||||
bet.participations.filter { $0.answer.uppercased() == "NO" }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(spacing: 5) {
|
||||
Text("OUI")
|
||||
.font(.system(size: 25))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.blue200)
|
||||
Spacer()
|
||||
Text("NON")
|
||||
.font(.system(size: 25))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.pink100)
|
||||
}
|
||||
|
||||
GeometryReader { geometry in
|
||||
ZStack(alignment: .leading) {
|
||||
HStack{
|
||||
Spacer()
|
||||
Rectangle()
|
||||
.frame(width: min(CGFloat(1-self.value) * geometry.size.width, geometry.size.width-20), height: 17)
|
||||
.foregroundStyle(AllInColors.PinkBetGradiant).cornerRadius(999)
|
||||
}
|
||||
|
||||
HStack(spacing: 0) {
|
||||
Rectangle()
|
||||
.frame(width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width-20), height: 17)
|
||||
.foregroundStyle(AllInColors.BlueBetGradiant)
|
||||
.cornerRadius(999)
|
||||
Image("loadingHeartIcon")
|
||||
.resizable()
|
||||
.frame(width: 29, height: 32)
|
||||
.padding(.leading, -10)
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
.frame(height: 40)
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("bet_status_details_drawer")
|
||||
.textStyle(weight: .medium, color: AllInColors.primaryTextColor, size: 10)
|
||||
Image(showInfos ? "chevronUpIcon" : "chevronDownIcon")
|
||||
.resizable()
|
||||
.frame(width: 10, height: 7)
|
||||
.scaledToFill()
|
||||
}
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
showInfos.toggle()
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.padding(.trailing, 5)
|
||||
|
||||
if showInfos {
|
||||
VStack(spacing: 1) {
|
||||
HStack(spacing: 5) {
|
||||
Image("blueAllCoinIcon")
|
||||
.resizable()
|
||||
.frame(width:12, height: 12)
|
||||
Text(yesParticipations.reduce(0, {x,y in x + y.stake}).description)
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.blue200)
|
||||
Spacer()
|
||||
Text(noParticipations.reduce(0, {x,y in x + y.stake}).description)
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.pink100)
|
||||
Image("pinkAllCoinIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
}
|
||||
HStack(spacing: 5){
|
||||
Image("bluePersonIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
Text(yesParticipations.count.description)
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.blue200)
|
||||
Spacer()
|
||||
Text(noParticipations.count.description)
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.pink100)
|
||||
Image("pinkPersonIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
|
||||
}
|
||||
HStack(spacing: 5){
|
||||
Image("blueBadgeIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
Text(yesParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0")
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.blue200)
|
||||
Spacer()
|
||||
Text(noParticipations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0")
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.pink100)
|
||||
Image("pinkBadgeIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
|
||||
}
|
||||
HStack(spacing: 5){
|
||||
Image("blueTrophyIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
Text("1.2")
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.blue200)
|
||||
Spacer()
|
||||
Text("1.2")
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(AllInColors.pink100)
|
||||
Image("pinkTrophyIcon")
|
||||
.resizable()
|
||||
.frame(width:12, height: 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
//
|
||||
// CustomBetLine.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Lucas Delanier on 10/06/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Model
|
||||
|
||||
struct CustomBetLine: View {
|
||||
|
||||
@State var showInfos: [String: Bool] = [:]
|
||||
var bet: BetDetail
|
||||
|
||||
var participationsForAnswer: [String: [Participation]] {
|
||||
Dictionary(grouping: bet.participations, by: { $0.answer.uppercased() })
|
||||
}
|
||||
|
||||
var sortedAnswers: [AnswerDetail] {
|
||||
bet.answers.sorted { $0.totalParticipants > $1.totalParticipants }
|
||||
}
|
||||
|
||||
func getTextStyle(for answer: AnswerDetail) -> Font.Weight {
|
||||
return answer == sortedAnswers.first ? .bold : .light
|
||||
}
|
||||
|
||||
func getColor(for answer: AnswerDetail) -> Color {
|
||||
return answer == sortedAnswers.first ? AllInColors.pinkAccentColor : AllInColors.blueAccentColor
|
||||
}
|
||||
|
||||
func getGradiant(for answer: AnswerDetail) -> LinearGradient {
|
||||
return answer == sortedAnswers.first ? AllInColors.PinkBetGradiant : AllInColors.BlueBetLineGradiant
|
||||
}
|
||||
|
||||
func getFlameImage(for answer: AnswerDetail) -> String {
|
||||
return answer == sortedAnswers.first ? "pinkFlameIcon" : "blueFlameIcon"
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(sortedAnswers, id: \.response) { answer in
|
||||
let participations = participationsForAnswer[answer.response.uppercased()] ?? []
|
||||
let totalParticipations = bet.participations.count
|
||||
let percentage = totalParticipations > 0 ? CGFloat(participations.count) / CGFloat(totalParticipations) : 0.0
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Text(answer.response)
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(getTextStyle(for: answer))
|
||||
.foregroundColor(getColor(for: answer))
|
||||
Spacer()
|
||||
}
|
||||
|
||||
GeometryReader { geometry in
|
||||
ZStack(alignment: .leading) {
|
||||
HStack(spacing: 0) {
|
||||
Rectangle()
|
||||
.frame(width: min(percentage * geometry.size.width, geometry.size.width-20), height: 17)
|
||||
.foregroundStyle(getGradiant(for: answer))
|
||||
.cornerRadius(999, corners: [.topLeft, .bottomLeft])
|
||||
Image(getFlameImage(for: answer))
|
||||
.resizable()
|
||||
.frame(width: 29, height: 32)
|
||||
.padding(.leading, -6)
|
||||
Text("\(Int(percentage * 100))%")
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(getColor(for: answer))
|
||||
.padding(.leading, 2)
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
.frame(height: 40)
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("bet_status_details_drawer")
|
||||
.textStyle(weight: .medium, color: AllInColors.primaryTextColor, size: 10)
|
||||
Image(showInfos[answer.response] ?? false ? "chevronUpIcon" : "chevronDownIcon")
|
||||
.resizable()
|
||||
.frame(width: 10, height: 7)
|
||||
.scaledToFill()
|
||||
}
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
showInfos[answer.response, default: false].toggle()
|
||||
}
|
||||
}
|
||||
.padding(.trailing, 5)
|
||||
|
||||
if showInfos[answer.response] ?? false {
|
||||
HStack{
|
||||
VStack(alignment: .leading,spacing: 1) {
|
||||
HStack(spacing: 5) {
|
||||
Image(answer == sortedAnswers.first ? "pinkAllCoinIcon" : "blueAllCoinIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
Text(participations.reduce(0, { x, y in x + y.stake }).description)
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(getColor(for: answer))
|
||||
}
|
||||
HStack(spacing: 5) {
|
||||
Image(answer == sortedAnswers.first ? "pinkPersonIcon" : "bluePersonIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
Text(participations.count.description)
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(getColor(for: answer))
|
||||
}
|
||||
HStack(spacing: 5) {
|
||||
Image(answer == sortedAnswers.first ? "pinkBadgeIcon" : "blueBadgeIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
Text(participations.max(by: { $0.stake < $1.stake })?.stake.description ?? "0")
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(getColor(for: answer))
|
||||
}
|
||||
HStack(spacing: 5) {
|
||||
Image(answer == sortedAnswers.first ? "pinkTrophyIcon" : "blueTrophyIcon")
|
||||
.resizable()
|
||||
.frame(width: 12, height: 12)
|
||||
Text(String(format: "%.2f", answer.odds))
|
||||
.font(.system(size: 15))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(getColor(for: answer))
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
//
|
||||
// ParticiationCell.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Lucas Delanier on 21/01/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Model
|
||||
|
||||
struct ParticiationCell: View {
|
||||
@State var participation: Participation?
|
||||
var body: some View {
|
||||
HStack(alignment: .center, spacing: 0){
|
||||
Circle()
|
||||
.frame(width: 30, height: 30)
|
||||
.foregroundColor(AllInColors.grey700Color)
|
||||
.padding(.trailing, 7)
|
||||
Text(participation?.user.username ?? "Unknown")
|
||||
.font(.system(size: 15))
|
||||
.foregroundStyle(AllInColors.primaryTextColor)
|
||||
.fontWeight(.semibold)
|
||||
Spacer()
|
||||
Text(participation?.stake.description ?? "NaN")
|
||||
.font(.system(size: 18))
|
||||
.foregroundStyle(AllInColors.lightPurpleColor)
|
||||
.fontWeight(.bold)
|
||||
.padding(.trailing, 8)
|
||||
Image("PurpleAllCoin")
|
||||
.resizable()
|
||||
.frame(width: 11, height: 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
//
|
||||
// userPicture.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Lucas Delanier on 04/06/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
struct UserPicture: View {
|
||||
var picture: String?
|
||||
var username: String
|
||||
var size: CGFloat
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if let pictureURL = picture {
|
||||
userImage(url: pictureURL)
|
||||
} else {
|
||||
placeholderImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor private func userImage(url: String) -> some View {
|
||||
AsyncCachedImage(url: URL(string: url)) { image in
|
||||
image
|
||||
.resizable()
|
||||
.frame(width: size, height: size)
|
||||
.clipShape(Circle())
|
||||
} placeholder: {
|
||||
placeholderImage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var placeholderImage: some View {
|
||||
Circle()
|
||||
.foregroundColor(.gray)
|
||||
.frame(width: size, height: size)
|
||||
.overlay(
|
||||
Text(username.prefix(2).uppercased())
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.white)
|
||||
.font(.system(size: fontSize(for: size)))
|
||||
)
|
||||
}
|
||||
|
||||
private func fontSize(for diameter: CGFloat) -> CGFloat {
|
||||
let fontScaleFactor: CGFloat = 0.45
|
||||
return diameter * fontScaleFactor
|
||||
}
|
||||
}
|
||||
|
||||
struct UserPicture_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
UserPicture(picture: nil, username: "Lucas", size: 100)
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Delegates.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Emre on 22/02/2024.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
if let selectedAction = options.shortcutItem {
|
||||
QuickAction.selectedAction = selectedAction
|
||||
}
|
||||
let sceneConfiguration = UISceneConfiguration(name: "Quick Action Scene", sessionRole: connectingSceneSession.role)
|
||||
sceneConfiguration.delegateClass = QuickActionSceneDelegate.self
|
||||
return sceneConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
class QuickActionSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||
QuickAction.selectedAction = shortcutItem
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
//
|
||||
// QuickAction.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Emre on 22/02/2024.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
enum QuickAction {
|
||||
static var selectedAction: UIApplicationShortcutItem?
|
||||
|
||||
static var homeuserInfo: [String : NSSecureCoding] {
|
||||
["name" : "Bet" as NSSecureCoding]
|
||||
}
|
||||
|
||||
static var createuserInfo: [String : NSSecureCoding] {
|
||||
["name" : "CreationBet" as NSSecureCoding]
|
||||
}
|
||||
|
||||
static var frienduserInfo: [String : NSSecureCoding] {
|
||||
["name" : "Friends" as NSSecureCoding]
|
||||
}
|
||||
|
||||
static var rankuserInfo: [String : NSSecureCoding] {
|
||||
["name" : "Ranking" as NSSecureCoding]
|
||||
}
|
||||
|
||||
static var allShortcutItems: [UIApplicationShortcutItem] = {
|
||||
var shortcuts: [UIApplicationShortcutItem] = []
|
||||
|
||||
if AppStateContainer.shared.loggedState.connectedUser {
|
||||
shortcuts.append(UIApplicationShortcutItem(type: "home", localizedTitle: "Coins : " + String(AppStateContainer.shared.user?.nbCoins ?? 0), localizedSubtitle: "", icon: UIApplicationShortcutIcon(templateImageName: "allCoinBlackIcon"), userInfo: homeuserInfo))
|
||||
}
|
||||
|
||||
shortcuts.append(contentsOf: [
|
||||
UIApplicationShortcutItem(type: "create", localizedTitle: "Créer un pari", localizedSubtitle: "", icon: UIApplicationShortcutIcon(systemImageName: "pencil.circle"), userInfo: createuserInfo),
|
||||
UIApplicationShortcutItem(type: "friend", localizedTitle: "Voir mes amis", localizedSubtitle: "", icon: UIApplicationShortcutIcon(systemImageName: "person.2.square.stack"), userInfo: frienduserInfo),
|
||||
UIApplicationShortcutItem(type: "ranking", localizedTitle: "Classement", localizedSubtitle: "Où en suis-je ?", icon: UIApplicationShortcutIcon(systemImageName: "rosette"), userInfo: rankuserInfo)
|
||||
])
|
||||
|
||||
return shortcuts
|
||||
}()
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
AllInApp
|
||||
|
||||
Created by Emre on 15/05/2024.
|
||||
|
||||
*/
|
||||
|
||||
/// Core
|
||||
|
||||
"generic_username" = "Username";
|
||||
"generic_email" = "Email";
|
||||
"generic_password" = "Password";
|
||||
"generic_login" = "Login";
|
||||
"generic_logout" = "Logout";
|
||||
"generic_already_have_account" = "Already have an account ?";
|
||||
"generic_register" = "Register";
|
||||
"generic_validate" = "Validate";
|
||||
"generic_ok" = "OK";
|
||||
"generic_error" = "Error";
|
||||
"generic_in_waiting" = "In waiting";
|
||||
"generic_search" = "Search";
|
||||
"generic_add" = "Add";
|
||||
"generic_acccept"="Accept";
|
||||
"generic_decline"="Decline";
|
||||
"generic_delete" = "Delete";
|
||||
"generic_stake" = "Stake";
|
||||
"network_error_text" = "Make sure to be properly connected to the network then try again.";
|
||||
|
||||
/// Bet type
|
||||
|
||||
"bet_type_binary" = "Yes / No";
|
||||
"bet_type_match" = "Sport match";
|
||||
"bet_type_custom" = "Custom answers";
|
||||
|
||||
/// Field error
|
||||
|
||||
"field_error_mandatory" = "This field is mandatory.";
|
||||
"field_error_not_identical" = "The fields are not identical.";
|
||||
"field_error_bad_email" = "Please enter a valid email.";
|
||||
|
||||
/// Drawer
|
||||
|
||||
"drawer_bets" = "Bets";
|
||||
"drawer_best_win" = "Best win";
|
||||
"drawer_nb_friends" = "Friends";
|
||||
"drawer_friends" = "FRIENDS";
|
||||
"drawer_friends_subtitle" = "Challenge your folks by adding them as friends.";
|
||||
"drawer_public_bets" = "PARTICIPATE";
|
||||
"drawer_public_bets_subtitle" = "Browse the most popular bets of the moment.";
|
||||
"drawer_create_a_bet" = "NEW BET";
|
||||
"drawer_create_a_bet_subtitle" = "Create a net bet and get your friends participating.";
|
||||
"drawer_bet_history" = "HISTORY";
|
||||
"drawer_bet_history_subtitle" = "View your current and finished bets.";
|
||||
"drawer_current_bets" = "MY PARTICIPATIONS";
|
||||
"drawer_current_bets_subtitle" = "Manage your bets and reward the winners.";
|
||||
"drawer_ranking" = "RANKING";
|
||||
"drawer_ranking_subtitle" = "Check your ranking among your friends.";
|
||||
|
||||
/// Welcome Page
|
||||
|
||||
"welcome_title" = "Welcome to,";
|
||||
"welcome_subtitle" = "Collect your Allcoins and come bet with your friend to prove who\'s best.";
|
||||
"welcome_join" = "Join";
|
||||
|
||||
/// Register Page
|
||||
|
||||
"register_hello" = "Hello,";
|
||||
"register_hello %@" = "Hello %@,";
|
||||
"register_title" = "We need this!";
|
||||
"register_subtitle" = "Don\'t worry it\'s fast.";
|
||||
"register_error_title" = "Error saving";
|
||||
"register_error_content" = "Registration failed. Try Again.";
|
||||
"register_confirm_password" = "Confirm password";
|
||||
"register_already_used" = "Email or nickname already used.";
|
||||
|
||||
/// Login Page
|
||||
|
||||
"login_title" = "Welcome back !";
|
||||
"login_subtitle" = "We missed you.";
|
||||
"login_error_title" = "Connection error";
|
||||
"login_error_content" = "Failed to login. Please try again.";
|
||||
"login_no_account" = "Don't have an account ?";
|
||||
"login_forgot_password" = "Forgot password ?";
|
||||
"login_bad_credentials" = "Incorrect login or password.";
|
||||
|
||||
/// Bet Creation Page
|
||||
|
||||
"bet_creation_theme_tooltip" = "Usually a common name describing the overall theme of the bet for reference..";
|
||||
"bet_creation_phrase_tooltip" = "Generally the question the participants will answer to.";
|
||||
"bet_creation_register_tooltip" = "After this date, nobody will be able to register anymore.";
|
||||
"bet_creation_bet_end_tooltip" = "After this date, the result will be announced.";
|
||||
"bet_creation_privacy_tooltip" = "Determines who will be able to see the bet.";
|
||||
"bet_creation_question" = "Question";
|
||||
"bet_creation_answer" = "Answer";
|
||||
"bet_creation_publish" = "Publish the bet";
|
||||
"bet_creation_theme" = "Theme";
|
||||
"bet_creation_theme_placeholder" = "Studies, sports, party…";
|
||||
"bet_creation_bet_phrase" = "Bet phrase";
|
||||
"bet_creation_bet_phrase_placeholder" = "Will David be missing this Monday in class ?";
|
||||
"bet_creation_end_registration_date" = "Registration end date";
|
||||
"bet_creation_end_bet_date" = "Bet end date";
|
||||
"bet_creation_bet_privacy" = "Bet privacy";
|
||||
"bet_creation_friends_available_format" = "friends available";
|
||||
"bet_creation_public_bottom_text_1" = "Your BET will be visible by all the users.";
|
||||
"bet_creation_public_bottom_text_2" = "Everyone will be able to join the BET.";
|
||||
"bet_creation_private_bottom_text_1" = "Your BET will only be visible by your friends.";
|
||||
"bet_creation_private_bottom_text_2" = "Only your friends will be able to join the BET.";
|
||||
"bet_creation_bottom_text_3" = "You can invite friends at any moment during the registration period.";
|
||||
"bet_creation_yes_no_bottom_text_1" = "The participants will have to respond with either YES or NO.";
|
||||
"bet_creation_yes_no_bottom_text_2" = "No other answer will be accepted.";
|
||||
"bet_creation_custom_bottom_text_1" = "You are going to fill in the different answers available in this bet.";
|
||||
"bet_creation_custom_bottom_text_2" = "Be careful to be clear and avoid any uncertainties";
|
||||
"bet_creation_error" = "Error while creating the bet.";
|
||||
"bet_creation_success_message" = "Bet created !";
|
||||
"bet_creation_response_title" = "Response title";
|
||||
"bet_creation_max_answers %lld" = "%lld more max.";
|
||||
"bet_creation_error_past_date" = "The registration end date must be later than the current date.";
|
||||
"bet_creation_error_date_order" = "The betting end date must be later than the registration end date.";
|
||||
|
||||
/// Bet page
|
||||
|
||||
"bet_popular" = "Popular";
|
||||
"bet_public" = "Public";
|
||||
"bet_private" = "Private";
|
||||
"bet_invitation" = "Invitation";
|
||||
"bet_current" = "Current";
|
||||
"bet_finished" = "Finished";
|
||||
"bet_starting" = "Starting on";
|
||||
"bet_started" = "Started on";
|
||||
"bet_ends" = "Ends on";
|
||||
"bet_ended" = "Ended on";
|
||||
"bet_participate" = "Participate";
|
||||
"bet_proposed_by_format %@" = "Proposed by %@";
|
||||
"bet_proposed_by_format" = "Proposed by";
|
||||
"bet_players_waiting_format %@" = "%@ players waiting";
|
||||
"bet_players_format" = "players";
|
||||
"bet_points_at_stake_format" = "points at stake";
|
||||
|
||||
/// Bet status
|
||||
|
||||
"bet_status_finished" = "Finished !";
|
||||
"bet_status_in_progress" = "In progress…";
|
||||
"bet_status_waiting" = "Waiting…";
|
||||
"bet_status_unavailable" = "Status unavailable";
|
||||
"bet_status_cancelled" = "Cancelled";
|
||||
"bet_status_place_your_bets" = "Place your bets";
|
||||
"bet_status_participants_list" = "Participants";
|
||||
"bet_status_details_drawer" = "Details";
|
||||
"participation_possible_winnings" = "Possible winnings";
|
||||
"bet_status_stake" = "Gambled";
|
||||
|
||||
/// Bet history
|
||||
|
||||
"bet_history_current_title" = "Current";
|
||||
"bet_history_title" = "History";
|
||||
|
||||
/// Bet confirmation
|
||||
|
||||
"bet_confirmation_text" = "This bet has now ended. You may now distribute the winnings by selecting the right answer.";
|
||||
"bet_confirmation_choose_response" = "Please select the final answer :";
|
||||
|
||||
/// Ranking
|
||||
|
||||
"ranking_title" = "Ranking";
|
||||
|
||||
/// Friends
|
||||
|
||||
"friends_title" = "Friends";
|
||||
"friends_request_sent" = "Request sent";
|
||||
"friends_request"= "Friends Request";
|
||||
|
||||
/// Daily reward
|
||||
|
||||
"daily_reward_title" = "Daily reward";
|
||||
"daily_reward_subtitle" = "Your daily reward is unlocked every day at 00:00 UTC and allows you to get between 10 and 150 Allcoins.";
|
||||
|
||||
/// Notification
|
||||
|
||||
"notification_title_end_bet_date" = "Who will be the winners?";
|
||||
"notification_subtitle_end_bet_date %@" = "The %@ bet has reached its deadline. Go to the app to enter the winning answer.";
|
||||
|
||||
/// Empty Views
|
||||
|
||||
"empty_ranking_title" = "It's a bit empty around here";
|
||||
"empty_ranking_explain" = "Add some friends to display them in the leaderboard";
|
||||
"empty_bets_title"= "No bet matches your search";
|
||||
"empty_friends_title" = "You don't have any friends yet";
|
||||
"empty_friends_explain" = "Add them from this screen";
|
||||
|
||||
/// Error Messages
|
||||
|
||||
"error_title" = "Error: Failed to Load Content.";
|
||||
"error_message" = "The content you are trying to load could not be retrieved. Please check your internet connection and try again";
|
@ -0,0 +1,50 @@
|
||||
//
|
||||
// NotificationService.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Emre on 22/02/2024.
|
||||
//
|
||||
|
||||
import UserNotifications
|
||||
|
||||
struct NotificationItem {
|
||||
var title: String
|
||||
var content: String
|
||||
var interval: TimeInterval
|
||||
}
|
||||
|
||||
class NotificationService: ObservableObject {
|
||||
|
||||
@Published var notifications: [NotificationItem] = []
|
||||
|
||||
func scheduleNotifications() {
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
|
||||
if granted {
|
||||
print("Permission for notifications granted.")
|
||||
} else {
|
||||
print("Permission for notifications denied.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadNotifications() {
|
||||
for notification in notifications {
|
||||
scheduleNotification(with: notification)
|
||||
}
|
||||
}
|
||||
|
||||
func removeAllNotifications() {
|
||||
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
|
||||
}
|
||||
|
||||
func scheduleNotification(with item: NotificationItem) {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = item.title
|
||||
content.body = item.content
|
||||
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: item.interval, repeats: false)
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
|
||||
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
//
|
||||
// asyncCachedImage.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by Lucas Delanier on 06/06/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
struct AsyncCachedImage<ImageView: View, PlaceholderView: View>: View {
|
||||
// Input dependencies
|
||||
var url: URL?
|
||||
@ViewBuilder var content: (Image) -> ImageView
|
||||
@ViewBuilder var placeholder: () -> PlaceholderView
|
||||
|
||||
// Downloaded image
|
||||
@State var image: UIImage? = nil
|
||||
|
||||
init(
|
||||
url: URL?,
|
||||
@ViewBuilder content: @escaping (Image) -> ImageView,
|
||||
@ViewBuilder placeholder: @escaping () -> PlaceholderView
|
||||
) {
|
||||
self.url = url
|
||||
self.content = content
|
||||
self.placeholder = placeholder
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if let uiImage = image {
|
||||
content(Image(uiImage: uiImage))
|
||||
} else {
|
||||
placeholder()
|
||||
.onAppear {
|
||||
Task {
|
||||
image = await downloadPhoto()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Downloads if the image is not cached already
|
||||
// Otherwise returns from the cache
|
||||
private func downloadPhoto() async -> UIImage? {
|
||||
do {
|
||||
guard let url else { return nil }
|
||||
|
||||
// Check if the image is cached already
|
||||
if let cachedResponse = URLCache.shared.cachedResponse(for: .init(url: url)) {
|
||||
return UIImage(data: cachedResponse.data)
|
||||
} else {
|
||||
let (data, response) = try await URLSession.shared.data(from: url)
|
||||
|
||||
// Save returned image data into the cache
|
||||
URLCache.shared.storeCachedResponse(.init(response: response, data: data), for: .init(url: url))
|
||||
|
||||
guard let image = UIImage(data: data) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
} catch {
|
||||
print("Error downloading: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
//
|
||||
// ProfileView.swift
|
||||
// AllIn
|
||||
//
|
||||
// Created by étudiant on 26/05/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ProfileView: View {
|
||||
|
||||
@Binding var showMenu: Bool
|
||||
|
||||
let parameters: [(title: String, icon: String, backgroundColor: Color, itemColor: Color)] = [
|
||||
(title: "Changer de pseudo", icon: "person", backgroundColor: AllInColors.componentBackgroundColor, itemColor: AllInColors.secondaryTextColor),
|
||||
(title: "Changer le mot de passe", icon: "lock", backgroundColor: AllInColors.componentBackgroundColor, itemColor: AllInColors.secondaryTextColor),
|
||||
(title: "Supprimer le compte", icon: "trash", backgroundColor: AllInColors.componentBackgroundColor, itemColor: AllInColors.secondaryTextColor),
|
||||
(title: "Déconnexion", icon: "xmark", backgroundColor: AllInColors.lightPurpleColor, itemColor: .white)
|
||||
]
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
TopBar(showMenu: self.$showMenu)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Image("defaultUserImage")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 80, height: 80)
|
||||
.cornerRadius(180)
|
||||
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text("Pseudo")
|
||||
.fontWeight(.bold)
|
||||
.font(.system(size: 20))
|
||||
|
||||
HStack(spacing: 15) {
|
||||
VStack(){
|
||||
Text("114")
|
||||
.fontWeight(.heavy)
|
||||
.font(.system(size: 15))
|
||||
Text(String(localized: "drawer_bets"))
|
||||
.fontWeight(.regular)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(AllInColors.grey600Color)
|
||||
}
|
||||
VStack(){
|
||||
Text("343")
|
||||
.fontWeight(.heavy)
|
||||
.font(.system(size: 15))
|
||||
Text(String(localized: "drawer_best_win"))
|
||||
.fontWeight(.regular)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(AllInColors.grey600Color)
|
||||
}
|
||||
VStack(){
|
||||
Text("5")
|
||||
.fontWeight(.heavy)
|
||||
.font(.system(size: 15))
|
||||
Text(String(localized: "drawer_nb_friends"))
|
||||
.fontWeight(.regular)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(AllInColors.grey600Color)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.leading, 10)
|
||||
}
|
||||
Text("Compte")
|
||||
.fontWeight(.bold)
|
||||
.font(.system(size: 16))
|
||||
.padding(.leading, 13)
|
||||
.padding(.top, 35)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
ForEach(0..<parameters.count, id: \.self) { index in
|
||||
HStack {
|
||||
Image(systemName: parameters[index].icon)
|
||||
.foregroundColor(parameters[index].itemColor)
|
||||
.padding(.leading, 13)
|
||||
Text(parameters[index].title)
|
||||
.font(.system(size: 14))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(parameters[index].itemColor)
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.resizable()
|
||||
.frame(width: 8, height: 12)
|
||||
.foregroundColor(parameters[index].itemColor)
|
||||
.padding(.trailing, 14)
|
||||
}
|
||||
.padding(.vertical, 15)
|
||||
.background(parameters[index].backgroundColor)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.cornerRadius(7)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.top, geometry.size.width*0.15)
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
.edgesIgnoringSafeArea(.bottom).background(AllInColors.backgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ProfileView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ProfileView(showMenu: .constant(false))
|
||||
}
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
//
|
||||
// AllInCoinsWidgets.swift
|
||||
// AllInCoinsWidgets
|
||||
//
|
||||
// Created by Emre on 01/03/2024.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct AllInCoinsWidgetEntryView : View {
|
||||
var user: Provider.Entry
|
||||
|
||||
@Environment(\.widgetFamily) var family
|
||||
|
||||
var body: some View {
|
||||
switch family {
|
||||
case .systemSmall:
|
||||
SmallSizedWidget()
|
||||
case .systemMedium:
|
||||
MediumSizedWidget()
|
||||
case .accessoryRectangular:
|
||||
LockScreenRectangularWidget()
|
||||
case .accessoryInline:
|
||||
LockScreenInlineWidget()
|
||||
case .accessoryCircular:
|
||||
LockScreenCircularWidget()
|
||||
default:
|
||||
Text("Not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func LockScreenInlineWidget() -> some View {
|
||||
if user.connected {
|
||||
Text("💸 " + user.coins.abbreviated())
|
||||
.bold()
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
|
||||
} else {
|
||||
Text("💸 Connexion")
|
||||
.bold()
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func LockScreenCircularWidget() -> some View {
|
||||
if user.connected {
|
||||
Gauge(value: Float(user.coins) / 10000) {
|
||||
Text(user.coins.abbreviated())
|
||||
}
|
||||
.gaugeStyle(.accessoryCircular)
|
||||
} else {
|
||||
ZStack {
|
||||
Image("launchScreenImage")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
|
||||
VStack {
|
||||
Text("Vide")
|
||||
Text("All In")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func LockScreenRectangularWidget() -> some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Image("allcoinIcon")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20)
|
||||
|
||||
if user.connected {
|
||||
VStack(alignment: .leading) {
|
||||
Text(user.coins.abbreviated())
|
||||
.fontWeight(.bold)
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
|
||||
Text(user.username.description.capitalized + ", venez jouez !")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
} else {
|
||||
Text("Connexion")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
|
||||
Text("All In")
|
||||
.font(.callout)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func SmallSizedWidget() -> some View {
|
||||
ZStack {
|
||||
Image("launchScreenImage")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Image("allcoinWhiteIcon")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 30, height: 30)
|
||||
|
||||
if user.connected {
|
||||
VStack(alignment: .leading) {
|
||||
Text(user.coins.abbreviated())
|
||||
.fontWeight(.bold)
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
|
||||
Text(user.username.description.capitalized + ", venez jouez !")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
} else {
|
||||
Text("Connexion")
|
||||
.font(.headline)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func MediumSizedWidget() -> some View {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.fill(LinearGradient(gradient: Gradient(colors: [Color("PinkAccentColor"), Color("PurpleAccentColor"), Color("BlueAccentColor")]), startPoint: .top, endPoint: .bottom))
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Image("allcoinWhiteIcon")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 45, height: 45)
|
||||
|
||||
if user.connected {
|
||||
VStack(alignment: .leading) {
|
||||
Text(user.coins.abbreviated())
|
||||
.fontWeight(.bold)
|
||||
.font(.title)
|
||||
.foregroundColor(.white)
|
||||
|
||||
Text(user.username.description.capitalized + ", venez jouez !")
|
||||
.font(.caption)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
} else {
|
||||
Text("Connexion")
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AllInCoinsWidget: Widget {
|
||||
let kind: String = "AllInCoinsWidgets"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
|
||||
AllInCoinsWidgetEntryView(user: entry)
|
||||
}
|
||||
.configurationDisplayName("All Coins")
|
||||
.description("Visualiser rapidement votre nombre de all coins restants.")
|
||||
.supportedFamilies([.systemSmall, .systemMedium, .accessoryRectangular, .accessoryInline, .accessoryCircular])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct AllInCoinsWidget_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AllInCoinsWidgetEntryView(user: User(date: Date(), coins: 1000, username: "test", connected: false))
|
||||
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||
|
||||
AllInCoinsWidgetEntryView(user: User(date: Date(), coins: 1000, username: "test", connected: true))
|
||||
.previewContext(WidgetPreviewContext(family: .systemMedium))
|
||||
}
|
||||
}
|
||||
|
||||
extension Int {
|
||||
func abbreviated() -> String {
|
||||
if self < 1000 {
|
||||
return "\(self)"
|
||||
}
|
||||
let abbreviations = ["", "K", "M", "B", "T"]
|
||||
var num = Double(self)
|
||||
var index = 0
|
||||
while num >= 1000 && index < abbreviations.count - 1 {
|
||||
num /= 1000
|
||||
index += 1
|
||||
}
|
||||
return String(format: "%.1f%@", num, abbreviations[index])
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>INEnums</key>
|
||||
<array/>
|
||||
<key>INIntentDefinitionModelVersion</key>
|
||||
<string>1.2</string>
|
||||
<key>INIntentDefinitionNamespace</key>
|
||||
<string>88xZPY</string>
|
||||
<key>INIntentDefinitionSystemVersion</key>
|
||||
<string>20A294</string>
|
||||
<key>INIntentDefinitionToolsBuildVersion</key>
|
||||
<string>12A6144</string>
|
||||
<key>INIntentDefinitionToolsVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>INIntents</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentCategory</key>
|
||||
<string>information</string>
|
||||
<key>INIntentDescriptionID</key>
|
||||
<string>tVvJ9c</string>
|
||||
<key>INIntentEligibleForWidgets</key>
|
||||
<true/>
|
||||
<key>INIntentIneligibleForSuggestions</key>
|
||||
<true/>
|
||||
<key>INIntentName</key>
|
||||
<string>Configuration</string>
|
||||
<key>INIntentResponse</key>
|
||||
<dict>
|
||||
<key>INIntentResponseCodes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentResponseCodeName</key>
|
||||
<string>success</string>
|
||||
<key>INIntentResponseCodeSuccess</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentResponseCodeName</key>
|
||||
<string>failure</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>INIntentTitle</key>
|
||||
<string>Configuration</string>
|
||||
<key>INIntentTitleID</key>
|
||||
<string>gpCwrM</string>
|
||||
<key>INIntentType</key>
|
||||
<string>Custom</string>
|
||||
<key>INIntentVerb</key>
|
||||
<string>View</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INTypes</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,17 @@
|
||||
//
|
||||
// AllInWidgetsBundle.swift
|
||||
// AllInWidgets
|
||||
//
|
||||
// Created by Emre on 01/03/2024.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct AllInWidgetsBundle: WidgetBundle {
|
||||
var body: some Widget {
|
||||
AllInCoinsWidget()
|
||||
AllInWidgetsLiveActivity()
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
//
|
||||
// AllInWidgetsLiveActivity.swift
|
||||
// AllInWidgets
|
||||
//
|
||||
// Created by Emre on 01/03/2024.
|
||||
//
|
||||
|
||||
import ActivityKit
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct AllInWidgetsAttributes: ActivityAttributes {
|
||||
public struct ContentState: Codable, Hashable {
|
||||
// Dynamic stateful properties about your activity go here!
|
||||
var value: Int
|
||||
}
|
||||
|
||||
// Fixed non-changing properties about your activity go here!
|
||||
var name: String
|
||||
}
|
||||
|
||||
struct AllInWidgetsLiveActivity: Widget {
|
||||
var body: some WidgetConfiguration {
|
||||
ActivityConfiguration(for: AllInWidgetsAttributes.self) { context in
|
||||
// Lock screen/banner UI goes here
|
||||
VStack {
|
||||
Text("Hello")
|
||||
}
|
||||
.activityBackgroundTint(Color.cyan)
|
||||
.activitySystemActionForegroundColor(Color.black)
|
||||
|
||||
} dynamicIsland: { context in
|
||||
DynamicIsland {
|
||||
// Expanded UI goes here. Compose the expanded UI through
|
||||
// various regions, like leading/trailing/center/bottom
|
||||
DynamicIslandExpandedRegion(.leading) {
|
||||
Text("Leading")
|
||||
}
|
||||
DynamicIslandExpandedRegion(.trailing) {
|
||||
Text("Trailing")
|
||||
}
|
||||
DynamicIslandExpandedRegion(.bottom) {
|
||||
Text("Bottom")
|
||||
// more content
|
||||
}
|
||||
} compactLeading: {
|
||||
Text("L")
|
||||
} compactTrailing: {
|
||||
Text("T")
|
||||
} minimal: {
|
||||
Text("Min")
|
||||
}
|
||||
.widgetURL(URL(string: "http://www.apple.com"))
|
||||
.keylineTint(Color.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AllInWidgetsLiveActivity_Previews: PreviewProvider {
|
||||
static let attributes = AllInWidgetsAttributes(name: "Me")
|
||||
static let contentState = AllInWidgetsAttributes.ContentState(value: 3)
|
||||
|
||||
static var previews: some View {
|
||||
attributes
|
||||
.previewContext(contentState, viewKind: .dynamicIsland(.compact))
|
||||
.previewDisplayName("Island Compact")
|
||||
attributes
|
||||
.previewContext(contentState, viewKind: .dynamicIsland(.expanded))
|
||||
.previewDisplayName("Island Expanded")
|
||||
attributes
|
||||
.previewContext(contentState, viewKind: .dynamicIsland(.minimal))
|
||||
.previewDisplayName("Minimal")
|
||||
attributes
|
||||
.previewContext(contentState, viewKind: .content)
|
||||
.previewDisplayName("Notification")
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFB",
|
||||
"green" : "0x7B",
|
||||
"red" : "0x7F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Logo.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 267 KiB |
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.933",
|
||||
"green" : "0.624",
|
||||
"red" : "0.098"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.933",
|
||||
"green" : "0.624",
|
||||
"red" : "0.098"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.537",
|
||||
"green" : "0.165",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.537",
|
||||
"green" : "0.165",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.953",
|
||||
"green" : "0.490",
|
||||
"red" : "0.667"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.953",
|
||||
"green" : "0.490",
|
||||
"red" : "0.667"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "allcoin.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 5.6 KiB |
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "allcoinWhite.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.7 KiB |
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "launchScreen.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 193 KiB |
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>codefirst.iut.uca.fr</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,78 @@
|
||||
//
|
||||
// Provider.swift
|
||||
// AllInWidgetsExtension
|
||||
//
|
||||
// Created by Emre on 01/03/2024.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import Intents
|
||||
|
||||
let allInApi = "https://codefirst.iut.uca.fr/containers/AllDev-api/"
|
||||
|
||||
struct Provider: IntentTimelineProvider {
|
||||
|
||||
func placeholder(in context: Context) -> User {
|
||||
User(date: Date())
|
||||
}
|
||||
|
||||
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (User) -> ()) {
|
||||
var entry = User(date: Date())
|
||||
entry.connected = true
|
||||
entry.username = "toto"
|
||||
entry.coins = 50000
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<User>) -> ()) {
|
||||
let currentDate = Date()
|
||||
|
||||
Task {
|
||||
if var userData = try? await fetchData() {
|
||||
userData.date = currentDate
|
||||
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
|
||||
let timeline = Timeline(entries: [userData], policy: .after(nextUpdate))
|
||||
completion(timeline)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchData() async throws -> User {
|
||||
let defaults = UserDefaults(suiteName: "group.alldev.AllIn")
|
||||
|
||||
guard let token = defaults?.string(forKey: "authenticationRefresh") else {
|
||||
return User()
|
||||
}
|
||||
|
||||
let url = URL(string: allInApi + "users/token")!
|
||||
var request = URLRequest(url: url)
|
||||
|
||||
request.httpMethod = "GET"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
|
||||
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
|
||||
if let userData = try? JSONDecoder().decode(User.self, from: data) {
|
||||
var updatedUser = userData
|
||||
updatedUser.connected = true
|
||||
return updatedUser
|
||||
}
|
||||
}
|
||||
return User()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct User: TimelineEntry, Codable {
|
||||
var date: Date = .init()
|
||||
var coins: Int = 0
|
||||
var username: String = ""
|
||||
var connected: Bool = false
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case coins = "nbCoins"
|
||||
case username = "username"
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|