mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '4b34a6643f212dab03affe205b8636ae8a6c687e'
Update widget
This commit is contained in:
commit
fa3be5d99b
159
Makefile
159
Makefile
@ -1,159 +0,0 @@
|
||||
.PHONY : kill_xcode clean bazel_app_debug_arm64 bazel_app_debug_sim_arm64 bazel_app_arm64 bazel_app_armv7 bazel_app check_sandbox_debug_build bazel_project bazel_project_noextensions
|
||||
|
||||
APP_VERSION="7.3"
|
||||
CORE_COUNT=$(shell sysctl -n hw.logicalcpu)
|
||||
CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1)
|
||||
|
||||
BAZEL=$(shell which bazel)
|
||||
|
||||
ifneq ($(BAZEL_HTTP_CACHE_URL),)
|
||||
export BAZEL_CACHE_FLAGS=\
|
||||
--remote_cache="$(BAZEL_HTTP_CACHE_URL)" --experimental_remote_downloader="$(BAZEL_HTTP_CACHE_URL)"
|
||||
else ifneq ($(BAZEL_CACHE_DIR),)
|
||||
export BAZEL_CACHE_FLAGS=\
|
||||
--disk_cache="${BAZEL_CACHE_DIR}"
|
||||
endif
|
||||
|
||||
ifneq ($(BAZEL_KEEP_GOING),)
|
||||
export BAZEL_KEEP_GOING_FLAGS=\
|
||||
-k
|
||||
else ifneq ($(BAZEL_CACHE_DIR),)
|
||||
export BAZEL_KEEP_GOING_FLAGS=
|
||||
endif
|
||||
|
||||
BAZEL_COMMON_FLAGS=\
|
||||
--announce_rc \
|
||||
--features=swift.use_global_module_cache \
|
||||
--features=swift.split_derived_files_generation \
|
||||
--features=swift.skip_function_bodies_for_derived_files \
|
||||
--jobs=${CORE_COUNT} \
|
||||
${BAZEL_KEEP_GOING_FLAGS} \
|
||||
|
||||
BAZEL_DEBUG_FLAGS=\
|
||||
--features=swift.enable_batch_mode \
|
||||
--swiftcopt=-j${CORE_COUNT_MINUS_ONE} \
|
||||
--experimental_guard_against_concurrent_changes \
|
||||
|
||||
BAZEL_SANDBOX_FLAGS=\
|
||||
--strategy=Genrule=sandboxed \
|
||||
--spawn_strategy=sandboxed \
|
||||
--strategy=SwiftCompile=sandboxed \
|
||||
|
||||
# --num-threads 0 forces swiftc to generate one object file per module; it:
|
||||
# 1. resolves issues with the linker caused by swift-objc mixing.
|
||||
# 2. makes the resulting binaries significantly smaller (up to 9% for this project).
|
||||
BAZEL_OPT_FLAGS=\
|
||||
--features=swift.opt_uses_wmo \
|
||||
--features=swift.opt_uses_osize \
|
||||
--swiftcopt='-num-threads' --swiftcopt='0' \
|
||||
--features=dead_strip \
|
||||
--objc_enable_binary_stripping \
|
||||
--apple_bitcode=watchos=embedded \
|
||||
|
||||
kill_xcode:
|
||||
killall Xcode || true
|
||||
|
||||
clean:
|
||||
"${BAZEL}" clean --expunge
|
||||
|
||||
bazel_app_debug_arm64:
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
TELEGRAM_DISABLE_EXTENSIONS="0" \
|
||||
build-system/prepare-build.sh Telegram distribution
|
||||
"${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_DEBUG_FLAGS} \
|
||||
-c dbg \
|
||||
--ios_multi_cpus=arm64 \
|
||||
--watchos_cpus=armv7k,arm64_32 \
|
||||
--verbose_failures
|
||||
|
||||
bazel_app_debug_sim_arm64:
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
TELEGRAM_DISABLE_EXTENSIONS="0" \
|
||||
build-system/prepare-build.sh Telegram distribution
|
||||
"${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_DEBUG_FLAGS} \
|
||||
-c dbg \
|
||||
--ios_multi_cpus=sim_arm64 \
|
||||
--watchos_cpus=armv7k,arm64_32 \
|
||||
--verbose_failures
|
||||
|
||||
bazel_app_arm64:
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
TELEGRAM_DISABLE_EXTENSIONS="0" \
|
||||
build-system/prepare-build.sh Telegram distribution
|
||||
"${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_OPT_FLAGS} \
|
||||
-c opt \
|
||||
--ios_multi_cpus=arm64 \
|
||||
--watchos_cpus=armv7k,arm64_32 \
|
||||
--apple_generate_dsym \
|
||||
--output_groups=+dsyms \
|
||||
--verbose_failures
|
||||
|
||||
bazel_app_armv7:
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
TELEGRAM_DISABLE_EXTENSIONS="0" \
|
||||
build-system/prepare-build.sh Telegram distribution
|
||||
"${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_OPT_FLAGS} \
|
||||
-c opt \
|
||||
--ios_multi_cpus=armv7 \
|
||||
--watchos_cpus=armv7k,arm64_32 \
|
||||
--apple_generate_dsym \
|
||||
--output_groups=+dsyms \
|
||||
--verbose_failures
|
||||
|
||||
bazel_app:
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
TELEGRAM_DISABLE_EXTENSIONS="0" \
|
||||
build-system/prepare-build.sh Telegram distribution
|
||||
"${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_OPT_FLAGS} \
|
||||
-c opt \
|
||||
--ios_multi_cpus=armv7,arm64 \
|
||||
--watchos_cpus=armv7k,arm64_32 \
|
||||
--apple_generate_dsym \
|
||||
--output_groups=+dsyms \
|
||||
--verbose_failures
|
||||
|
||||
check_sandbox_debug_build:
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
TELEGRAM_DISABLE_EXTENSIONS="0" \
|
||||
build-system/prepare-build.sh Telegram distribution
|
||||
"${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_DEBUG_FLAGS} ${BAZEL_SANDBOX_FLAGS} \
|
||||
-c opt \
|
||||
--ios_multi_cpus=arm64 \
|
||||
--watchos_cpus=armv7k,arm64_32 \
|
||||
--apple_generate_dsym \
|
||||
--output_groups=+dsyms \
|
||||
--verbose_failures
|
||||
|
||||
bazel_project: kill_xcode
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
TELEGRAM_DISABLE_EXTENSIONS="0" \
|
||||
build-system/prepare-build.sh Telegram development
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
build-system/generate-xcode-project.sh Telegram
|
||||
|
||||
bazel_project_noextensions: kill_xcode
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
TELEGRAM_DISABLE_EXTENSIONS="1" \
|
||||
build-system/prepare-build.sh Telegram development
|
||||
APP_VERSION="${APP_VERSION}" \
|
||||
BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \
|
||||
BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \
|
||||
build-system/generate-xcode-project.sh Telegram
|
100
README.md
100
README.md
@ -1,16 +1,96 @@
|
||||
# Telegram iOS Source Code Compilation Guide
|
||||
|
||||
1. Install the brew package manager, if you haven’t already.
|
||||
2. Install the packages yasm, cmake:
|
||||
```
|
||||
brew install yasm cmake
|
||||
```
|
||||
3. Clone the project from GitHub:
|
||||
We welcome all developers to use our API and source code to create applications on our platform.
|
||||
There are several things we require from **all developers** for the moment.
|
||||
|
||||
# Creating your Telegram Application
|
||||
|
||||
1. [**Obtain your own api_id**](https://core.telegram.org/api/obtaining_api_id) for your application.
|
||||
2. Please **do not** use the name Telegram for your app — or make sure your users understand that it is unofficial.
|
||||
3. Kindly **do not** use our standard logo (white paper plane in a blue circle) as your app's logo.
|
||||
3. Please study our [**security guidelines**](https://core.telegram.org/mtproto/security_guidelines) and take good care of your users' data and privacy.
|
||||
4. Please remember to publish **your** code too in order to comply with the licences.
|
||||
|
||||
# Compilation Guide
|
||||
|
||||
1. Install Xcode (directly from https://developer.apple.com/download/more or using the App Store).
|
||||
2. Clone the project from GitHub:
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/TelegramMessenger/Telegram-iOS.git
|
||||
```
|
||||
4. Open Telegram-iOS.workspace.
|
||||
5. Open the Telegram-iOS-Fork scheme.
|
||||
6. Start the compilation process.
|
||||
7. To run the app on your device, you will need to set the correct values for the signature, .entitlements files and package IDs in accordance with your developer account values.
|
||||
|
||||
3. Download Bazel 3.7.0
|
||||
|
||||
```
|
||||
mkdir -p $HOME/bazel-dist
|
||||
cd $HOME/bazel-dist
|
||||
curl -O -L https://github.com/bazelbuild/bazel/releases/download/3.7.0/bazel-3.7.0-darwin-x86_64
|
||||
mv bazel-3.7.0* bazel
|
||||
```
|
||||
|
||||
Verify that it's working
|
||||
|
||||
```
|
||||
chmod +x bazel
|
||||
./bazel --version
|
||||
```
|
||||
|
||||
4. Adjust configuration parameters
|
||||
|
||||
```
|
||||
mkdir -p $HOME/telegram-configuration
|
||||
cp -R build-system/example-configuration/* $HOME/telegram-configuration/
|
||||
```
|
||||
|
||||
- Modify the values in `variables.bzl`
|
||||
- Replace the provisioning profiles in `provisioning` with valid files
|
||||
|
||||
5. (Optional) Create a build cache directory to speed up rebuilds
|
||||
|
||||
```
|
||||
mkdir -p "$HOME/telegram-bazel-cache"
|
||||
```
|
||||
|
||||
5. Build the app
|
||||
|
||||
```
|
||||
python3 build-system/Make/Make.py \
|
||||
--bazel="$HOME/bazel-dist/bazel" \
|
||||
--cacheDir="$HOME/telegram-bazel-cache" \
|
||||
build \
|
||||
--configurationPath="$HOME/telegram-configuration" \
|
||||
--buildNumber=100001 \
|
||||
--configuration=release_universal
|
||||
```
|
||||
|
||||
6. (Optional) Generate an Xcode project
|
||||
|
||||
```
|
||||
python3 build-system/Make/Make.py \
|
||||
--bazel="$HOME/bazel-dist/bazel" \
|
||||
--cacheDir="$HOME/telegram-bazel-cache" \
|
||||
generateProject \
|
||||
--configurationPath="$HOME/telegram-configuration" \
|
||||
--disableExtensions
|
||||
```
|
||||
|
||||
Tip: use `--disableExtensions` when developing to speed up development by not building application extensions.
|
||||
|
||||
|
||||
# Tips
|
||||
|
||||
Bazel is used to build the app. To simplify the development setup a helper script is provided (`build-system/Make/Make.py`). See help:
|
||||
|
||||
```
|
||||
python3 build-system/Make/Make.py --help
|
||||
python3 build-system/Make/Make.py build --help
|
||||
python3 build-system/Make/Make.py generateProject --help
|
||||
```
|
||||
|
||||
Each release is built using specific Xcode and Bazel versions (see `versions.json`). The helper script checks the versions of installed software and reports an error if they don't match the ones specified in `versions.json`. There are flags that allow to bypass these checks:
|
||||
|
||||
```
|
||||
python3 build-system/Make/Make.py --overrideBazelVersion build ... # Don't check the version of Bazel
|
||||
python3 build-system/Make/Make.py --overrideXcodeVersion build ... # Don't check the version of Xcode
|
||||
```
|
||||
|
@ -1126,25 +1126,35 @@ plist_fragment(
|
||||
)
|
||||
)
|
||||
|
||||
swift_library(
|
||||
name = "GeneratedSources",
|
||||
module_name = "GeneratedSources",
|
||||
srcs = glob([
|
||||
"Generated/**/*.swift",
|
||||
]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
swift_library(
|
||||
name = "WidgetExtensionLib",
|
||||
module_name = "WidgetExtensionLib",
|
||||
srcs = glob([
|
||||
"WidgetKitWidget/**/*.swift",
|
||||
"Generated/**/*.swift",
|
||||
]),
|
||||
data = [
|
||||
"SiriIntents/Intents.intentdefinition",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/BuildConfig:BuildConfig",
|
||||
"//submodules/WidgetItems:WidgetItems",
|
||||
"//submodules/WidgetItems:WidgetItems_iOS14",
|
||||
"//submodules/WidgetItemsUtils:WidgetItemsUtils",
|
||||
"//submodules/AppLockState:AppLockState",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/SyncCore:SyncCore",
|
||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
||||
":GeneratedSources",
|
||||
],
|
||||
)
|
||||
|
||||
@ -1221,7 +1231,6 @@ swift_library(
|
||||
module_name = "IntentsExtensionLib",
|
||||
srcs = glob([
|
||||
"SiriIntents/**/*.swift",
|
||||
"Generated/**/*.swift",
|
||||
]),
|
||||
data = [
|
||||
"SiriIntents/Intents.intentdefinition",
|
||||
@ -1235,6 +1244,7 @@ swift_library(
|
||||
"//submodules/BuildConfig:BuildConfig",
|
||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
||||
"//submodules/AppLockState:AppLockState",
|
||||
":GeneratedSources",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -9,6 +9,7 @@ import Contacts
|
||||
import OpenSSLEncryptionProvider
|
||||
import AppLockState
|
||||
import UIKit
|
||||
import GeneratedSources
|
||||
|
||||
private var accountCache: Account?
|
||||
|
||||
|
@ -13,6 +13,9 @@ import Postbox
|
||||
import SyncCore
|
||||
import TelegramCore
|
||||
import OpenSSLEncryptionProvider
|
||||
import WidgetItemsUtils
|
||||
|
||||
import GeneratedSources
|
||||
|
||||
private var installedSharedLogger = false
|
||||
|
||||
@ -156,9 +159,16 @@ struct Provider: IntentTimelineProvider {
|
||||
)
|
||||
}
|
||||
|
||||
var mappedMessage: WidgetDataPeer.Message?
|
||||
if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) {
|
||||
if let message = transaction.getMessage(index.id) {
|
||||
mappedMessage = WidgetDataPeer.Message(message: message)
|
||||
}
|
||||
}
|
||||
|
||||
peers.append(WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return postbox.mediaBox.resourcePath(representation.resource)
|
||||
}, badge: badge))
|
||||
}, badge: badge, message: mappedMessage))
|
||||
}
|
||||
}
|
||||
return WidgetDataPeers(accountPeerId: widgetPeers.accountPeerId, peers: peers)
|
||||
@ -195,12 +205,13 @@ struct AvatarItemView: View {
|
||||
var accountPeerId: Int64
|
||||
var peer: WidgetDataPeer
|
||||
var itemSize: CGFloat
|
||||
var displayBadge: Bool = true
|
||||
|
||||
var body: some View {
|
||||
return ZStack {
|
||||
Image(uiImage: avatarImage(accountPeerId: accountPeerId, peer: peer, size: CGSize(width: itemSize, height: itemSize)))
|
||||
.clipShape(Circle())
|
||||
if let badge = peer.badge, badge.count > 0 {
|
||||
if displayBadge, let badge = peer.badge, badge.count > 0 {
|
||||
Text("\(badge.count)")
|
||||
.font(Font.system(size: 16.0))
|
||||
.multilineTextAlignment(.center)
|
||||
@ -219,6 +230,7 @@ struct AvatarItemView: View {
|
||||
|
||||
struct WidgetView: View {
|
||||
@Environment(\.widgetFamily) private var widgetFamily
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let data: PeersWidgetData
|
||||
|
||||
func placeholder(geometry: GeometryProxy) -> some View {
|
||||
@ -329,12 +341,150 @@ struct WidgetView: View {
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
var body1: some View {
|
||||
ZStack {
|
||||
peerViews()
|
||||
}
|
||||
.padding(0.0)
|
||||
}
|
||||
|
||||
func chatTopLine(_ peer: WidgetDataPeer) -> some View {
|
||||
let dateText: String
|
||||
if let message = peer.message {
|
||||
dateText = DateFormatter.localizedString(from: Date(timeIntervalSince1970: Double(message.timestamp)), dateStyle: .none, timeStyle: .short)
|
||||
} else {
|
||||
dateText = ""
|
||||
}
|
||||
return HStack(alignment: .center, spacing: 0.0, content: {
|
||||
Text(peer.name).font(Font.system(size: 16.0, weight: .medium, design: .default)).foregroundColor(.primary)
|
||||
Spacer()
|
||||
Text(dateText).font(Font.system(size: 14.0, weight: .regular, design: .default)).foregroundColor(.secondary)
|
||||
})
|
||||
}
|
||||
|
||||
func chatBottomLine(_ peer: WidgetDataPeer) -> some View {
|
||||
var text = peer.message?.text ?? ""
|
||||
if let message = peer.message {
|
||||
//TODO:localize
|
||||
switch message.content {
|
||||
case .text:
|
||||
break
|
||||
case .image:
|
||||
text = "🖼 Photo"
|
||||
case .video:
|
||||
text = "📹 Video"
|
||||
case .gif:
|
||||
text = "Gif"
|
||||
case let .file(file):
|
||||
text = "📎 \(file.name)"
|
||||
case let .music(music):
|
||||
if !music.title.isEmpty && !music.artist.isEmpty {
|
||||
text = "\(music.artist) — \(music.title)"
|
||||
} else if !music.title.isEmpty {
|
||||
text = music.title
|
||||
} else if !music.artist.isEmpty {
|
||||
text = music.artist
|
||||
} else {
|
||||
text = "Music"
|
||||
}
|
||||
case .voiceMessage:
|
||||
text = "🎤 Voice Message"
|
||||
case .videoMessage:
|
||||
text = "Video Message"
|
||||
case let .sticker(sticker):
|
||||
text = "\(sticker.altText) Sticker"
|
||||
case let .call(call):
|
||||
if call.isVideo {
|
||||
text = "Video Call"
|
||||
} else {
|
||||
text = "Voice Call"
|
||||
}
|
||||
case .mapLocation:
|
||||
text = "Location"
|
||||
case let .game(game):
|
||||
text = "🎮 \(game.title)"
|
||||
case let .poll(poll):
|
||||
text = "📊 \(poll.title)"
|
||||
}
|
||||
}
|
||||
|
||||
var hasBadge = false
|
||||
if let badge = peer.badge, badge.count > 0 {
|
||||
hasBadge = true
|
||||
}
|
||||
|
||||
return HStack(alignment: .center, spacing: hasBadge ? 6.0 : 0.0, content: {
|
||||
Text(text).lineLimit(nil).font(Font.system(size: 15.0, weight: .regular, design: .default)).foregroundColor(.secondary).multilineTextAlignment(.leading).frame(maxHeight: .infinity, alignment: .topLeading)
|
||||
Spacer()
|
||||
if let badge = peer.badge, badge.count > 0 {
|
||||
VStack {
|
||||
Spacer()
|
||||
Text("\(badge.count)")
|
||||
.font(Font.system(size: 14.0))
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 4.0)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(badge.isMuted ? Color.gray : Color.blue)
|
||||
.frame(minWidth: 20, idealWidth: 20, maxWidth: .infinity, minHeight: 20, idealHeight: 20, maxHeight: 20.0, alignment: .center)
|
||||
)
|
||||
.padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 6.0, trailing: 3.0))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func chatContent(_ peer: WidgetDataPeer) -> some View {
|
||||
return VStack(alignment: .leading, spacing: 2.0, content: {
|
||||
chatTopLine(peer)
|
||||
chatBottomLine(peer).frame(maxHeight: .infinity)
|
||||
})
|
||||
}
|
||||
|
||||
func chatContentView(_ index: Int) -> AnyView {
|
||||
let peers: WidgetDataPeers
|
||||
switch data {
|
||||
case let .peers(peersValue):
|
||||
peers = peersValue
|
||||
if peers.peers.count <= index {
|
||||
return AnyView(Spacer())
|
||||
}
|
||||
default:
|
||||
return AnyView(Spacer())
|
||||
}
|
||||
|
||||
return AnyView(
|
||||
Link(destination: URL(string: linkForPeer(id: peers.peers[index].id))!, label: {
|
||||
HStack(alignment: .center, spacing: 0.0, content: {
|
||||
AvatarItemView(accountPeerId: peers.accountPeerId, peer: peers.peers[index], itemSize: 60.0, displayBadge: false).frame(width: 60.0, height: 60.0, alignment: .leading).padding(EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0))
|
||||
chatContent(peers.peers[index]).frame(maxWidth: .infinity).padding(EdgeInsets(top: 10.0, leading: 0.0, bottom: 10.0, trailing: 10.0))
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
func getSeparatorColor() -> Color {
|
||||
switch colorScheme {
|
||||
case .light:
|
||||
return Color(.sRGB, red: 200.0 / 255.0, green: 199.0 / 255.0, blue: 204.0 / 255.0, opacity: 1.0)
|
||||
case .dark:
|
||||
return Color(.sRGB, red: 61.0 / 255.0, green: 61.0 / 255.0, blue: 64.0 / 255.0, opacity: 1.0)
|
||||
@unknown default:
|
||||
return .secondary
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader(content: { geometry in
|
||||
ZStack {
|
||||
chatContentView(0).position(x: geometry.size.width / 2.0, y: geometry.size.height / 4.0).frame(width: geometry.size.width, height: geometry.size.height / 2.0, alignment: .leading)
|
||||
chatContentView(1).position(x: geometry.size.width / 2.0, y: geometry.size.height / 2.0 + geometry.size.height / 4.0).frame(width: geometry.size.width, height: geometry.size.height / 2.0, alignment: .leading)
|
||||
Rectangle().foregroundColor(getSeparatorColor()).position(x: geometry.size.width / 2.0, y: geometry.size.height / 4.0).frame(width: geometry.size.width, height: 0.33, alignment: .leading)
|
||||
}
|
||||
})
|
||||
.padding(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
private let buildConfig: BuildConfig = {
|
||||
@ -429,7 +579,7 @@ struct Static_Widget: Widget {
|
||||
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in
|
||||
WidgetView(data: getWidgetData(contents: entry.contents))
|
||||
})
|
||||
.supportedFamilies([.systemSmall, .systemMedium])
|
||||
.supportedFamilies([.systemMedium])
|
||||
.configurationDisplayName(presentationData.widgetGalleryTitle)
|
||||
.description(presentationData.widgetGalleryDescription)
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ def get_bazel_version(bazel_path):
|
||||
command_result = run_executable_with_output(bazel_path, ['--version']).strip('\n')
|
||||
if not command_result.startswith('bazel '):
|
||||
raise Exception('{} is not a valid bazel binary'.format(bazel_path))
|
||||
command_result.replace('bazel ', '')
|
||||
command_result = command_result.replace('bazel ', '')
|
||||
return command_result
|
||||
|
||||
|
||||
@ -132,7 +132,7 @@ class BuildEnvironment:
|
||||
self.bazel_version, actual_bazel_version, self.bazel_path))
|
||||
self.bazel_version = actual_bazel_version
|
||||
else:
|
||||
print('Required bazel version is {}, but {} is reported by {}'.format(
|
||||
print('Required bazel version is "{}", but "{}"" is reported by {}'.format(
|
||||
self.bazel_version, actual_bazel_version, self.bazel_path))
|
||||
exit(1)
|
||||
|
||||
|
@ -136,7 +136,7 @@ class BazelCommandLine:
|
||||
# Always build universal Watch binaries.
|
||||
'--watchos_cpus=armv7k,arm64_32'
|
||||
] + self.common_release_args
|
||||
elif configuration == 'release':
|
||||
elif configuration == 'release_universal':
|
||||
self.configuration_args = [
|
||||
# bazel optimized build configuration
|
||||
'-c', 'opt',
|
||||
@ -145,7 +145,7 @@ class BazelCommandLine:
|
||||
'--ios_multi_cpus=armv7,arm64',
|
||||
|
||||
# Always build universal Watch binaries.
|
||||
'--watchos_cpus=armv7k,arm64_32'
|
||||
'--watchos_cpus=armv7k,arm64_32',
|
||||
|
||||
# Generate DSYM files when building.
|
||||
'--apple_generate_dsym',
|
||||
@ -266,9 +266,13 @@ def resolve_configuration(bazel_command_line: BazelCommandLine, arguments):
|
||||
|
||||
|
||||
def generate_project(arguments):
|
||||
bazel_x86_64_path = None
|
||||
if is_apple_silicon():
|
||||
bazel_x86_64_path = arguments.bazel_x86_64
|
||||
|
||||
bazel_command_line = BazelCommandLine(
|
||||
bazel_path=arguments.bazel,
|
||||
bazel_x86_64_path=arguments.bazel_x86_64,
|
||||
bazel_x86_64_path=bazel_x86_64_path,
|
||||
override_bazel_version=arguments.overrideBazelVersion,
|
||||
override_xcode_version=arguments.overrideXcodeVersion
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
load(
|
||||
"//build-input/data:variables.bzl",
|
||||
"@build_configuration//:variables.bzl",
|
||||
"telegram_api_id",
|
||||
"telegram_api_hash",
|
||||
"telegram_app_center_id",
|
||||
|
44
submodules/Postbox/Sources/TopChatMessageView.swift
Normal file
44
submodules/Postbox/Sources/TopChatMessageView.swift
Normal file
@ -0,0 +1,44 @@
|
||||
import Foundation
|
||||
|
||||
final class MutableTopChatMessageView: MutablePostboxView {
|
||||
private let peerIds: Set<PeerId>
|
||||
fileprivate var messages: [PeerId: Message] = [:]
|
||||
|
||||
init(postbox: Postbox, peerIds: Set<PeerId>) {
|
||||
self.peerIds = peerIds
|
||||
|
||||
for peerId in self.peerIds {
|
||||
if let index = postbox.chatListIndexTable.get(peerId: peerId).topMessageIndex {
|
||||
self.messages[peerId] = postbox.getMessage(index.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool {
|
||||
var updated = false
|
||||
for peerId in self.peerIds {
|
||||
if transaction.currentOperationsByPeerId[peerId] != nil {
|
||||
if let index = postbox.chatListIndexTable.get(peerId: peerId).topMessageIndex {
|
||||
self.messages[peerId] = postbox.getMessage(index.id)
|
||||
} else {
|
||||
self.messages.removeValue(forKey: peerId)
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return TopChatMessageView(self)
|
||||
}
|
||||
}
|
||||
|
||||
public final class TopChatMessageView: PostboxView {
|
||||
public let messages: [PeerId: Message]
|
||||
|
||||
init(_ view: MutableTopChatMessageView) {
|
||||
self.messages = view.messages
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ public enum PostboxViewKey: Hashable {
|
||||
case basicPeer(PeerId)
|
||||
case allChatListHoles(PeerGroupId)
|
||||
case historyTagInfo(peerId: PeerId, tag: MessageTags)
|
||||
case topChatMessage(peerIds: [PeerId])
|
||||
|
||||
public var hashValue: Int {
|
||||
switch self {
|
||||
@ -91,6 +92,8 @@ public enum PostboxViewKey: Hashable {
|
||||
return groupId.hashValue
|
||||
case let .historyTagInfo(peerId, tag):
|
||||
return peerId.hashValue ^ tag.hashValue
|
||||
case let .topChatMessage(peerIds):
|
||||
return peerIds.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,6 +273,12 @@ public enum PostboxViewKey: Hashable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .topChatMessage(peerIds):
|
||||
if case .topChatMessage(peerIds) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -334,5 +343,7 @@ func postboxViewForKey(postbox: Postbox, key: PostboxViewKey) -> MutablePostboxV
|
||||
return MutableAllChatListHolesView(postbox: postbox, groupId: groupId)
|
||||
case let .historyTagInfo(peerId, tag):
|
||||
return MutableHistoryTagInfoView(postbox: postbox, peerId: peerId, tag: tag)
|
||||
case let .topChatMessage(peerIds):
|
||||
return MutableTopChatMessageView(postbox: postbox, peerIds: Set(peerIds))
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +187,7 @@ swift_library(
|
||||
"//submodules/Markdown:Markdown",
|
||||
"//submodules/SearchPeerMembers:SearchPeerMembers",
|
||||
"//submodules/WidgetItems:WidgetItems",
|
||||
"//submodules/WidgetItemsUtils:WidgetItemsUtils",
|
||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
||||
"//submodules/PhoneNumberFormat:PhoneNumberFormat",
|
||||
"//submodules/AppLock:AppLock",
|
||||
@ -211,6 +212,7 @@ swift_library(
|
||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||
"//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode",
|
||||
"//submodules/AudioBlob:AudioBlob",
|
||||
"//Telegram:GeneratedSources",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -8,6 +8,9 @@ import TelegramPresentationData
|
||||
import NotificationsPresentationData
|
||||
import WidgetKit
|
||||
import TelegramUIPreferences
|
||||
import WidgetItemsUtils
|
||||
|
||||
import GeneratedSources
|
||||
|
||||
final class WidgetDataContext {
|
||||
private var currentAccount: Account?
|
||||
@ -32,9 +35,45 @@ final class WidgetDataContext {
|
||||
}
|
||||
|
||||
case disabled
|
||||
case peers(peers: [Peer], unread: [PeerId: Unread])
|
||||
case peers(peers: [Peer], unread: [PeerId: Unread], messages: [PeerId: WidgetDataPeer.Message])
|
||||
}
|
||||
|
||||
let updatedAdditionalPeerIds: Signal<Set<PeerId>, NoError> = Signal { subscriber in
|
||||
if #available(iOSApplicationExtension 14.0, iOS 14.0, *) {
|
||||
#if arch(arm64) || arch(i386) || arch(x86_64)
|
||||
WidgetCenter.shared.getCurrentConfigurations({ result in
|
||||
var peerIds = Set<PeerId>()
|
||||
if case let .success(infos) = result {
|
||||
for info in infos {
|
||||
if let configuration = info.configuration as? SelectFriendsIntent {
|
||||
if let items = configuration.friends {
|
||||
for item in items {
|
||||
guard let identifier = item.identifier, let peerIdValue = Int64(identifier) else {
|
||||
continue
|
||||
}
|
||||
peerIds.insert(PeerId(peerIdValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscriber.putNext(peerIds)
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
#else
|
||||
subscriber.putNext(Set())
|
||||
subscriber.putCompletion()
|
||||
#endif
|
||||
} else {
|
||||
subscriber.putNext(Set())
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
|
||||
return EmptyDisposable
|
||||
}
|
||||
|> runOn(.mainQueue())
|
||||
|
||||
let preferencesKey: PostboxViewKey = .preferences(keys: Set([
|
||||
ApplicationSpecificPreferencesKeys.widgetSettings
|
||||
]))
|
||||
@ -69,13 +108,24 @@ final class WidgetDataContext {
|
||||
case .disabled:
|
||||
return .single(.disabled)
|
||||
case let .peers(peers):
|
||||
return combineLatest(queue: .mainQueue(), peers.filter { !$0.isDeleted }.map { account.postbox.peerView(id: $0.id)}) |> mapToSignal { peerViews -> Signal<CombinedRecentPeers, NoError> in
|
||||
return account.postbox.unreadMessageCountsView(items: peerViews.map {
|
||||
.peer($0.peerId)
|
||||
})
|
||||
|> map { values -> CombinedRecentPeers in
|
||||
return combineLatest(queue: .mainQueue(), peers.filter { !$0.isDeleted }.map { account.postbox.peerView(id: $0.id)})
|
||||
|> mapToSignal { peerViews -> Signal<CombinedRecentPeers, NoError> in
|
||||
let topMessagesKey: PostboxViewKey = .topChatMessage(peerIds: peerViews.map {
|
||||
$0.peerId
|
||||
})
|
||||
return combineLatest(queue: .mainQueue(),
|
||||
account.postbox.unreadMessageCountsView(items: peerViews.map {
|
||||
.peer($0.peerId)
|
||||
}),
|
||||
account.postbox.combinedView(keys: [topMessagesKey])
|
||||
)
|
||||
|> map { values, combinedView -> CombinedRecentPeers in
|
||||
var peers: [Peer] = []
|
||||
var unread: [PeerId: CombinedRecentPeers.Unread] = [:]
|
||||
var messages: [PeerId: WidgetDataPeer.Message] = [:]
|
||||
|
||||
let topMessages = combinedView.views[topMessagesKey] as! TopChatMessageView
|
||||
|
||||
for peerView in peerViews {
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
var isMuted: Bool = false
|
||||
@ -93,21 +143,25 @@ final class WidgetDataContext {
|
||||
unread[peerView.peerId] = CombinedRecentPeers.Unread(count: Int32(unreadCount), isMuted: isMuted)
|
||||
}
|
||||
|
||||
if let message = topMessages.messages[peerView.peerId] {
|
||||
messages[peerView.peerId] = WidgetDataPeer.Message(message: message)
|
||||
}
|
||||
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
return .peers(peers: peers, unread: unread)
|
||||
return .peers(peers: peers, unread: unread, messages: messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return recent
|
||||
let processedRecent = recent
|
||||
|> map { result -> WidgetData in
|
||||
switch result {
|
||||
case .disabled:
|
||||
return WidgetData(accountId: account.id.int64, content: .disabled)
|
||||
case let .peers(peers, unread):
|
||||
case let .peers(peers, unread, messages):
|
||||
return WidgetData(accountId: account.id.int64, content: .peers(WidgetDataPeers(accountPeerId: account.peerId.toInt64(), peers: peers.compactMap { peer -> WidgetDataPeer? in
|
||||
var name: String = ""
|
||||
var lastName: String?
|
||||
@ -133,13 +187,110 @@ final class WidgetDataContext {
|
||||
)
|
||||
}
|
||||
|
||||
let message = messages[peer.id]
|
||||
|
||||
return WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return account.postbox.mediaBox.resourcePath(representation.resource)
|
||||
}, badge: badge)
|
||||
}, badge: badge, message: message)
|
||||
})))
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
let additionalPeerIds = Signal<Set<PeerId>, NoError>.single(Set()) |> then(updatedAdditionalPeerIds)
|
||||
let processedCustom: Signal<WidgetData, NoError> = additionalPeerIds
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { additionalPeerIds -> Signal<CombinedRecentPeers, NoError> in
|
||||
return combineLatest(queue: .mainQueue(), additionalPeerIds.map { account.postbox.peerView(id: $0) })
|
||||
|> mapToSignal { peerViews -> Signal<CombinedRecentPeers, NoError> in
|
||||
let topMessagesKey: PostboxViewKey = .topChatMessage(peerIds: peerViews.map {
|
||||
$0.peerId
|
||||
})
|
||||
return combineLatest(queue: .mainQueue(),
|
||||
account.postbox.unreadMessageCountsView(items: peerViews.map {
|
||||
.peer($0.peerId)
|
||||
}),
|
||||
account.postbox.combinedView(keys: [topMessagesKey])
|
||||
)
|
||||
|> map { values, combinedView -> CombinedRecentPeers in
|
||||
var peers: [Peer] = []
|
||||
var unread: [PeerId: CombinedRecentPeers.Unread] = [:]
|
||||
var messages: [PeerId: WidgetDataPeer.Message] = [:]
|
||||
|
||||
let topMessages = combinedView.views[topMessagesKey] as! TopChatMessageView
|
||||
|
||||
for peerView in peerViews {
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
var isMuted: Bool = false
|
||||
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
||||
switch notificationSettings.muteState {
|
||||
case .muted:
|
||||
isMuted = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let unreadCount = values.count(for: .peer(peerView.peerId))
|
||||
if let unreadCount = unreadCount, unreadCount > 0 {
|
||||
unread[peerView.peerId] = CombinedRecentPeers.Unread(count: Int32(unreadCount), isMuted: isMuted)
|
||||
}
|
||||
|
||||
if let message = topMessages.messages[peerView.peerId] {
|
||||
messages[peerView.peerId] = WidgetDataPeer.Message(message: message)
|
||||
}
|
||||
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
return .peers(peers: peers, unread: unread, messages: messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> map { result -> WidgetData in
|
||||
switch result {
|
||||
case .disabled:
|
||||
return WidgetData(accountId: account.id.int64, content: .disabled)
|
||||
case let .peers(peers, unread, messages):
|
||||
return WidgetData(accountId: account.id.int64, content: .peers(WidgetDataPeers(accountPeerId: account.peerId.toInt64(), peers: peers.compactMap { peer -> WidgetDataPeer? in
|
||||
var name: String = ""
|
||||
var lastName: String?
|
||||
|
||||
if let user = peer as? TelegramUser {
|
||||
if let firstName = user.firstName {
|
||||
name = firstName
|
||||
lastName = user.lastName
|
||||
} else if let lastName = user.lastName {
|
||||
name = lastName
|
||||
} else if let phone = user.phone, !phone.isEmpty {
|
||||
name = phone
|
||||
}
|
||||
} else {
|
||||
name = peer.debugDisplayTitle
|
||||
}
|
||||
|
||||
var badge: WidgetDataPeer.Badge?
|
||||
if let unreadValue = unread[peer.id], unreadValue.count > 0 {
|
||||
badge = WidgetDataPeer.Badge(
|
||||
count: Int(unreadValue.count),
|
||||
isMuted: unreadValue.isMuted
|
||||
)
|
||||
}
|
||||
|
||||
let message = messages[peer.id]
|
||||
|
||||
return WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return account.postbox.mediaBox.resourcePath(representation.resource)
|
||||
}, badge: badge, message: message)
|
||||
})))
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
return combineLatest(processedRecent, processedCustom)
|
||||
|> map { processedRecent, _ -> WidgetData in
|
||||
return processedRecent
|
||||
}
|
||||
}).start(next: { widgetData in
|
||||
let path = basePath + "/widget-data"
|
||||
if let data = try? JSONEncoder().encode(widgetData) {
|
||||
|
@ -10,3 +10,14 @@ swift_library(
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
||||
swift_library(
|
||||
name = "WidgetItems_iOS14",
|
||||
module_name = "WidgetItems_iOS14",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
@ -15,20 +15,223 @@ public struct WidgetDataPeer: Codable, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public struct Message: Codable, Equatable {
|
||||
public enum Content: Codable, Equatable {
|
||||
public enum DecodingError: Error {
|
||||
case generic
|
||||
}
|
||||
|
||||
public struct Image: Codable, Equatable {
|
||||
public init() {
|
||||
}
|
||||
}
|
||||
|
||||
public struct Video: Codable, Equatable {
|
||||
public init() {
|
||||
}
|
||||
}
|
||||
|
||||
public struct File: Codable, Equatable {
|
||||
public var name: String
|
||||
|
||||
public init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
public struct Gif: Codable, Equatable {
|
||||
public init() {
|
||||
}
|
||||
}
|
||||
|
||||
public struct Music: Codable, Equatable {
|
||||
public var artist: String
|
||||
public var title: String
|
||||
public var duration: Int32
|
||||
|
||||
public init(artist: String, title: String, duration: Int32) {
|
||||
self.artist = artist
|
||||
self.title = title
|
||||
self.duration = duration
|
||||
}
|
||||
}
|
||||
|
||||
public struct VoiceMessage: Codable, Equatable {
|
||||
public var duration: Int32
|
||||
|
||||
public init(duration: Int32) {
|
||||
self.duration = duration
|
||||
}
|
||||
}
|
||||
|
||||
public struct VideoMessage: Codable, Equatable {
|
||||
public var duration: Int32
|
||||
|
||||
public init(duration: Int32) {
|
||||
self.duration = duration
|
||||
}
|
||||
}
|
||||
|
||||
public struct Sticker: Codable, Equatable {
|
||||
public var altText: String
|
||||
|
||||
public init(altText: String) {
|
||||
self.altText = altText
|
||||
}
|
||||
}
|
||||
|
||||
public struct Call: Codable, Equatable {
|
||||
public var isVideo: Bool
|
||||
|
||||
public init(isVideo: Bool) {
|
||||
self.isVideo = isVideo
|
||||
}
|
||||
}
|
||||
|
||||
public struct MapLocation: Codable, Equatable {
|
||||
public init() {
|
||||
}
|
||||
}
|
||||
|
||||
public struct Game: Codable, Equatable {
|
||||
public var title: String
|
||||
|
||||
public init(title: String) {
|
||||
self.title = title
|
||||
}
|
||||
}
|
||||
|
||||
public struct Poll: Codable, Equatable {
|
||||
public var title: String
|
||||
|
||||
public init(title: String) {
|
||||
self.title = title
|
||||
}
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case text
|
||||
case image
|
||||
case video
|
||||
case file
|
||||
case gif
|
||||
case music
|
||||
case voiceMessage
|
||||
case videoMessage
|
||||
case sticker
|
||||
case call
|
||||
case mapLocation
|
||||
case game
|
||||
case poll
|
||||
}
|
||||
|
||||
case text
|
||||
case image(Image)
|
||||
case video(Video)
|
||||
case file(File)
|
||||
case gif(Gif)
|
||||
case music(Music)
|
||||
case voiceMessage(VoiceMessage)
|
||||
case videoMessage(VideoMessage)
|
||||
case sticker(Sticker)
|
||||
case call(Call)
|
||||
case mapLocation(MapLocation)
|
||||
case game(Game)
|
||||
case poll(Poll)
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let _ = try? container.decode(String.self, forKey: .text) {
|
||||
self = .text
|
||||
} else if let image = try? container.decode(Image.self, forKey: .image) {
|
||||
self = .image(image)
|
||||
} else if let video = try? container.decode(Video.self, forKey: .video) {
|
||||
self = .video(video)
|
||||
} else if let gif = try? container.decode(Gif.self, forKey: .gif) {
|
||||
self = .gif(gif)
|
||||
} else if let file = try? container.decode(File.self, forKey: .file) {
|
||||
self = .file(file)
|
||||
} else if let music = try? container.decode(Music.self, forKey: .voiceMessage) {
|
||||
self = .music(music)
|
||||
} else if let voiceMessage = try? container.decode(VoiceMessage.self, forKey: .voiceMessage) {
|
||||
self = .voiceMessage(voiceMessage)
|
||||
} else if let videoMessage = try? container.decode(VideoMessage.self, forKey: .videoMessage) {
|
||||
self = .videoMessage(videoMessage)
|
||||
} else if let sticker = try? container.decode(Sticker.self, forKey: .sticker) {
|
||||
self = .sticker(sticker)
|
||||
} else if let call = try? container.decode(Call.self, forKey: .call) {
|
||||
self = .call(call)
|
||||
} else if let mapLocation = try? container.decode(MapLocation.self, forKey: .mapLocation) {
|
||||
self = .mapLocation(mapLocation)
|
||||
} else if let game = try? container.decode(Game.self, forKey: .game) {
|
||||
self = .game(game)
|
||||
} else if let poll = try? container.decode(Poll.self, forKey: .poll) {
|
||||
self = .poll(poll)
|
||||
} else {
|
||||
throw DecodingError.generic
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
switch self {
|
||||
case .text:
|
||||
try container.encode("", forKey: .text)
|
||||
case let .image(image):
|
||||
try container.encode(image, forKey: .image)
|
||||
case let .video(video):
|
||||
try container.encode(video, forKey: .video)
|
||||
case let .file(file):
|
||||
try container.encode(file, forKey: .file)
|
||||
case let .gif(gif):
|
||||
try container.encode(gif, forKey: .gif)
|
||||
case let .music(music):
|
||||
try container.encode(music, forKey: .music)
|
||||
case let .voiceMessage(voiceMessage):
|
||||
try container.encode(voiceMessage, forKey: .voiceMessage)
|
||||
case let .videoMessage(videoMessage):
|
||||
try container.encode(videoMessage, forKey: .videoMessage)
|
||||
case let .sticker(sticker):
|
||||
try container.encode(sticker, forKey: .sticker)
|
||||
case let .call(call):
|
||||
try container.encode(call, forKey: .call)
|
||||
case let .mapLocation(mapLocation):
|
||||
try container.encode(mapLocation, forKey: .mapLocation)
|
||||
case let .game(game):
|
||||
try container.encode(game, forKey: .game)
|
||||
case let .poll(poll):
|
||||
try container.encode(poll, forKey: .poll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var text: String
|
||||
public var content: Content
|
||||
public var timestamp: Int32
|
||||
|
||||
public init(text: String, content: Content, timestamp: Int32) {
|
||||
self.text = text
|
||||
self.content = content
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
}
|
||||
|
||||
public var id: Int64
|
||||
public var name: String
|
||||
public var lastName: String?
|
||||
public var letters: [String]
|
||||
public var avatarPath: String?
|
||||
public var badge: Badge?
|
||||
public var message: Message?
|
||||
|
||||
public init(id: Int64, name: String, lastName: String?, letters: [String], avatarPath: String?, badge: Badge?) {
|
||||
public init(id: Int64, name: String, lastName: String?, letters: [String], avatarPath: String?, badge: Badge?, message: Message?) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.lastName = lastName
|
||||
self.letters = letters
|
||||
self.avatarPath = avatarPath
|
||||
self.badge = badge
|
||||
self.message = message
|
||||
}
|
||||
}
|
||||
|
||||
|
19
submodules/WidgetItemsUtils/BUILD
Normal file
19
submodules/WidgetItemsUtils/BUILD
Normal file
@ -0,0 +1,19 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "WidgetItemsUtils",
|
||||
module_name = "WidgetItemsUtils",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/SyncCore:SyncCore",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/WidgetItems:WidgetItems",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
59
submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift
Normal file
59
submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift
Normal file
@ -0,0 +1,59 @@
|
||||
import Foundation
|
||||
|
||||
import Postbox
|
||||
import SyncCore
|
||||
import TelegramCore
|
||||
import WidgetItems
|
||||
|
||||
public extension WidgetDataPeer.Message {
|
||||
init(message: Message) {
|
||||
var content: WidgetDataPeer.Message.Content = .text
|
||||
for media in message.media {
|
||||
switch media {
|
||||
case _ as TelegramMediaImage:
|
||||
content = .image(WidgetDataPeer.Message.Content.Image())
|
||||
case let file as TelegramMediaFile:
|
||||
var fileName = "file"
|
||||
for attribute in file.attributes {
|
||||
if case let .FileName(value) = attribute {
|
||||
fileName = value
|
||||
break
|
||||
}
|
||||
}
|
||||
content = .file(WidgetDataPeer.Message.Content.File(name: fileName))
|
||||
for attribute in file.attributes {
|
||||
switch attribute {
|
||||
case let .Sticker(altText, _, _):
|
||||
content = .sticker(WidgetDataPeer.Message.Content.Sticker(altText: altText))
|
||||
case let .Video(duration, _, flags):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
content = .videoMessage(WidgetDataPeer.Message.Content.VideoMessage(duration: Int32(duration)))
|
||||
} else {
|
||||
content = .video(WidgetDataPeer.Message.Content.Video())
|
||||
}
|
||||
case let .Audio(isVoice, duration, title, performer, _):
|
||||
if isVoice {
|
||||
content = .voiceMessage(WidgetDataPeer.Message.Content.VoiceMessage(duration: Int32(duration)))
|
||||
} else {
|
||||
content = .music(WidgetDataPeer.Message.Content.Music(artist: performer ?? "", title: title ?? "", duration: Int32(duration)))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
case let action as TelegramMediaAction:
|
||||
switch action.action {
|
||||
case let .phoneCall(_, _, _, isVideo):
|
||||
content = .call(WidgetDataPeer.Message.Content.Call(isVideo: isVideo))
|
||||
default:
|
||||
break
|
||||
}
|
||||
case _ as TelegramMediaMap:
|
||||
content = .mapLocation(WidgetDataPeer.Message.Content.MapLocation())
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
self.init(text: message.text, content: content, timestamp: message.timestamp)
|
||||
}
|
||||
}
|
9
third-party/libvpx/BUILD
vendored
9
third-party/libvpx/BUILD
vendored
@ -55,6 +55,12 @@ genrule(
|
||||
rm -rf "$$BUILD_DIR"
|
||||
mkdir -p "$$BUILD_DIR"
|
||||
|
||||
YASM_DIR="$$BUILD_DIR/yasm"
|
||||
rm -rf "$$YASM_DIR"
|
||||
mkdir -p "$$YASM_DIR"
|
||||
tar -xf "$(location //third-party/yasm:yasm.tar)" -C "$$YASM_DIR"
|
||||
ABS_YASM_DIR="$$(pwd)/$$(dirname $$YASM_DIR)/$$(basename $$YASM_DIR)"
|
||||
|
||||
cp $(location :build-libvpx-bazel.sh) "$$BUILD_DIR/"
|
||||
cp $(location :0001-Add-support-for-arm64-iphonesimulator-gcc.patch) "$$BUILD_DIR/"
|
||||
|
||||
@ -81,6 +87,9 @@ genrule(
|
||||
outs = ["Public/vpx/" + x for x in headers] +
|
||||
["Public/vpx/vpx_config.h"] +
|
||||
["Public/vpx/lib{}.a".format(x) for x in libs],
|
||||
tools = [
|
||||
"//third-party/yasm:yasm.tar",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
]
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"app": "7.3",
|
||||
"app": "7.3.1",
|
||||
"bazel": "3.7.0",
|
||||
"xcode": "12.3"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user