mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Messed up some stuff
This commit is contained in:
parent
f8466c2027
commit
56cb69d856
7 changed files with 85 additions and 30 deletions
|
|
@ -9171,6 +9171,12 @@
|
|||
},
|
||||
"Live Performance Data" : {
|
||||
|
||||
},
|
||||
"Loop Mode" : {
|
||||
|
||||
},
|
||||
"Loop Mode:" : {
|
||||
|
||||
},
|
||||
"Lottie JSON URL" : {
|
||||
"localizations" : {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ struct IdleAnimationView: View {
|
|||
Group {
|
||||
if let animation = selectedAnimation {
|
||||
AnimationContentView(animation: animation)
|
||||
.id("\(animation.id)-\(animation.hashValue)") // Force complete recreation when ANY property changes
|
||||
} else {
|
||||
// Fallback to original face if nothing selected
|
||||
MinimalFaceFeatures(height: 20, width: 30)
|
||||
|
|
@ -38,8 +39,9 @@ private struct AnimationContentView: View {
|
|||
LottieView(state: LUStateData(
|
||||
type: .loadedFrom(url),
|
||||
speed: animation.speed,
|
||||
loopMode: .loop
|
||||
loopMode: config.loopMode.lottieLoopMode
|
||||
))
|
||||
.id(animation.id) // Force reload when animation changes
|
||||
.frame(
|
||||
width: config.cropWidth * config.scale,
|
||||
height: config.cropHeight * config.scale
|
||||
|
|
@ -55,8 +57,9 @@ private struct AnimationContentView: View {
|
|||
LottieView(state: LUStateData(
|
||||
type: .loadedFrom(url),
|
||||
speed: animation.speed,
|
||||
loopMode: .loop
|
||||
loopMode: config.loopMode.lottieLoopMode
|
||||
))
|
||||
.id(animation.id) // Force reload when animation changes
|
||||
.frame(
|
||||
width: config.cropWidth * config.scale,
|
||||
height: config.cropHeight * config.scale
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ struct AnimationEditorView: View {
|
|||
@State private var opacity: CGFloat = 1.0
|
||||
@State private var paddingBottom: CGFloat = 0
|
||||
@State private var expandWithAnimation: Bool = false
|
||||
@State private var loopMode: AnimationLoopMode = .loop
|
||||
|
||||
@State private var showingError = false
|
||||
@State private var errorMessage = ""
|
||||
|
|
@ -57,6 +58,7 @@ struct AnimationEditorView: View {
|
|||
_opacity = State(initialValue: config.opacity)
|
||||
_paddingBottom = State(initialValue: config.paddingBottom)
|
||||
_expandWithAnimation = State(initialValue: config.expandWithAnimation)
|
||||
_loopMode = State(initialValue: config.loopMode)
|
||||
} else {
|
||||
let fileName = sourceURL.deletingPathExtension().lastPathComponent
|
||||
_name = State(initialValue: fileName)
|
||||
|
|
@ -107,7 +109,7 @@ struct AnimationEditorView: View {
|
|||
LottieView(state: LUStateData(
|
||||
type: .loadedFrom(sourceURL),
|
||||
speed: speed,
|
||||
loopMode: .loop
|
||||
loopMode: loopMode.lottieLoopMode
|
||||
))
|
||||
.frame(width: cropWidth * scale * previewScale, height: cropHeight * scale * previewScale)
|
||||
.offset(x: offsetX * previewScale, y: offsetY * previewScale)
|
||||
|
|
@ -313,6 +315,18 @@ struct AnimationEditorView: View {
|
|||
.buttonStyle(.bordered)
|
||||
.controlSize(.small)
|
||||
}
|
||||
|
||||
// Loop Mode
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Loop Mode:")
|
||||
.font(.subheadline)
|
||||
Picker("Loop Mode", selection: $loopMode) {
|
||||
ForEach(AnimationLoopMode.allCases, id: \.self) { mode in
|
||||
Text(mode.rawValue).tag(mode)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
|
@ -430,7 +444,9 @@ struct AnimationEditorView: View {
|
|||
}
|
||||
.padding()
|
||||
}
|
||||
.frame(idealWidth: 900, idealHeight: 650)
|
||||
.frame(minWidth: 800, minHeight: 600)
|
||||
.fixedSize()
|
||||
.alert("Import Error", isPresented: $showingError) {
|
||||
Button("OK") { }
|
||||
} message: {
|
||||
|
|
@ -455,15 +471,21 @@ struct AnimationEditorView: View {
|
|||
rotation: rotation,
|
||||
opacity: opacity,
|
||||
paddingBottom: paddingBottom,
|
||||
expandWithAnimation: expandWithAnimation
|
||||
expandWithAnimation: expandWithAnimation,
|
||||
loopMode: loopMode
|
||||
)
|
||||
|
||||
// If editing existing animation, update it directly
|
||||
// If editing existing animation, create a NEW animation object with updated values
|
||||
if let existing = existingAnimation {
|
||||
var updatedAnimation = existing
|
||||
updatedAnimation.name = name
|
||||
updatedAnimation.speed = speed
|
||||
updatedAnimation.transformConfig = config
|
||||
// Create completely new animation to force view refresh
|
||||
let updatedAnimation = CustomIdleAnimation(
|
||||
id: existing.id, // Keep same ID so it replaces the right one
|
||||
name: name,
|
||||
source: existing.source,
|
||||
speed: speed,
|
||||
isBuiltIn: existing.isBuiltIn,
|
||||
transformConfig: config
|
||||
)
|
||||
animation = updatedAnimation
|
||||
dismiss()
|
||||
return
|
||||
|
|
|
|||
|
|
@ -123,19 +123,20 @@ struct IdleAnimationsSettingsSection: View {
|
|||
animation: $editedAnimation,
|
||||
existingAnimation: editingExistingAnimation
|
||||
)
|
||||
.frame(minWidth: 800, minHeight: 600)
|
||||
.interactiveDismissDisabled(false)
|
||||
.onChange(of: editedAnimation) { oldValue, newValue in
|
||||
if let animation = newValue {
|
||||
// Animation was successfully imported/edited via editor
|
||||
if let existingAnim = editingExistingAnimation {
|
||||
// Editing existing animation - update it
|
||||
if var animations = customIdleAnimations as? [CustomIdleAnimation],
|
||||
let index = animations.firstIndex(where: { $0.id == existingAnim.id }) {
|
||||
var animations = Defaults[.customIdleAnimations]
|
||||
if let index = animations.firstIndex(where: { $0.id == existingAnim.id }) {
|
||||
animations[index] = animation
|
||||
customIdleAnimations = animations
|
||||
Defaults[.customIdleAnimations] = animations
|
||||
|
||||
// Update selection if this was the selected animation
|
||||
if selectedIdleAnimation?.id == existingAnim.id {
|
||||
selectedIdleAnimation = animation
|
||||
Defaults[.selectedIdleAnimation] = animation
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -183,6 +184,7 @@ struct IdleAnimationsSettingsSection: View {
|
|||
editorSourceURL = url
|
||||
editorIsRemote = false
|
||||
editedAnimation = nil
|
||||
editingExistingAnimation = nil // Reset to nil for new imports
|
||||
showingEditor = true
|
||||
|
||||
case .failure(let error):
|
||||
|
|
@ -202,6 +204,7 @@ struct IdleAnimationsSettingsSection: View {
|
|||
editorSourceURL = url
|
||||
editorIsRemote = true
|
||||
editedAnimation = nil
|
||||
editingExistingAnimation = nil // Reset to nil for new imports
|
||||
showingURLSheet = false
|
||||
showingEditor = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,29 +47,29 @@ class IdleAnimationManager {
|
|||
animations.append(contentsOf: bundledAnimations)
|
||||
}
|
||||
|
||||
// Load previously imported user animations from storage
|
||||
if let userAnimations = loadStoredUserAnimations() {
|
||||
animations.append(contentsOf: userAnimations)
|
||||
}
|
||||
// Get existing animations
|
||||
var existing = Defaults[.customIdleAnimations]
|
||||
|
||||
// Only set if empty (first launch)
|
||||
if Defaults[.customIdleAnimations].isEmpty {
|
||||
if existing.isEmpty {
|
||||
// First launch - set everything
|
||||
Defaults[.customIdleAnimations] = animations
|
||||
Defaults[.selectedIdleAnimation] = builtInFace // Default to built-in face
|
||||
print("✅ [IdleAnimationManager] Initialized with \(animations.count) animations")
|
||||
Defaults[.selectedIdleAnimation] = builtInFace
|
||||
print("✅ [IdleAnimationManager] First launch: Initialized with \(animations.count) animations")
|
||||
} else {
|
||||
// Merge: add any new bundled animations that aren't already in the list
|
||||
var existing = Defaults[.customIdleAnimations]
|
||||
let existingNames = Set(existing.map { $0.name })
|
||||
// Subsequent launch - ensure all bundled animations are present
|
||||
let existingIDs = Set(existing.map { $0.id })
|
||||
let existingNames = Set(existing.filter { $0.isBuiltIn }.map { $0.name })
|
||||
|
||||
for newAnim in animations where newAnim.isBuiltIn {
|
||||
if !existingNames.contains(newAnim.name) {
|
||||
existing.append(newAnim)
|
||||
// Add any missing bundled animations
|
||||
for bundledAnim in animations where bundledAnim.isBuiltIn {
|
||||
if !existingNames.contains(bundledAnim.name) {
|
||||
existing.insert(bundledAnim, at: existing.firstIndex(where: { !$0.isBuiltIn }) ?? existing.count)
|
||||
print("➕ [IdleAnimationManager] Added missing bundled animation: \(bundledAnim.name)")
|
||||
}
|
||||
}
|
||||
|
||||
Defaults[.customIdleAnimations] = existing
|
||||
print("✅ [IdleAnimationManager] Updated with \(existing.count) animations")
|
||||
print("✅ [IdleAnimationManager] Subsequent launch: \(existing.count) total animations (\(animations.count - 1) bundled + built-in face)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,14 @@ public final class ImageService: ImageServiceProtocol {
|
|||
|
||||
private init() {
|
||||
let config = URLSessionConfiguration.default
|
||||
|
||||
// Create proper cache directory path
|
||||
let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
|
||||
let artworkCachePath = cacheDir.appendingPathComponent("artwork_cache").path
|
||||
|
||||
let cache = URLCache(memoryCapacity: 50 * 1024 * 1024, // 50MB
|
||||
diskCapacity: 100 * 1024 * 1024, // 100MB
|
||||
diskPath: "artwork_cache")
|
||||
diskPath: artworkCachePath)
|
||||
config.urlCache = cache
|
||||
config.timeoutIntervalForRequest = 15
|
||||
config.timeoutIntervalForResource = 30
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import SwiftUI
|
||||
import Defaults
|
||||
import Lottie
|
||||
|
||||
private let availableDirectories = FileManager
|
||||
.default
|
||||
|
|
@ -54,10 +55,25 @@ struct AnimationTransformConfig: Codable, Hashable, Equatable {
|
|||
var opacity: CGFloat = 1.0
|
||||
var paddingBottom: CGFloat = 0 // Allow adjustment to fill notch from bottom
|
||||
var expandWithAnimation: Bool = false // Whether notch should expand horizontally with animation
|
||||
var loopMode: AnimationLoopMode = .loop // Loop mode for animation
|
||||
|
||||
static let `default` = AnimationTransformConfig()
|
||||
}
|
||||
|
||||
enum AnimationLoopMode: String, Codable, CaseIterable {
|
||||
case loop = "Loop"
|
||||
case playOnce = "Play Once"
|
||||
case autoReverse = "Auto Reverse"
|
||||
|
||||
var lottieLoopMode: LottieLoopMode {
|
||||
switch self {
|
||||
case .loop: return .loop
|
||||
case .playOnce: return .playOnce
|
||||
case .autoReverse: return .autoReverse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AnimationSource: Codable, Hashable, Equatable {
|
||||
case lottieFile(URL) // Local file (in app support or bundle)
|
||||
case lottieURL(URL) // Remote URL
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue