-
-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Sticker Packs * Update Swiftcord.xcodeproj/project.pbxproj Co-authored-by: CryptoAlgo <[email protected]> * Update Swiftcord.xcodeproj/project.pbxproj Co-authored-by: CryptoAlgo <[email protected]> * Update Swiftcord.xcodeproj/project.pbxproj Co-authored-by: CryptoAlgo <[email protected]> * Update Swiftcord.xcodeproj/project.pbxproj Co-authored-by: CryptoAlgo <[email protected]> * Update Swiftcord/Views/Message/MessageStickerView.swift Co-authored-by: CryptoAlgo <[email protected]> * Linting * Remove DiscordKit Reference * Update Package.resolved * Update Package.resolved * Update project.pbxproj * DiscordKit main branch * Update project.pbxproj --------- Co-authored-by: CryptoAlgo <[email protected]>
1 parent
3a6f06a
commit 7651c43
Showing
5 changed files
with
310 additions
and
186 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// | ||
// BasicStickerView.swift | ||
// Swiftcord | ||
// | ||
// Created by Vincent Kwok on 23/2/22. | ||
// | ||
import SwiftUI | ||
import Lottie | ||
import CachedAsyncImage | ||
import DiscordKitCore | ||
import DiscordKit | ||
|
||
struct StickerLoadingView: View { | ||
let size: Double | ||
var body: some View { | ||
RoundedRectangle(cornerRadius: 12) | ||
.fill(.gray.opacity(Double.random(in: 0.15...0.3))) | ||
.frame(width: size, height: size) | ||
} | ||
} | ||
|
||
struct StickerErrorView: View { | ||
let size: Double | ||
var body: some View { | ||
Image(systemName: "square.slash") | ||
.font(.system(size: size - 10)) | ||
.opacity(0.5) | ||
.frame(width: size, height: size) | ||
} | ||
} | ||
|
||
enum StickerPlayCondition { | ||
case always | ||
case onHover | ||
case useDefault | ||
} | ||
|
||
// Most basic sticker player | ||
struct StickerItemView: View { | ||
let sticker: StickerItem | ||
let size: Double // Width and height of sticker | ||
let play: StickerPlayCondition | ||
@State private var error = false | ||
@State private var animation: Lottie.LottieAnimation? | ||
@State private var hovered = false | ||
@AppStorage("stickerAlwaysAnim") private var alwaysAnimStickers = true | ||
private func playAnimation(value: Bool) { | ||
// Without this check, the sticker animation restarts if it's hovered | ||
if (play == .useDefault && !alwaysAnimStickers) || play == .onHover { | ||
hovered = value | ||
} | ||
} | ||
|
||
var body: some View { | ||
if error { | ||
StickerErrorView(size: size) | ||
} else { | ||
switch sticker.format_type { | ||
case .png: | ||
// Literally a walk in the park compared to lottie | ||
AsyncImage(url: URL(string: "\(DiscordKitConfig.default.cdnURL)stickers/\(sticker.id).png")!) { phase in | ||
switch phase { | ||
case .empty: StickerLoadingView(size: size) | ||
case .success(let image): image.resizable().scaledToFill() | ||
case .failure: StickerErrorView(size: size) | ||
default: StickerErrorView(size: size) | ||
} | ||
} | ||
.frame(width: size, height: size) | ||
.clipShape(RoundedRectangle(cornerRadius: 7)) | ||
case .lottie: | ||
if animation == nil { | ||
StickerLoadingView(size: size).onAppear { | ||
Lottie.LottieAnimation.loadedFrom( | ||
url: URL(string: "\(DiscordKitConfig.default.cdnURL)stickers/\(sticker.id).json")!, | ||
closure: { anim in | ||
guard let anim = anim else { | ||
error = true | ||
return | ||
} | ||
animation = anim | ||
}, | ||
animationCache: Lottie.DefaultAnimationCache.sharedCache | ||
) | ||
}.transition(.customOpacity) | ||
} else { | ||
LottieView( | ||
animation: animation!, | ||
play: .constant(play == .always || (play == .useDefault && alwaysAnimStickers) || hovered), | ||
width: size, | ||
height: size | ||
) | ||
.lottieLoopMode(.loop) | ||
.frame(width: size, height: size) | ||
.transition(.customOpacity) | ||
.onHover(perform: playAnimation) | ||
} | ||
default: | ||
// Well it doesn't animate for some reason | ||
CachedAsyncImage(url: URL(string: "\(DiscordKitConfig.default.cdnURL)stickers/\(sticker.id).png?passthrough=true")!) { phase in | ||
switch phase { | ||
case .empty: StickerLoadingView(size: size) | ||
case .success(let image): image.resizable().scaledToFill() | ||
case .failure: StickerErrorView(size: size) | ||
default: StickerErrorView(size: size) | ||
} | ||
} | ||
.frame(width: size, height: size) | ||
.clipShape(RoundedRectangle(cornerRadius: 7)) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// | ||
// MessageStickerView.swift | ||
// Swiftcord | ||
// | ||
// Created by Vincent Kwok on 23/2/22. | ||
// | ||
|
||
import SwiftUI | ||
import Lottie | ||
import DiscordKitCore | ||
import DiscordKit | ||
import CachedAsyncImage | ||
|
||
struct StickerPackView: View { | ||
let pack: StickerPack | ||
@Binding var packPresenting: Bool | ||
@State private var stickerHovered: Int? | ||
@State private var listHovered: Bool = false | ||
var body: some View { | ||
VStack { | ||
if pack.banner_asset_id != nil { | ||
VStack { | ||
CachedAsyncImage(url: pack.banner_asset_id?.stickerPackBannerURL(with: .webp, size: 1024)) { image in | ||
image.resizable().scaledToFill() | ||
} placeholder: { ProgressView().progressViewStyle(.circular)} | ||
}.frame(height: 100) | ||
} | ||
VStack { | ||
HStack(spacing: 15) { | ||
// Back button | ||
Button { | ||
packPresenting = false | ||
} label: {Image(systemName: "arrow.left")} | ||
.controlSize(.large) | ||
Text(pack.name).font(.title).fontWeight(.bold) | ||
Spacer() | ||
Text(" x\(pack.stickers.count)") | ||
.font(.system(size: 16)) | ||
.opacity(0.7) | ||
} | ||
Divider() | ||
Text(pack.description) | ||
.fixedSize(horizontal: false, vertical: true) | ||
.frame(maxWidth: .infinity, alignment: .leading) | ||
List { | ||
ForEach(0..<Int(ceil(Double(pack.stickers.count)/3.0)), id: \.self) { row in | ||
HStack { | ||
ForEach(0..<min(3, Int(pack.stickers.count - row * 3)), id: \.self) { column in | ||
let index: Int = row*3+column | ||
StickerItemView(sticker: pack.stickers[index], size: 95, play: .onHover) | ||
.onHover { | ||
stickerHovered = $0 ? index : nil | ||
} | ||
.scaleEffect((stickerHovered == index) ? 1.1 : 1.0) | ||
.opacity((stickerHovered == index) ? 1 : 0.5) | ||
} | ||
}.frame(maxWidth: .infinity) | ||
} | ||
} | ||
.frame(height: 320) | ||
.onHover {listHovered = $0} | ||
if listHovered { | ||
Text(stickerHovered == nil ? "" : pack.stickers[stickerHovered!].name) | ||
.frame(height: 30) | ||
.font(.title3) | ||
.transition(.opacity) | ||
} else { | ||
HStack { | ||
Image("NitroSubscriber") | ||
Text("You need a Nitro subscription to send stickers from this pack.") | ||
.fixedSize(horizontal: false, vertical: true) | ||
.frame(height: 30) | ||
} | ||
.transition(.opacity) | ||
} | ||
|
||
}.padding(14) | ||
.animation(Animation.easeOut(duration: 0.1), value: stickerHovered) | ||
.animation(Animation.linear(duration: 0.1), value: listHovered) | ||
} | ||
.frame(width: 360) | ||
} | ||
} | ||
|
||
struct CustomButtonStyle: ButtonStyle { | ||
func makeBody(configuration: Self.Configuration) -> some View { | ||
configuration.label | ||
.background(Color.blue) | ||
.cornerRadius(10.0) | ||
.padding() | ||
.contentShape(Rectangle()) | ||
} | ||
} | ||
|
||
struct MessageStickerView: View { | ||
let sticker: StickerItem | ||
@State private var infoShow = false | ||
@State private var error = false | ||
@State private var fullSticker: Sticker? | ||
@State public var packPresenting = false | ||
@State private var fullStickerPack: StickerPack? | ||
|
||
private func openPopoverEvt() { | ||
AnalyticsWrapper.event(type: .openPopout, properties: [ | ||
"type": "Sticker Popout", | ||
"sticker_pack_id": fullSticker?.pack_id ?? "", | ||
"sticker_id": fullSticker?.id ?? "" | ||
]) | ||
} | ||
private func loadStickerPack() async -> StickerPack? { | ||
guard let stickerPacks: [StickerPack] = try? await restAPI.listNitroStickerPacks() else {return nil} | ||
for pack in stickerPacks where pack.id == fullSticker!.pack_id { | ||
return pack | ||
} | ||
return nil | ||
} | ||
|
||
var body: some View { | ||
Button { | ||
if fullSticker == nil { | ||
Task { | ||
fullSticker = try await restAPI.getSticker(sticker.id) | ||
openPopoverEvt() | ||
} | ||
} else { | ||
openPopoverEvt() | ||
} | ||
infoShow.toggle() | ||
packPresenting = false | ||
|
||
} label: { | ||
StickerItemView(sticker: sticker, size: 160, play: .useDefault) | ||
.frame(width: 160, height: 160) | ||
} | ||
.buttonStyle(.borderless) | ||
.popover(isPresented: $infoShow, arrowEdge: .trailing) { | ||
if packPresenting { | ||
if let fullStickerPack = fullStickerPack { | ||
StickerPackView(pack: fullStickerPack, packPresenting: $packPresenting) | ||
} | ||
} else { | ||
VStack(alignment: .leading, spacing: 14) { | ||
if let fullSticker = fullSticker { | ||
StickerItemView(sticker: sticker, size: 240, play: .always) | ||
Divider() | ||
Text(fullSticker.name).font(.title2).fontWeight(.bold) | ||
if let description = fullSticker.description { | ||
Text(description).padding(.top, -8) | ||
} | ||
if sticker.format_type == .aPNG { | ||
Text("Sorry, aPNG stickers can't be played (yet)").font(.footnote) | ||
} | ||
if fullSticker.pack_id != nil { | ||
Button { | ||
Task { | ||
fullStickerPack = await loadStickerPack() | ||
packPresenting = true | ||
} | ||
} label: { | ||
Label("View Sticker Pack", systemImage: "square.on.square") | ||
.frame(maxWidth: .infinity) | ||
} | ||
.buttonStyle(FlatButtonStyle()) | ||
.controlSize(.small) | ||
} | ||
} else { | ||
Text("Loading sticker...").font(.headline) | ||
ProgressView() | ||
.progressViewStyle(.linear) | ||
.frame(width: 240) | ||
.tint(.blue) | ||
} | ||
} | ||
.padding(14) | ||
.frame(width: 268) | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct StickerView_Previews: PreviewProvider { | ||
static var previews: some View { | ||
// MessageStickerView(sticker: StickerItem(id: )) | ||
EmptyView() | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.